# Cloudflare for Platforms
URL: https://developers.cloudflare.com/cloudflare-for-platforms/
import { Description, Feature } from "~/components"
Cloudflare's offering for SaaS businesses.
Extend Cloudflare's security, reliability, and performance services to your customers with Cloudflare for Platforms. Together with Cloudflare for SaaS and Workers for Platforms, your customers can build custom logic to meet their needs right into your application.
***
## Products
Cloudflare for SaaS allows you to extend the security and performance benefits of Cloudflare’s network to your customers via their own custom or vanity domains.
Workers for Platforms help you deploy serverless functions programmatically on behalf of your customers.
---
# Analytics
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/hostname-analytics/
import { Render } from "~/components"
You can use custom hostname analytics for two general purposes: exploring how your customers use your product and sharing the benefits provided by Cloudflare with your customers.
These analytics include **Site Analytics**, **Bot Analytics**, **Cache Analytics**, **Security Events**, and [any other datasets](/analytics/graphql-api/features/data-sets/) with the `clientRequestHTTPHost` field.
:::note
The plan of your Cloudflare for SaaS application determines the analytics available for your custom hostnames.
:::
## Explore customer usage
Use custom hostname analytics to help your organization with billing and infrastructure decisions, answering questions like:
* "How many total requests is your service getting?"
* "Is one customer transferring significantly more data than the others?"
* "How many global customers do you have and where are they distributed?"
If you see one customer is using more data than another, you might increase their bill. If requests are increasing in a certain geographic region, you might want to increase the origin servers in that region.
To access custom hostname analytics, either [use the dashboard](/analytics/faq/about-analytics/) and filter by the `Host` field or [use the GraphQL API](/analytics/graphql-api/) and filter by the `clientRequestHTTPHost` field. For more details, refer to our tutorial on [Querying HTTP events by hostname with GraphQL](/analytics/graphql-api/tutorials/end-customer-analytics/).
## Share Cloudflare data with your customers
With custom hostname analytics, you can also share site information with your customers, including data about:
* How many pageviews their site is receiving.
* Whether their site has a large percentage of bot traffic.
* How fast their site is.
Build custom dashboards to share this information by specifying an individual custom hostname in `clientRequestHTTPHost` field of [any dataset](/analytics/graphql-api/features/data-sets/) that includes this field.
## Logpush
[Logpush](/logs/about/) sends metadata from Cloudflare products to your cloud storage destination or SIEM.
Using [filters](/logs/reference/filters/), you can send set sample rates (or not include logs altogether) based on filter criteria. This flexibility allows you to maintain selective logs for custom hostnames without massively increasing your log volume.
Filtering is available for [all Cloudflare datasets](/logs/reference/log-fields/zone/).
---
# Cloudflare for SaaS
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/
import { LinkButton, Render } from "~/components";
As a SaaS provider, you may want to support subdomains under your own zone in addition to letting your customers use their own domain names with your services. For example, a customer may want to use their vanity domain `app.customer.com` to point to an application hosted on your Cloudflare zone `service.saas.com`. Cloudflare for SaaS allows you to increase security, performance, and reliability of your customers' domains.
## Benefits
When you use Cloudflare for SaaS, it helps you to:
- Provide custom domain support.
- Keep your customers' traffic encrypted.
- Keep your customers online.
- Facilitate fast load times of your customers' domains.
- Gain insight through traffic analytics.
## Limitations
If your customers already have their applications on Cloudflare, they cannot control some Cloudflare features for hostnames managed by your Custom Hostnames configuration, including:
- Argo
- Early Hints
- Page Shield
- Spectrum
- Wildcard DNS
## How it works
As the SaaS provider, you can extend Cloudflare's products to customer-owned custom domains by adding them to your zone [as custom hostnames](/cloudflare-for-platforms/cloudflare-for-saas/domain-support/). Through a suite of easy-to-use products, Cloudflare for SaaS routes traffic from custom hostnames to an origin, set up on your domain. Cloudflare for SaaS is highly customizable. Three possible configurations are shown below.
### Standard Cloudflare for SaaS configuration:
Custom hostnames are routed to a default origin server called fallback origin. This configuration is available on all plans.

### Cloudflare for SaaS with Apex Proxying:
This allows you to support apex domains even if your customers are using a DNS provider that does not allow a CNAME at the apex. This is available as an add-on for Enterprise plans. For more details, refer to [Apex Proxying](/cloudflare-for-platforms/cloudflare-for-saas/start/advanced-settings/apex-proxying/).

### Cloudflare for SaaS with BYOIP:
This allows you to support apex domains even if your customers are using a DNS provider that does not allow a CNAME at the apex. Also, you can point to your own IPs if you want to bring an IP range to Cloudflare (instead of Cloudflare provided IPs). This is available as an add-on for Enterprise plans.

## Availability
Cloudflare for SaaS is bundled with non-Enterprise plans and available as an add-on for Enterprise plans. For more details, refer to [Plans](/cloudflare-for-platforms/cloudflare-for-saas/plans/).
## Next steps
Get started
Learn more
---
# Plans
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/plans/
import { FeatureTable, Render } from "~/components"
## Enterprise plan benefits
The Enterprise plan offers features that give SaaS providers flexibility when it comes to meeting their end customer's requirements. In addition to that, Enterprise customers are able to extend all of the benefits of the Enterprise plan to their customer's custom hostnames. This includes advanced Bot Mitigation, WAF rules, analytics, DDoS mitigation, and more.
In addition, large SaaS providers rely on Enterprise level support, multi-user accounts, SSO, and other benefits that are not provided in non-Enterprise plans.
---
# Demos
URL: https://developers.cloudflare.com/cloudflare-for-platforms/workers-for-platforms/demos/
import { ExternalResources, GlossaryTooltip } from "~/components"
Learn how you can use Workers for Platforms within your existing architecture.
## Demos
Explore the following demo applications for Workers for Platforms.
---
# Workers for Platforms
URL: https://developers.cloudflare.com/cloudflare-for-platforms/workers-for-platforms/
import { CardGrid, Description, Feature, LinkTitleCard, Plan, RelatedProduct, Stream } from "~/components"
Deploy custom code on behalf of your users or let your users directly deploy their own code to your platform, managing infrastructure.
Workers for Platforms allows you to run your own code as a wrapper around your user's code. With Workers for Platforms, you can logically group your code separately from your users' code, create custom logic, and use additional APIs such as [script tags](/cloudflare-for-platforms/workers-for-platforms/configuration/tags/) for bulk operations.
Workers for Platforms is built on top of [Cloudflare Workers](/workers/). Workers for Platforms lets you surpass Cloudflare Workers' 500 scripts per account [limit](/cloudflare-for-platforms/workers-for-platforms/platform/limits/).
***
## Features
Learn how to set up Workers for Platforms.
Learn about Workers for Platforms architecture.
***
## Related products
Cloudflare Workers provides a serverless execution environment that allows you to create new applications or augment existing ones without configuring or maintaining infrastructure.
***
## More resources
Learn about limits that apply to your Workers for Platforms project.
Connect with the Workers community on Discord to ask questions, show what you are building, and discuss the platform with other developers.
Follow @CloudflareDev on Twitter to learn about product announcements, and what is new in Cloudflare Workers.
---
# Create custom hostnames
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/domain-support/create-custom-hostnames/
import { Render, TabItem, Tabs } from "~/components";
There are several required steps before a custom hostname can become active. For more details, refer to our [Get started guide](/cloudflare-for-platforms/cloudflare-for-saas/start/getting-started/).
To create a custom hostname:
## Hostnames over 64 characters
The Common Name (CN) restriction establishes a limit of 64 characters ([RFC 5280](https://www.rfc-editor.org/rfc/rfc5280.html)). If you have a hostname that exceeds this length, you can set `cloudflare_branding` to `true` when creating your custom hostnames [via API](/api/resources/custom_hostnames/methods/create/).
```txt
"ssl": {
"cloudflare_branding": true
}
```
Cloudflare branding means that `sni.cloudflaressl.com` will be added as the certificate Common Name (CN) and the long hostname will be included as a part of the Subject Alternative Name (SAN).
---
# Custom metadata
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/domain-support/custom-metadata/
import { Render } from "~/components";
You may wish to configure per-hostname (customer) settings beyond the scale of Page Rules or Rate Limiting, which have a maximum of 125 rules each.
To do this, you will first need to reach out to your account team to enable access to Custom Metadata. After configuring custom metadata, you can use it in the following ways:
- Read the metadata JSON from [Cloudflare Workers](/workers/) (requires access to Workers) to define per-hostname behavior.
- Use custom metadata values in [rule expressions](/ruleset-engine/rules-language/expressions/) of different Cloudflare security products to define the rule scope.
---
## Examples
- Per-customer URL rewriting — for example, customers 1-10,000 fetch assets from server A, 10,001-20,000 from server B, etc.
- Adding custom headers — for example, `X-Customer-ID: $number` based on the metadata you provided
- Setting HTTP Strict Transport Security (“HSTS”) headers on a per-customer basis
Please speak with your Solutions Engineer to discuss additional logic and requirements.
## Submitting custom metadata
You may add custom metadata to Cloudflare via the Custom Hostnames API. This data can be added via a [`PATCH` request](/api/resources/custom_hostnames/methods/edit/) to the specific hostname ID to set metadata for that hostname, for example:
```bash
curl --request PATCH \
"https://api.cloudflare.com/client/v4/zones/{zone_id}/custom_hostnames/{hostname_id}" \
--header "X-Auth-Email: " \
--header "X-Auth-Key: " \
--header "Content-Type: application/json" \
--data '{
"ssl": {
"method": "http",
"type": "dv"
},
"custom_metadata": {
"customer_id": "12345",
"redirect_to_https": true,
"security_tag": "low"
}
}'
```
Changes to metadata will propagate across Cloudflare’s edge within 30 seconds.
---
## Accessing custom metadata from a Cloudflare Worker
The metadata object will be accessible on each request using the `request.cf.hostMetadata` property. You can then read the data, and customize any behavior on it using the Worker.
In the example below we will use the user_id in the Worker that was submitted using the API call above `"custom_metadata":{"customer_id":"12345","redirect_to_https": true,"security_tag":"low"}`, and set a request header to send the `customer_id` to the origin:
```js
export default {
/**
* Fetch and add a X-Customer-Id header to the origin based on hostname
* @param {Request} request
*/
async fetch(request, env, ctx): Promise {
const customer_id = request.cf.hostMetadata.customer_id;
const newHeaders = new Headers(request.headers);
newHeaders.append('X-Customer-Id', customer_id);
const init = {
headers: newHeaders,
method: request.method,
};
return fetch(request.url, init);
},
} satisfies ExportedHandler;
```
## Accessing custom metadata in a rule expression
Use the [`cf.hostname.metadata`](/ruleset-engine/rules-language/fields/reference/cf.hostname.metadata/) field to access the metadata object in rule expressions. To obtain the different values from the JSON object, use the [`lookup_json_string`](/ruleset-engine/rules-language/functions/#lookup_json_string) function.
The following rule expression defines that there will be a rule match if the `security_tag` value in custom metadata contains the value `low`:
```txt
lookup_json_string(cf.hostname.metadata, "security_tag") eq "low"
```
---
## Best practices
- Ensure that the JSON schema used is fixed: changes to the schema without corresponding Cloudflare Workers changes will potentially break websites, or fall back to any defined “default” behavior
- Prefer a flat JSON structure
- Use string keys in snake_case (rather than camelCase or PascalCase)
- Use proper booleans (true/false rather than `true` or `1` or `0`)
- Use numbers to represent integers instead of strings (`1` or `2` instead of `"1"` or `"2"`)
- Define fallback behaviour in the non-presence of metadata
- Define fallback behaviour if a key or value in the metadata are unknown
General guidance is to follow [Google’s JSON Style guide](https://google.github.io/styleguide/jsoncstyleguide.xml) where appropriate.
---
## Limitations
There are some limitations to the metadata that can be provided to Cloudflare:
- It must be valid JSON.
- Any origin resolution — for example, directing requests for a given hostname to a specific backend — must be provided as a hostname that exists within Cloudflare’s DNS (even for non-authoritative setups). Providing an IP address directly will cause requests to error.
- The total payload must not exceed 4 KB.
- It requires a Cloudflare Worker that knows how to process the schema and trigger logic based on the contents.
- Custom metadata cannot be set on custom hostnames that contain wildcards.
:::note
Be careful when modifying the schema. Adding, removing, or changing keys and possible values may cause the Cloudflare Worker to either ignore the data or return an error for requests that trigger it.
:::
### Terraform support
[Terraform](/terraform/) only allows maps of a single type, so Cloudflare's Terraform support for custom metadata for custom hostnames is limited to string keys and values.
---
# Custom hostnames
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/domain-support/
import { DirectoryListing } from "~/components";
Cloudflare for SaaS allows you, as a SaaS provider, to extend the benefits of Cloudflare products to custom domains by adding them to your zone as custom hostnames. We support adding hostnames that are a subdomain of your zone (for example, `sub.serviceprovider.com`) and vanity domains (for example, `customer.com`) to your SaaS zone.
## Resources
---
# Move hostnames
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/domain-support/migrating-custom-hostnames/
As a SaaS provider, you may want, or have, multiple zones to manage hostnames. Each zone can have different configurations or origins, as well as correlate to varying products. You might shift custom hostnames between zones to enable or disable certain features. Cloudflare allows migration within the same account through the steps below:
***
## CNAME
If your custom hostname uses a CNAME record, add the custom hostname to the new zone and [update your DNS record](/dns/manage-dns-records/how-to/create-dns-records/#edit-dns-records) to point to the new zone.
:::note
If you would like to migrate the custom hostname without end customers changing the DNS target, use [apex proxying](/cloudflare-for-platforms/cloudflare-for-saas/start/advanced-settings/apex-proxying/).
:::
1. [Add custom hostname](/cloudflare-for-platforms/cloudflare-for-saas/start/getting-started/) to your new zone.
2. Direct your customer to [change the DNS record](/dns/manage-dns-records/how-to/create-dns-records/#edit-dns-records) so that it points to the new zone.
3. Confirm that the custom hostname has validated in the new zone.
4. Wait for the certificate to validate automatically through Cloudflare or [validate it using Domain Control Validation (DCV)](/ssl/edge-certificates/changing-dcv-method/methods/#perform-dcv).
5. Remove custom hostname from the old zone.
Once these steps are complete, the custom hostname's traffic will route to the second SaaS zone and will use its configuration.
## A record
Through [Apex Proxying](/cloudflare-for-platforms/cloudflare-for-saas/start/advanced-settings/apex-proxying/) or [BYOIP](/byoip/), you can migrate the custom hostname without action from your end customer.
1. Verify with the account team that your apex proxying IPs have been assigned to both SaaS zones.
2. [Add custom hostname](/cloudflare-for-platforms/cloudflare-for-saas/start/getting-started/) to the new zone.
3. Confirm that the custom hostname has validated in the new zone.
4. Wait for the certificate to validate automatically through Cloudflare or [validate it using DCV](/ssl/edge-certificates/changing-dcv-method/methods/#perform-dcv).
5. Remove custom hostname from the old zone.
:::note
The most recently edited custom hostname will be active. For instance, `example.com` exists on `SaaS Zone 1`. It is added to `SaaS Zone 2`. Because it was activated more recently on `SaaS Zone 2`, that is where it will be active. However, if edits are made to example.com on `SaaS Zone 1`, it will reactivate on that zone instead of `SaaS Zone 2`.
:::
## Wildcard certificate
If you are migrating custom hostnames that rely on a Wildcard certificate, Cloudflare cannot automatically complete Domain Control Validation (DCV).
1. [Add custom hostname](/cloudflare-for-platforms/cloudflare-for-saas/start/getting-started/) to the new zone.
2. Direct your customer to [change the DNS record](/dns/manage-dns-records/how-to/create-dns-records/#edit-dns-records) so that it points to the new zone.
3. [Validate the certificate](/ssl/edge-certificates/changing-dcv-method/methods/#perform-dcv) on the new zone through DCV.
The custom hostname can activate on the new zone even if the certificate is still active on the old zone. This ensures a valid certificate exists during migration. However, it is important to validate the certificate on the new zone as soon as possible.
:::note
Verify that the custom hostname successfully activated after the migration in the Cloudflare dashboard by selecting **SSL/TLS** > **Custom hostnames** > **`{your custom hostname}`**.
:::
---
# Remove custom hostnames
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/domain-support/remove-custom-hostnames/
import { Render, TabItem, Tabs } from "~/components";
As a SaaS provider, your customers may decide to no longer participate in your service offering. If that happens, you need to stop routing traffic through those custom hostnames.
## Domains using Cloudflare
If your customer's domain is also using Cloudflare, they can stop routing their traffic through your custom hostname by updating their Cloudflare DNS.
If they update their [`CNAME` record](/cloudflare-for-platforms/cloudflare-for-saas/start/getting-started/#3-have-customer-create-cname-record) so that it no longer points to your `CNAME` target:
- The domain's traffic will not route through your custom hostname.
- The custom hostname will enter into a **Moved** state.
If the custom hostname is in a **Moved** state for seven days, it will transition into a **Deleted** state.
## Domains not using Cloudflare
If your customer's domain is not using Cloudflare, you must remove a customer's custom hostname from your zone if they decide to churn.
This is especially important if your end customers are using Cloudflare because if the custom hostname changes the DNS target to point away from your SaaS zone, the custom hostname will continue to route to your service. This is a result of the [custom hostname priority logic](/ssl/reference/certificate-and-hostname-priority/#hostname-priority).
To delete a custom hostname and any issued certificates using the API, send a [`DELETE` request](/api/resources/custom_hostnames/methods/delete/).
## For end customers
---
# Connection request details
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/reference/connection-details/
When forwarding connections to your origin server, Cloudflare will set request parameters according to the following:
## Host header
Cloudflare will not alter the Host header by default, and will forward exactly as sent by the client. If you wish to change the value of the Host header you can utilise [Page-Rules](/workers/configuration/workers-with-page-rules/) or [Workers](/workers/) using the steps outlined in [certificate management](/cloudflare-for-platforms/cloudflare-for-saas/security/certificate-management/).
## SNI
When establishing a TLS connection to your origin server, if the request is being sent to your configured Fallback Host then the value of the SNI sent by Cloudflare will match the value of the Host header sent by the client (i.e. the custom hostname).
If however the request is being forwarded to a Custom Origin, then the value of the SNI will be that of the Custom Origin.
---
# Reference
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/reference/
import { DirectoryListing } from "~/components";
---
# Token validity periods
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/reference/token-validity-periods/
import { Render } from "~/components"
When you perform [TXT](/cloudflare-for-platforms/cloudflare-for-saas/security/certificate-management/issue-and-validate/validate-certificates/txt/) domain control validation, you will need to share these tokens with your customers.
However, these tokens expire after a certain amount of time, depending on your chosen certificate authority.
| Certificate authority | Token validity |
| --------------------- | -------------- |
| Let's Encrypt | 7 days |
| Google Trust Services | 14 days |
| SSL.com | 14 days |
:::caution
:::
---
# Troubleshooting
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/reference/troubleshooting/
import { Details } from "~/components";
## Rate limits
By default, you may issue up to 15 certificates per minute. Only successful submissions (POSTs that return 200) are counted towards your limit. If you exceed your limit, you will be prevented from issuing new certificates for 30 seconds.
If you require a higher rate limit, contact your Customer Success Manager.
***
## Purge cache
To remove specific files from Cloudflare’s cache, [purge the cache](/cache/how-to/purge-cache/purge-by-single-file/) while specifying one or more hosts.
***
## Resolution error 1016 (Origin DNS error) when accessing the custom hostname
Cloudflare returns a 1016 error when the custom hostname cannot be routed or proxied.
There are three main causes of error 1016:
1. Custom Hostname ownership validation is not complete. To check validation status, run an API call to [search for a certificate by hostname](/cloudflare-for-platforms/cloudflare-for-saas/start/common-api-calls/) and check the verification error field: `"verification_errors": ["custom hostname does not CNAME to this zone."]`.
2. Fallback Origin is not [correctly set](/cloudflare-for-platforms/cloudflare-for-saas/start/getting-started/#1-create-fallback-origin). Confirm that you have created a DNS record for the fallback origin and also set the fallback origin.
3. A Wildcard Custom Hostname has been created, but the requested hostname is associated with a domain that exists in Cloudflare as a standalone zone. In this case, the [hostname priority](/ssl/reference/certificate-and-hostname-priority/#hostname-priority) for the standalone zone will take precedence over the wildcard custom hostname. This behavior applies even if there is no DNS record for this standalone zone hostname.
In this scenario each hostname that needs to be served by the Cloudflare for SaaS parent zone needs to be added as an individual Custom Hostname.
:::note
If you encounter other 1XXX errors, refer to [Troubleshooting Cloudflare 1XXX Errors](/support/troubleshooting/cloudflare-errors/troubleshooting-cloudflare-1xxx-errors/).
:::
***
## Custom hostname in Moved status
To move a custom hostname back to an Active status, send a [PATCH request](/api/resources/custom_hostnames/methods/edit/) to restart the hostname validation. A Custom Hostname in a Moved status is deleted after 7 days.
In some circumstances, custom hostnames can also enter a **Moved** state if your customer changes their DNS records pointing to your SaaS service. For more details, refer to [Remove custom hostnames](/cloudflare-for-platforms/cloudflare-for-saas/domain-support/remove-custom-hostnames/).
***
## CAA Errors
The `caa_error` in the status of a custom hostname means that the CAA records configured on the domain prevented the Certificate Authority to issue the certificate.
You can check which CAA records are configured on a domain using the `dig` command:
`dig CAA example.com`
You will need to ensure that the required CAA records for the selected Certificate Authority are configured.
For example, here are the records required to issue [Let's Encrypt](https://letsencrypt.org/docs/caa/) and [Google Trust Services](https://pki.goog/faq/#caa) certificates:
```
example.com CAA 0 issue "pki.goog; cansignhttpexchanges=yes"
example.com CAA 0 issuewild "pki.goog; cansignhttpexchanges=yes"
example.com CAA 0 issue "letsencrypt.org"
example.com CAA 0 issuewild "letsencrypt.org"
example.com CAA 0 issue "ssl.com"
example.com CAA 0 issuewild "ssl.com"
```
More details can be found on the [CAA records FAQ](/ssl/edge-certificates/troubleshooting/caa-records/).
## Older devices have issues connecting
As Let's Encrypt - one of the [certificate authorities (CAs)](/ssl/reference/certificate-authorities/) used by Cloudflare - has announced changes in its [chain of trust](/ssl/concepts/#chain-of-trust), starting September 9, 2024, there may be issues with older devices trying to connect to your custom hostname certificate.
Consider the following solutions:
- Use the [Edit Custom Hostname](/api/resources/custom_hostnames/methods/edit/) endpoint to set the `certificate_authority` parameter to an empty string (`""`): this sets the custom hostname certificate to "default CA", leaving the choice up to Cloudflare. Cloudflare will always attempt to issue the certificate from a more compatible CA, such as [Google Trust Services](/ssl/reference/certificate-authorities/#google-trust-services), and will only fall back to using Let’s Encrypt if there is a [CAA record](/ssl/edge-certificates/caa-records/) in place that blocks Google from issuing a certificate.
```sh
curl --request PATCH \\
https://api.cloudflare.com/client/v4/zones/{zone_id}/custom_hostnames/{custom_hostname_id} \\
--header "X-Auth-Email: " \\
--header "X-Auth-Key: " \\
--header "Content-Type: application/json" \\
--data '{
"ssl": {
"method": "txt",
"type": "dv",
"certificate_authority": ""
}
}'
```
- Use the [Edit Custom Hostname](/api/resources/custom_hostnames/methods/edit/) endpoint to set the `certificate_authority` parameter to `google`: this sets Google Trust Services as the CA for your custom hostnames. In your API call, make sure to also include `method` and `type` in the `ssl` object.
- If you are using a custom certificate for your custom hostname, refer to the [custom certificates troubleshooting](/ssl/edge-certificates/custom-certificates/troubleshooting/#lets-encrypt-chain-update).
## Custom hostname fails to verify because the zone is held
The [zone hold feature](/fundamentals/setup/account/account-security/zone-holds/) is a toggle that will prevent their zone from being activated on other Cloudflare account.
When the option `Also prevent subdomains` is enabled, this prevents the verification of custom hostnames for this domain. The custom hostname will remain in the `Blocked` status, with the following error message: `The hostname is associated with a held zone. Please contact the owner of this domain to have the hold removed.` In this case, the owner of the zone needs to [release the hold](/fundamentals/setup/account/account-security/zone-holds/#release-zone-holds) before the custom hostname can become activated.
## Hostnames over 64 characters
The Common Name (CN) restriction establishes a limit of 64 characters ([RFC 5280](https://www.rfc-editor.org/rfc/rfc5280.html)). If you have a hostname that exceeds this length, you may find the following error:
```txt
Since no host is 64 characters or fewer, Cloudflare Branding is required. Please check your input and try again. (1469)
```
To solve this, you can set `cloudflare_branding` to `true` when [creating your custom hostnames](/cloudflare-for-platforms/cloudflare-for-saas/domain-support/create-custom-hostnames/#hostnames-over-64-characters) via API.
Cloudflare branding means that `sni.cloudflaressl.com` will be added as the certificate Common Name (CN) and the long hostname will be included as a part of the Subject Alternative Name (SAN).
---
# Deprecation - Version 1
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/reference/versioning/
The first version of SSL for SaaS will be deprecated on September 1, 2021.
## Why is SSL for SaaS changing?
In SSL for SaaS v1, traffic for Custom Hostnames is proxied to the origin based on the IP addresses assigned to the zone with SSL for SaaS enabled. This IP-based routing introduces complexities that prevented customers from making changes with zero downtime.
SSL for SaaS v2 removes IP-based routing and its associated problems. Instead, traffic is proxied to the origin based on the custom hostname of the SaaS zone. This means that Custom Hostnames will now need to pass a **hostname verification** step after Custom Hostname creation and in addition to SSL certificate validation. This adds a layer of security from SSL for SaaS v1 by ensuring that only verified hostnames are proxied to your origin.
## What action is needed?
To ensure that your service is not disrupted, you need to perform an additional ownership check on every new Custom Hostname. There are three methods to verify ownership: TXT, HTTP, and CNAME. Use TXT and HTTP for pre-validation to validate the Custom Hostname before traffic is proxied by Cloudflare’s edge.
### Recommended validation methods
Using a [TXT](#dns-txt-record) or [HTTP](#http-token) validation method helps you avoid downtime during your migration. If you choose to use [CNAME validation](#cname-validation), your domain might fall behind on its [backoff schedule](/ssl/edge-certificates/changing-dcv-method/validation-backoff-schedule/).
#### DNS TXT Record
When creating a Custom Hostname with the TXT method through the [API](/api/resources/custom_hostnames/methods/create/), a TXT ownership\_verification record is provided for your customer to add to their DNS for the ownership validation check. When the TXT record is added, the Custom Hostname will be marked as **Active** in the Cloudflare SSL/TLS app under the Custom Hostnames tab.
#### HTTP Token
When creating a Custom Hostname with the HTTP through the [API](/api/resources/custom_hostnames/methods/create/), an HTTP ownership\_verification token is provided. HTTP verification is used mainly by organizations with a large deployed base of custom domains with HTTPS support. Serving the HTTP token from your origin web server allows hostname verification before proxying domain traffic through Cloudflare.
Cloudflare sends GET requests to the http\_url using `User-Agent: Cloudflare Custom Hostname Verification`.
If you validated a hostname that is not proxying traffic through Cloudflare, the Custom Hostname will be marked as **Active** in the Cloudflare SSL/TLS app when the HTTP token is verified (under the **Custom Hostnames** tab).
If your hostname is already proxying traffic through Cloudflare, then HTTP validation is not enough by itself and the hostname will only go active when DNS-based validation is complete.
### Other validation methods
Though you can use [CNAME validation](#cname-validation), we recommend you either use a [TXT](#dns-txt-record) or [HTTP](#http-token) validation method.
#### CNAME Validation
Custom Hostnames can also be validated once Cloudflare detects that the Custom Hostname is a CNAME record pointing to the fallback record configured for the SSL for SaaS domain. Though this is the simplest validation method, it increases the risk of errors. Since a CNAME record would also route traffic to Cloudflare’s edge, traffic may reach our edge before the Custom Hostname has completed validation or the SSL certificate has issued.
Once you have tested and added the hostname validation step to your Custom Hostname creation process, please contact your Cloudflare Account Team to schedule a date to migrate your SSL for SaaS v1 zones. Your Cloudflare Account Team will work with you to validate your existing Custom Hostnames without downtime.
## If you are using BYOIP or Apex Proxying:
Both BYOIP addresses and IP addresses configured for Apex Proxying allow for hostname validation to complete successfully by having either a BYOIP address or an Apex Proxy IP address as the target of a DNS A record for a custom hostname.
## What is available in the new version of SSL for SaaS?
SSL for SaaS v2 is functionally equivalent to SSL for SaaS v1, but removes the requirements to use specific anycast IP addresses at Cloudflare’s edge and Cloudflare’s Universal SSL product with the SSL for SaaS zone.
:::note
SSL for SaaS v2 is now called Cloudflare for SaaS.
:::
## What happens during the migration?
Once the migration has been started for your zone(s), Cloudflare will require every Custom Hostname to pass a hostname verification check. Existing Custom Hostnames that are proxying to Cloudflare with a DNS CNAME record will automatically re-validate and migrate to the new version with no downtime. Any Custom Hostnames created after the start of the migration will need to pass the hostname validation check using one of the validation methods mentioned above.
:::note
You can revert the migration at any time.
:::
### Before the migration
Before your migration, you should:
1. To test validation methods, set up a test zone and ask your Solutions Engineer (SE) to enable SSL for SaaS v2.
2. Wait for your SE to run our pre-migration tool. This tool groups your hostnames into one of the following statuses:
* `test_pending`: In the process of being verified or was unable to be verified and re-queued for verification. A custom hostname will be re-queued 25 times before moving to the `test_failed` status.
* `test_active`: Passed CNAME verification
* `test_active_apex`: Passed Apex Proxy verification
* `test_blocked`: Hostname will be blocked during the migration because hostname belongs to a banned zone. Contact your CSM to verify banned custom hostnames and proceed with the migration.
* `test_failed`: Failed hostname verification 25 times
3. Review the results of our pre-migration tool (run by your Solutions Engineer) using one of the following methods:
* Via the API: `https://api.cloudflare.com/client/v4/zones/{zone_tag}/custom_hostnames?hostname_status={status}`
* Via a CSV file (provided by your SE)
* Via the Cloudflare dashboard:

4. Approve the migration. Your Cloudflare account team will work with you to schedule a migration window for each of your SSL for SaaS zones.
## During the migration
After the migration has started and has had some time to progress, Cloudflare will generate a list of Custom Hostnames that failed to migrate and ask for your approval to complete the migration. When you give your approval, the migration will be complete, SSL for SaaS v1 will be disabled for the zone, and any Custom Hostname that has not completed hostname validation will no longer function.
The migration timeline depends on the number of Custom Hostnames. For example, if a zone has fewer than 10,000 Custom Hostnames, the list can be generated around an hour after beginning the migration. If a zone has millions of Custom Hostnames, it may take up to 24 hours to identify instances that failed to successfully migrate.
When your Cloudflare Account Team asks for approval to complete the migration, please respond in a timely manner. You will have **two weeks** to validate any remaining Custom Hostnames before they are systematically deleted.
## When is the migration?
The migration process starts on March 31, 2021 and will continue until final deprecation on September 1, 2021.
If you would like to begin the migration process before March 31, 2021, please contact your Cloudflare Account Team and they will work with you to expedite the process. Otherwise, your Cloudflare Account Team will reach out to you with a time for a migration window so that your zones are migrated before **September 1, 2021** end-of-life date.
## What if I have additional questions?
If you have any questions, please contact your Cloudflare Account Team or [SaaSv2@cloudflare.com](mailto:saasv2@cloudflare.com).
---
# How it works
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/saas-customers/how-it-works/
import { Example } from "~/components";
Orange-to-Orange (O2O) is a specific traffic routing configuration where traffic routes through two Cloudflare zones: the first Cloudflare zone is owned by customer 1 and the second Cloudflare zone is owned by customer 2, who is considered a SaaS provider.
If one or more hostnames are onboarded to a SaaS Provider that uses Cloudflare products as part of their platform - specifically the [Cloudflare for SaaS product](/cloudflare-for-platforms/cloudflare-for-saas/) - those hostnames will be created as [custom hostnames](/cloudflare-for-platforms/cloudflare-for-saas/domain-support/) in the SaaS Provider's zone.
To give the SaaS provider permission to route traffic through their zone, any custom hostname must be activated by you (the SaaS customer) by placing a [CNAME record](/cloudflare-for-platforms/cloudflare-for-saas/start/getting-started/#3-have-customer-create-cname-record) on your authoritative DNS. If your authoritative DNS is Cloudflare, you have the option to [proxy](/fundamentals/concepts/how-cloudflare-works/#application-services) your CNAME record, achieving an Orange-to-Orange setup.
## With O2O
If you have your own Cloudflare zone (`example.com`) and your zone contains a [proxied DNS record](/dns/proxy-status/) matching the custom hostname (`mystore.example.com`) with a **CNAME** target defined by the SaaS Provider, then O2O will be enabled.
DNS management for **example.com**
| **Type** | **Name** | **Target** | **Proxy status** |
| -------- | ------------ | --------------------------------- | ---------------- |
| `CNAME` | `mystore` | `customers.saasprovider.com` | Proxied |
With O2O enabled, the settings configured in your Cloudflare zone will be applied to the traffic first, and then the settings configured in the SaaS provider's zone will be applied to the traffic second.
```mermaid
flowchart TD
accTitle: O2O-enabled traffic flow diagram
A[Website visitor]
subgraph Cloudflare
B[Customer-owned zone]
C[SaaS Provider-owned zone]
end
D[SaaS Provider Origin]
A --> B
B --> C
C --> D
```
## Without O2O
If you do not have your own Cloudflare zone and have only onboarded one or more of your hostnames to a SaaS Provider, then O2O will not be enabled.
Without O2O enabled, the settings configured in the SaaS Provider's zone will be applied to the traffic.
```mermaid
flowchart TD
accTitle: Your zone using a SaaS provider, but without O2O
A[Website visitor]
subgraph Cloudflare
B[SaaS Provider-owned zone]
end
C[SaaS Provider Origin]
A --> B
B --> C
```
---
# SaaS customers
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/saas-customers/
import { DirectoryListing } from "~/components"
Cloudflare partners with many [SaaS providers](/cloudflare-for-platforms/cloudflare-for-saas/saas-customers/provider-guides/) to extend our performance and security benefits to your website.
If you are a SaaS customer, you can take this process a step further by managing your own zone on Cloudflare. This setup - known as **Orange-to-Orange (O2O)** - allows you to benefit from your provider's setup but still customize how Cloudflare treats incoming traffic to your zone.
## Related resources
---
# Product compatibility
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/saas-customers/product-compatibility/
As a general rule, settings on the customer zone will override settings on the SaaS zone. In addition, [Orange-to-Orange](/cloudflare-for-platforms/cloudflare-for-saas/saas-customers/) does not permit traffic directed to a custom hostname zone into another custom hostname zone.
The following table provides a list of compatibility guidelines for various Cloudflare products and features.
:::note
This is not an exhaustive list of Cloudflare products and features.
:::
| Product | Customer zone | SaaS provider zone | Notes |
| --------------------------------------------------------------------------------------------------- | ------------- | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Access](/cloudflare-for-platforms/cloudflare-for-saas/security/secure-with-access/) | Yes | Yes | |
| [API Shield](/api-shield/) | Yes | No | |
| [Argo Smart Routing](/argo-smart-routing/) | No | Yes | Customer zones can still use Smart Routing for non-O2O traffic. |
| [Bot Management](/bots/plans/bm-subscription/) | Yes | Yes | |
| [Browser Integrity Check](/waf/tools/browser-integrity-check/) | Yes | Yes | |
| [Cache](/cache/) | Yes\* | Yes | Though caching is possible on a customer zone, it is generally discouraged (especially for HTML).
Your SaaS provider likely performs its own caching outside of Cloudflare and caching on your zone might lead to out-of-sync or stale cache states.
Customer zones can still cache content that are not routed through a SaaS provider's zone. |
| [China Network](/china-network/) | No | No | |
| [DNS](/dns/) | Yes\* | Yes | As a SaaS customer, do not remove the records related to your Cloudflare for SaaS setup.
Otherwise, your traffic will begin routing away from your SaaS provider. |
| [HTTP/2 prioritization](https://blog.cloudflare.com/better-http-2-prioritization-for-a-faster-web/) | Yes | Yes\* | This feature must be enabled on the customer zone to function. |
| [Image resizing](/images/transform-images/) | Yes | Yes | |
| IPv6 | Yes | Yes | |
| [IPv6 Compatibility](/network/ipv6-compatibility/) | Yes | Yes\* | If the customer zone has **IPv6 Compatibility** enabled, generally the SaaS zone should as well.
If not, make sure the SaaS zone enables [Pseudo IPv4](/network/pseudo-ipv4/). |
| [Load Balancing](/load-balancing/) | No | Yes | Customer zones can still use Load Balancing for non-O2O traffic. |
| [Page Rules](/rules/page-rules/) | Yes\* | Yes | Page Rules that match the subdomain used for O2O may block or interfere with the flow of visitors to your website. |
| [Mirage](/speed/optimization/images/mirage/) | Yes | Yes | |
| [Origin Rules](/rules/origin-rules/) | Yes | Yes | Enterprise zones can configure Origin Rules, by setting the Host Header and DNS Overrides to direct traffic to a SaaS zone. |
| [Page Shield](/page-shield/) | Yes | Yes | |
| [Polish](/images/polish/) | Yes\* | Yes | Polish only runs on cached assets. If the customer zone is bypassing cache for SaaS zone destined traffic, then images optimized by Polish will not be loaded from origin. |
| [Rate Limiting](/waf/rate-limiting-rules/) | Yes\* | Yes | Rate Limiting rules that match the subdomain used for O2O may block or interfere with the flow of visitors to your website. |
| [Rocket Loader](/speed/optimization/content/rocket-loader/) | No | No | |
| [Security Level](/waf/tools/security-level/) | Yes | Yes | |
| [Spectrum](/spectrum/) | No | No | |
| [Transform Rules](/rules/transform/) | Yes\* | Yes | Transform Rules that match the subdomain used for O2O may block or interfere with the flow of visitors to your website. |
| [WAF custom rules](/waf/custom-rules/) | Yes | Yes | WAF custom rules that match the subdomain used for O2O may block or interfere with the flow of visitors to your website. |
| [WAF managed rules](/waf/managed-rules/) | Yes | Yes | |
| [Waiting Room](/waiting-room/) | Yes | Yes | |
| [Websockets](/network/websockets/) | No | No | |
| [Workers](/workers/) | Yes\* | Yes | Similar to Page Rules, Workers that match the subdomain used for O2O may block or interfere with the flow of visitors to your website. |
| [Zaraz](/zaraz/) | Yes | No | |
---
# Remove domain
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/saas-customers/remove-domain/
import { Render } from "~/components"
---
# Security
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/security/
Cloudflare for SaaS provides increased security per custom hostname through:
* [Certificate management](/cloudflare-for-platforms/cloudflare-for-saas/security/certificate-management/)
* [Issue certificates through Cloudflare](/cloudflare-for-platforms/cloudflare-for-saas/security/certificate-management/issue-and-validate/)
* [Upload your own certificates](/cloudflare-for-platforms/cloudflare-for-saas/security/certificate-management/custom-certificates/)
* Control your traffic's level of encryption with [TLS settings](/cloudflare-for-platforms/cloudflare-for-saas/security/certificate-management/enforce-mtls/)
* Create and deploy WAF custom rules, rate limiting rules, and managed rulesets using [WAF for SaaS](/cloudflare-for-platforms/cloudflare-for-saas/security/waf-for-saas/)
---
# Secure with Cloudflare Access
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/security/secure-with-access/
Cloudflare Access provides visibility and control over who has access to your [custom hostnames](/cloudflare-for-platforms/cloudflare-for-saas/domain-support/). You can allow or block users based on identity, device posture, and other [Access rules](/cloudflare-one/policies/access/).
## Prerequisites
* You must have an active custom hostname. For setup instructions, refer to [Configuring Cloudflare for SaaS](/cloudflare-for-platforms/cloudflare-for-saas/start/getting-started/).
* You must have a Cloudflare Zero Trust plan in your SaaS provider account. Learn more about [getting started with Zero Trust](/cloudflare-one/setup/).
* You can only run Access on custom hostnames if they are managed externally to Cloudflare or in a separate Cloudflare account. If the custom hostname zone is in the same account as the SaaS zone, the Access application will not be applied.
## Setup
1. At your SaaS provider account, select [Zero Trust](https://one.dash.cloudflare.com).
2. Go to **Access** > **Applications**.
3. Select **Add an application** and, for type of application, select **Self-hosted**.
4. Enter a name for your Access application and, in **Session Duration**, choose how often the user's [application token](/cloudflare-one/identity/authorization-cookie/application-token/) should expire.
5. Select **Add public hostname**.
6. For **Input method**, select _Custom_.
7. In **Hostname**, enter your custom hostname (for example, `mycustomhostname.com`).
8. Follow the remaining [self-hosted application creation steps](/cloudflare-one/applications/configure-apps/self-hosted-public-app/) to publish the application.
---
# Common API Calls
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/start/common-api-calls/
As a SaaS provider, you may want to configure and manage Cloudflare for SaaS [via the API](/api/) rather than the [Cloudflare dashboard](https://dash.cloudflare.com/). Below are relevant API calls for creating, editing, and deleting custom hostnames, as well as monitoring, updating, and deleting fallback origins. Further details can be found in the [Cloudflare API documentation](/api/).
***
## Custom hostnames
| Endpoint | Notes |
| -------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| [List custom hostnames](/api/resources/custom_hostnames/methods/list/) | Use the `page` parameter to pull additional pages. Add a `hostname` parameter to search for specific hostnames. |
| [Create custom hostname](/api/resources/custom_hostnames/methods/create/) | In the `validation_records` object of the response, use the `txt_name` and `txt_record` listed to validate the custom hostname. |
| [Custom hostname details](/api/resources/custom_hostnames/methods/get/) | |
| [Edit custom hostname](/api/resources/custom_hostnames/methods/edit/) | When sent with an `ssl` object that matches the existing value, indicates that hostname should restart domain control validation (DCV). |
| [Delete custom hostname](/api/resources/custom_hostnames/methods/delete/) | Also deletes any associated SSL/TLS certificates. |
## Fallback origins
Our API includes the following endpoints related to the [fallback origin](/cloudflare-for-platforms/cloudflare-for-saas/start/getting-started/#1-create-fallback-origin) of a custom hostname:
* [Get fallback origin](/api/resources/custom_hostnames/subresources/fallback_origin/methods/get/)
* [Update fallback origin](/api/resources/custom_hostnames/subresources/fallback_origin/methods/update/)
* [Remove fallback origin](/api/resources/custom_hostnames/subresources/fallback_origin/methods/delete/)
---
# Enable
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/start/enable/
To enable Cloudflare for SaaS for your account:
1. Log into the [Cloudflare dashboard](https://dash.cloudflare.com).
2. Select your account and zone.
3. Go to **SSL/TLS** > **Custom Hostnames**.
4. Select **Enable**.
5. The next step depends on the zone's plan:
* **Enterprise**: Can preview this product as a [non-contract service](/fundamentals/subscriptions-and-billing/preview-services/), which provide full access, free of metered usage fees, limits, and certain other restrictions.
* **Non-enterprise**: Will have to enter payment information.
:::note
Different zone plan levels have access to different features. For more details, refer to [Plans](/cloudflare-for-platforms/cloudflare-for-saas/plans/).
:::
---
# Configuring Cloudflare for SaaS
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/start/getting-started/
import { Example, Render } from "~/components"
***
***
## Initial setup
### 1. Create fallback origin
### 2. (Optional) Create CNAME target
The CNAME target — optional, but highly encouraged — provides a friendly and more flexible place for customers to [route their traffic](#3-have-customer-create-cname-record). You may want to use a subdomain such as `customers..com`.
[Create](/dns/manage-dns-records/how-to/create-dns-records/#create-dns-records) a proxied CNAME that points your CNAME target to your fallback origin (can be a wildcard such as `*.customers.saasprovider.com`).
| **Type** | **Name** | **Target** | **Proxy status** |
| -------- | ------------ | --------------------------------- | ---------------- |
| `CNAME` | `.customers` | `proxy-fallback.saasprovider.com` | Proxied |
***
## Per-hostname setup
### 3. Have customer create CNAME record
To finish the custom hostname setup, your customer needs to set up a CNAME record at their authoritative DNS that points to your [CNAME target](#2-optional-create-cname-target) [^1].
Your customer's CNAME record might look like the following:
```txt
mystore.example.com CNAME customers.saasprovider.com
```
This record would route traffic in the following way:
```mermaid
flowchart TD
accTitle: How traffic routing works with a CNAME target
A[Request to mystore.example.com] --> B[customers.saasprovider.com]
B --> C[proxy-fallback.saasprovider.com]
```
Requests to `mystore.example.com` would go to your CNAME target (`customers.saasprovider.com`), which would then route to your fallback origin (`proxy-fallback.saasprovider.com`).
[^1]:
#### Service continuation
---
# Get started
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/start/
import { DirectoryListing } from "~/components";
---
# Custom limits
URL: https://developers.cloudflare.com/cloudflare-for-platforms/workers-for-platforms/configuration/custom-limits/
Custom limits allow you to programmatically enforce limits on your customers' Workers' resource usage. You can set limits for the maximum CPU time and number of subrequests per invocation. If a user Worker hits either of these limits, the user Worker will immediately throw an exception.
## Set Custom limits
Custom limits can be set in the dynamic dispatch Worker:
```js
export default {
async fetch(request, env) {
try {
// parse the URL, read the subdomain
let workerName = new URL(request.url).host.split('.')[0];
let userWorker = env.dispatcher.get(
workerName,
{},
{// set limits
limits: {cpuMs: 10, subRequests: 5}
}
);
return await userWorker.fetch(request);
} catch (e) {
if (e.message.startsWith('Worker not found')) {
// we tried to get a worker that doesn't exist in our dispatch namespace
return new Response('', { status: 404 });
}
return new Response(e.message, { status: 500 });
}
},
};
```
---
# Configuration
URL: https://developers.cloudflare.com/cloudflare-for-platforms/workers-for-platforms/configuration/
import { DirectoryListing } from "~/components"
---
# Observability
URL: https://developers.cloudflare.com/cloudflare-for-platforms/workers-for-platforms/configuration/observability/
Workers for Platforms provides you with logs and analytics that can be used to share data with end users.
## Logs
Learn how to access logs with Workers for Platforms.
### Workers Trace Events Logpush
Workers Trace Events logpush is used to get raw Workers execution logs. Refer to [Logpush](/workers/observability/logs/logpush/) for more information.
Logpush can be enabled for an entire dispatch namespace or a single user Worker. To capture logs for all of the user Workers in a dispatch namespace:
1. Create a [Logpush job](/workers/observability/logs/logpush/#create-a-logpush-job).
2. Enable [logging](/workers/observability/logs/logpush/#enable-logging-on-your-worker) on your dispatch Worker.
Enabling logging on your dispatch Worker collects logs for both the dispatch Worker and for any user Workers in the dispatch namespace. Logs are automatically collected for all new Workers added to a dispatch namespace. To enable logging for an individual user Worker rather than an entire dispatch namespace, skip step 1 and complete step 2 on your user Worker.
All logs are forwarded to the Logpush job that you have setup for your account. Logpush filters can be used on the `Outcome` or `Script Name` field to include or exclude specific values or send logs to different destinations.
### Tail Workers
A [Tail Worker](/workers/observability/logs/tail-workers/) receives information about the execution of other Workers (known as producer Workers), such as HTTP statuses, data passed to `console.log()` or uncaught exceptions.
Use [Tail Workers](/workers/observability/logs/tail-workers/) instead of Logpush if you want granular control over formatting before logs are sent to their destination to receive [diagnostics channel events](/workers/runtime-apis/nodejs/diagnostics-channel), or if you want logs delivered in real-time.
Adding a Tail Worker to your dispatch Worker collects logs for both the dispatch Worker and for any user Workers in the dispatch namespace. Logs are automatically collected for all new Workers added to a dispatch namespace. To enable logging for an individual user Worker rather than an entire dispatch namespace, add the [Tail Worker configuration](/workers/observability/logs/tail-workers/#configure-tail-workers) directly to the user Worker.
## Analytics
There are two ways for you to review your Workers for Platforms analytics.
### Workers Analytics Engine
[Workers Analytics Engine](/analytics/analytics-engine/) can be used with Workers for Platforms to provide analytics to end users. It can be used to expose events relating to a Workers invocation or custom user-defined events. Platforms can write/query events by script tag to get aggregates over a user’s usage.
### GraphQL Analytics API
Use Cloudflare’s [GraphQL Analytics API](/analytics/graphql-api) to get metrics relating to your Dispatch Namespaces. Use the `dispatchNamespaceName` dimension in the `workersInvocationsAdaptive` node to query usage by namespace.
---
# Outbound Workers
URL: https://developers.cloudflare.com/cloudflare-for-platforms/workers-for-platforms/configuration/outbound-workers/
import { WranglerConfig } from "~/components";
Outbound Workers sit between your customer's Workers and the public Internet. They give you visibility into all outgoing `fetch()` requests from user Workers.

## General Use Cases
Outbound Workers can be used to:
* Log all subrequests to identify malicious domains or usage patterns.
* Create, allow, or block lists for hostnames requested by user Workers.
* Configure authentication to your APIs behind the scenes (without end developers needing to set credentials).
## Use Outbound Workers
To use Outbound Workers:
1. Create a Worker intended to serve as your Outbound Worker.
2. Outbound Worker can be specified as an optional parameter in the [dispatch namespaces](/cloudflare-for-platforms/workers-for-platforms/get-started/configuration/#2-create-a-dispatch-namespace) binding in a project's [Wrangler configuration file](/workers/wrangler/configuration/). Optionally, to pass data from your dynamic dispatch Worker to the Outbound Worker, the variable names can be specified under **parameters**.
Make sure that you have `wrangler@3.3.0` or later [installed](/workers/wrangler/install-and-update/).
```toml
[[dispatch_namespaces]]
binding = "dispatcher"
namespace = ""
outbound = {service = "", parameters = ["params_object"]}
```
3. Edit your dynamic dispatch Worker to call the Outbound Worker and declare variables to pass on `dispatcher.get()`.
```js
export default {
async fetch(request, env) {
try {
// parse the URL, read the subdomain
let workerName = new URL(request.url).host.split('.')[0];
let context_from_dispatcher = {
'customer_name': workerName,
'url': request.url,
}
let userWorker = env.dispatcher.get(
workerName,
{},
{// outbound arguments. object name must match parameters in the binding
outbound: {
params_object: context_from_dispatcher,
}
}
);
return await userWorker.fetch(request);
} catch (e) {
if (e.message.startsWith('Worker not found')) {
// we tried to get a worker that doesn't exist in our dispatch namespace
return new Response('', { status: 404 });
}
return new Response(e.message, { status: 500 });
}
}
}
```
4. The Outbound Worker will now be invoked on any `fetch()` requests from a user Worker. The user Worker will trigger a [FetchEvent](/workers/runtime-apis/handlers/fetch/) on the Outbound Worker. The variables declared in the binding can be accessed in the Outbound Worker through `env.`.
The following is an example of an Outbound Worker that logs the fetch request from user Worker and creates a JWT if the fetch request matches `api.example.com`.
```js
export default {
// this event is fired when the dispatched Workers make a subrequest
async fetch(request, env, ctx) {
// env contains the values we set in `dispatcher.get()`
const customer_name = env.customer_name;
const original_url = env.url;
// log the request
ctx.waitUntil(fetch(
'https://logs.example.com',
{
method: 'POST',
body: JSON.stringify({
customer_name,
original_url,
}),
},
));
const url = new URL(original_url);
if (url.host === 'api.example.com') {
// pre-auth requests to our API
const jwt = make_jwt_for_customer(customer_name);
let headers = new Headers(request.headers);
headers.set('Authorization', `Bearer ${jwt}`);
// clone the request to set new headers using existing body
let new_request = new Request(request, {headers});
return fetch(new_request)
}
return fetch(request)
}
};
```
:::note
Outbound Workers do not intercept fetch requests made from [Durable Objects](/durable-objects/) or [mTLS certificate bindings](/workers/runtime-apis/bindings/mtls/).
:::
---
# Configure Workers for Platforms
URL: https://developers.cloudflare.com/cloudflare-for-platforms/workers-for-platforms/get-started/configuration/
import { Render, PackageManagers, WranglerConfig } from "~/components";
## Prerequisites:
### Enable Workers for Platforms
To enable Workers for Platforms, you will need to purchase the [Workers for Platforms Paid plan](/cloudflare-for-platforms/workers-for-platforms/platform/pricing/).
1. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com/?to=/:account/workers-for-platforms), and select your account.
2. Complete the payment process for the Workers for Platforms Paid plan.
If you are an Enterprise customer, contact your Cloudflare account team to enable Workers for Platforms.
### Learn about Workers for Platforms
Refer to [How Workers for Platforms works](/cloudflare-for-platforms/workers-for-platforms/reference/how-workers-for-platforms-works/) to learn more about Workers for Platforms terminology and architecture.
---
This guide will instruct you on setting up Workers for Platforms. You will configure a [dispatch namespace](/cloudflare-for-platforms/workers-for-platforms/reference/how-workers-for-platforms-works/#dispatch-namespace), a [dynamic dispatch Worker](/cloudflare-for-platforms/workers-for-platforms/reference/how-workers-for-platforms-works/#dynamic-dispatch-worker) and a [user Worker](/cloudflare-for-platforms/workers-for-platforms/reference/how-workers-for-platforms-works/#user-workers) to test a request end to end.
### 1. Create a user Worker
First, create a [user Worker](/cloudflare-for-platforms/workers-for-platforms/reference/how-workers-for-platforms-works/#user-workers). User Workers are Workers that your end users (end developers) will be uploading.
User Workers can be created using C3. C3 (create-cloudflare-cli) is a command-line tool designed to help you setup and deploy Workers to Cloudflare as fast as possible.
Open a terminal window and run C3 to create your Worker project. This example creates a user Worker called `customer-worker-1`.
```sh
npm create cloudflare@latest customer-worker-1 -- --type=hello-world
```
When following the interactive prompts, answer the questions as below:
- Select `no` to using TypeScript.
- **Select `no` to deploying your application.**
### 2. Create a dispatch namespace
Create a [dispatch namespace](/cloudflare-for-platforms/workers-for-platforms/reference/how-workers-for-platforms-works/#dispatch-namespace). A dispatch namespace is made up of a collection of [user Workers](/cloudflare-for-platforms/workers-for-platforms/reference/how-workers-for-platforms-works/#user-workers).
This example creates a dispatch namespace called `testing`. To create a dispatch namespace, run:
```sh
npx wrangler dispatch-namespace create testing
```
### 3. Upload a user Worker to the dispatch namespace
Make sure you are in your user Worker's project directory:
```sh
cd customer-worker-1
```
To upload and deploy the user Worker to the dispatch namespace, running the following command:
```sh
npx wrangler deploy --dispatch-namespace testing
```
### 4. Create a dynamic dispatch Worker
A [dynamic dispatch Worker](/cloudflare-for-platforms/workers-for-platforms/reference/how-workers-for-platforms-works/#dynamic-dispatch-worker) is a specialized routing Worker that directs incoming requests to the appropriate user Workers in your dispatch namespace. Instead of using [Workers Routes](/workers/configuration/routing/routes/), dispatch Workers let you programmatically control request routing through code.
#### Why use a dynamic dispatch Worker?
* **Scale**: Perfect for routing thousands or millions of hostnames to different Workers, without needing to rely on [Workers Routes](/workers/configuration/routing/routes/)
* **Custom routing logic**: Write code to determine exactly how requests should be routed. For example:
* Map hostnames directly to specific Workers
* Route requests based on subdomains
* Use request metadata or headers for routing decisions
* **Add platform functionality**: Build in additional features at the routing layer.
* Run authentication checks before requests reach user Workers
* Sanitize incoming requests
* Attach useful context like user IDs or account information
* Transform requests or responses as needed
**To create your dynamic dispatch Worker:**
Navigate up a level from your user Worker's project directory:
```sh
cd ..
```
Create your dynamic dispatch Worker. In this example, the dispatch Worker is called `my-dispatcher`.
Change to your project's directory:
```sh
cd my-dispatcher
```
Open the Wrangler file in your project directory, and add the dispatch namespace binding:
```toml
[[dispatch_namespaces]]
binding = "DISPATCHER"
namespace = "testing"
```
Add the following to the index.js file:
```js
export default {
async fetch(req, env) {
const worker = env.DISPATCHER.get("customer-worker-1");
return await worker.fetch(req);
},
};
```
This example shows a simple dynamic dispatch Worker that routes all requests to a single user Worker. For more advanced routing patterns, you could route based on hostname, path, custom metadata, or other request properties.
Deploy your dynamic dispatch Worker:
```sh
npx wrangler deploy
```
### 5. Test a request
You will now send a request to the route your dynamic dispatch Worker is on. You should receive the response (`Hello world`) you created in your user Worker (`customer-worker-1`) that you call from your dynamic dispatch Worker (`my-dispatcher`).
Preview the response to your Workers for Platforms project at `https://my-dispatcher..workers.dev/`.
By completing this guide, you have successfully set up a dispatch namespace, dynamic dispatch Worker and user Worker to test a request end to end.
## Related resources
- [Workers for Platforms example project](https://github.com/cloudflare/workers-for-platforms-example) - An end to end example project using Workers for Platforms
---
# Static assets
URL: https://developers.cloudflare.com/cloudflare-for-platforms/workers-for-platforms/configuration/static-assets/
import { Aside } from "@astrojs/starlight/components";
Workers for Platforms lets you deploy front-end applications at scale. By hosting static assets on Cloudflare's global network, you can deliver faster load times worldwide and eliminate the need for external infrastructure. You can also combine these static assets with dynamic logic in Cloudflare Workers, providing a full-stack experience for your customers.
### What you can build
#### Static sites
Host and serve HTML, CSS, JavaScript, and media files directly from Cloudflare's network, ensuring fast loading times worldwide. This is ideal for blogs, landing pages, and documentation sites.
#### Full-stack applications
Combine asset hosting with Cloudflare Workers to power dynamic, interactive applications. Store and retrieve data using Cloudflare KV, D1, and R2 Storage, allowing you to serve both front-end assets and backend logic from a single Worker.
### Benefits
#### Global caching for faster performance
Cloudflare automatically caches static assets at data centers worldwide, reducing latency and improving load times by up to 2x for users everywhere.
#### Scalability without infrastructure management
Your applications scale automatically to handle high traffic without requiring you to provision or manage infrastructure. Cloudflare dynamically adjusts to demand in real time.
#### Unified deployment for static and dynamic content
Deploy front-end assets alongside server-side logic, all within Cloudflare Workers. This eliminates the need for a separate hosting provider and ensures a streamlined deployment process.
---
## Deploy static assets to User Workers
It is common that, as the Platform, you will be responsible for uploading static assets on behalf of your end users. This often looks like this:
1. Your user uploads files (HTML, CSS, images) through your interface.
2. Your platform interacts with the Workers for Platforms APIs to attach the static assets to the User Worker script.
Once you receive the static files from your users (for a new or updated site), complete the following steps to attach the files to the corresponding User Worker:
1. Create an Upload Session
2. Upload file contents
3. Deploy/Update the Worker
After these steps are completed, the User Worker's static assets will be live on the Cloudflare's global network.
### 1. Create an Upload Session
Before sending any file data, you need to tell Cloudflare which files you intend to upload. That list of files is called a manifest. Each item in the manifest includes:
- A file path (for example, `"/index.html"` or `"/assets/logo.png"`)
- A hash (32-hex characters) representing the file contents
- The file size in bytes
#### Example manifest (JSON)
```json
{
"/index.html": {
"hash": "08f1dfda4574284ab3c21666d1ee8c7d4",
"size": 1234
},
"/styles.css": {
"hash": "36b8be012ee77df5f269b11b975611d3",
"size": 5678
}
}
```
To start the upload process, send a POST request to the Create Assets Upload Session [API endpoint](/api/resources/workers_for_platforms/subresources/dispatch/subresources/namespaces/subresources/scripts/subresources/asset_upload/methods/create/).
```bash
POST /accounts/{account_id}/workers/dispatch/namespaces/{namespace}/scripts/{script_name}/assets-upload-session
```
Path Parameters:
- `namespace`: Name of the Workers for Platforms dispatch namespace
- `script_name`: Name of the User Worker
In the request body, include a JSON object listing each file path along with its hash and size. This helps Cloudflare identify which files you intend to upload and allows Cloudflare to check if any of them are already stored.
#### Sample request
```bash
curl -X POST \
"https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/workers/dispatch/namespaces/$NAMESPACE_NAME/scripts/$SCRIPT_NAME/assets-upload-session" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $API_TOKEN" \
--data '{
"manifest": {
"/index.html": {
"hash": "08f1dfda4574284ab3c21666d1ee8c7d4",
"size": 1234
},
"/styles.css": {
"hash": "36b8be012ee77df5f269b11b975611d3",
"size": 5678
}
}
}'
```
#### Generating the hash
You can compute a SHA-256 digest of the file contents, then truncate or otherwise represent it consistently as a 32-hex-character string. Make sure to do it the same way each time so Cloudflare can reliably match files across uploads.
#### API Response
If all the files are already stored on Cloudflare, the response will only return the JWT token. If new or updated files are needed, the response will return:
- `jwt`: An upload token (valid for 1 hour) which will be used in the API request to upload the file contents (Step 2).
- `buckets`: An array of file-hash groups indicating which files to upload together. Files that have been recently uploaded won't appear in buckets, since Cloudflare already has them.
:::note
This step alone does not store files on Cloudflare. You must upload the actual file data in the next step.
:::
### 2. Upload File Contents
If the response to the Upload Session API returns `buckets`, that means you have new or changed files that need to be uploaded to Cloudflare.
Use the [Workers Assets Upload API](https://developers.cloudflare.com/api/resources/workers/subresources/assets/subresources/upload/) to transmit the raw file bytes in base64-encoded format for any missing or changed files. Once uploaded, Cloudflare will store these files so they can then be attached to a User Worker.
#### API Request Authentication
Unlike most Cloudflare API calls that use an account-wide API token in the Authorization header, uploading file contents requires using the short-lived JWT token returned in the `jwt` field of the `assets-upload-session` response.
Include it as a Bearer token in the header:
```bash
Authorization: Bearer
```
This token is valid for one hour and must be supplied for each upload request to the Workers Assets Upload API.
#### File fields (multipart/form-data)
You must send the files as multipart/form-data with base64-encoded content:
- Field name: The file hash (for example, `36b8be012ee77df5f269b11b975611d3`)
- Field value: A Base64-encoded string of the file's raw bytes
#### Example: Uploading multiple files within a single bucket
If your Upload Session response listed a single "bucket" containing two file hashes:
```json
"buckets": [
[
"08f1dfda4574284ab3c21666d1ee8c7d4",
"36b8be012ee77df5f269b11b975611d3"
]
]
```
You can upload both files in one request, each as a form-data field:
```bash
curl -X POST \
"https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/workers/assets/upload?base64=true" \
-H "Authorization: Bearer " \
-F "08f1dfda4574284ab3c21666d1ee8c7d4=" \
-F "36b8be012ee77df5f269b11b975611d3="
```
- `` is the token from step 1's assets-upload-session response
- `` is the Base64-encoded content of index.html
- `` is the Base64-encoded content of styles.css
If you have multiple buckets (for example, `[["hashA"], ["hashB"], ["hashC"]]`), you might need to repeat this process for each bucket, making one request per bucket group.
Once every file in the manifest has been uploaded, a status code of `201` will be returned, with the `jwt` field present. This JWT is a final "completion" token which can be used to create a deployment of a Worker with this set of assets. This completion token is valid for 1 hour.
```json
{
"success": true,
"errors": [],
"messages": [],
"result": {
"jwt": ""
}
}
```
`` indicates that Cloudflare has successfully received and stored the file contents specified by your manifest. You will use this `` in Step 3 to finalize the attachment of these files to the Worker.
### 3. Deploy the User Worker with static assets
Now that Cloudflare has all the files it needs (from the previous upload steps), you must attach them to the User Worker by making a PUT request to the [Upload User Worker API](https://developers.cloudflare.com/api/resources/workers_for_platforms/subresources/dispatch/subresources/namespaces/subresources/scripts/methods/update/). This final step links the static assets to the User Worker using the completion token you received after uploading file contents.
You can also specify any optional settings under the `assets.config` field to customize how your files are served (for example, to handle trailing slashes in HTML paths).
#### API request example
```bash
curl -X PUT \
"https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/workers/dispatch/namespaces/$NAMESPACE_NAME/scripts/$SCRIPT_NAME" \
-H "Content-Type: multipart/form-data" \
-H "Authorization: Bearer $API_TOKEN" \
-F 'metadata={
"main_module": "index.js",
"assets": {
"jwt": "",
"config": {
"html_handling": "auto-trailing-slash"
}
},
"compatibility_date": "2025-01-24"
};type=application/json' \
-F 'index.js=@/path/to/index.js;type=application/javascript'
```
- The `"jwt": ""` links the newly uploaded files to the Worker
- Including "html_handling" (or other fields under "config") is optional and can customize how static files are served
- If the user's Worker code has not changed, you can omit the code file or re-upload the same index.js
Once this PUT request succeeds, the files are served on the User Worker. Requests routed to that Worker will serve the new or updated static assets.
---
## Deploying static assets with Wrangler
If you prefer a CLI-based approach and your platform setup allows direct publishing, you can use Wrangler to deploy both your Worker code and static assets. Wrangler bundles and uploads static assets (from a specified directory) along with your Worker script, so you can manage everything in one place.
Create or update your [Wrangler configuration file](/workers/wrangler/configuration/) to specify where Wrangler should look for static files:
import { WranglerConfig } from "~/components";
```toml
name = "my-static-site"
main = "./src/index.js"
compatibility_date = "2025-01-29"
[assets]
directory = "./public"
binding = "ASSETS"
```
- `directory`: The local folder containing your static files (for example, `./public`).
- `binding`: The binding name used to reference these assets within your Worker code.
### 1. Organize your files
Place your static files (HTML, CSS, images, etc.) in the specified directory (in this example, `./public`). Wrangler will detect and bundle these files when you publish your Worker.
If you need to reference these files in your Worker script to serve them dynamically, you can use the `ASSETS` binding like this:
```js
export default {
async fetch(request, env, ctx) {
return env.ASSETS.fetch(request);
},
};
```
### 2. Deploy the User Worker with the static assets
Run Wrangler to publish both your Worker code and the static assets:
```bash
npx wrangler deploy --name --dispatch-namespace
```
Wrangler will automatically detect your static files, bundle them, and upload them to Cloudflare along with your Worker code.
---
# Tags
URL: https://developers.cloudflare.com/cloudflare-for-platforms/workers-for-platforms/configuration/tags/
To help you manage your customers’ Workers, use tags to better perform create, read, update, delete (CRUD) operations at scale. Tag user Worker scripts based on user ID, account ID, project ID, and environment. After you tag user Workers, when a user deletes their project, you will be able to delete all Workers associated with that project simultaneously.
```bash
curl --request PUT \
"https://api.cloudflare.com/client/v4/accounts/{account_id}/workers/dispatch/namespaces/{namespace_name}/scripts/{script_name}/tags" \
--header "Authorization: Bearer " \
--header "Content-Type: application/javascript" \
--data "['TAG1', 'TAG2', 'TAG3']"
```
:::note
You can set a maximum of eight tags per script. Avoid special characters like `,` and `&` when naming your tag.
:::
You can include script tags and bindings on multipart script uploads in the metadata blob.
```bash
curl --request PUT \
"https://api.cloudflare.com/client/v4/accounts/{account_id}/workers/dispatch/namespaces/{namespace_name}/scripts/{script_name}" \
--header "Authorization: Bearer " \
--header "Content-Type: multipart/form-data" \
--form 'metadata="{\"main_module\": \"worker.js\", \"bindings\": [{\"name\": \"KV\", \"type\": \"kv_namespace\", \"namespace_id\": \"\"}], \"tags\": [\"customer-123\", \"staging\", \"free-user\"]}"' \
--form 'worker.js=@"/path/to/worker.js";type=application/javascript+module'
```
### Tags API reference
| Method and endpoint | Description |
| ------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `GET https://api.cloudflare.com/client/v4/accounts/{account_id}/workers/dispatch/namespaces/{namespace_name}/scripts/{script_name}/tags` | Lists tags through a response body of a list of tag strings. |
| `GET https://api.cloudflare.com/client/v4/accounts/{account_id}/workers/dispatch/namespaces/{namespace_name}/scripts/{script_name}/tags?tags={filter}` | Returns true or false where `filter` is a comma separated pairs of tag names to a yes or no value (for example, `my-tag-value:yes`). |
| `GET https://api.cloudflare.com/client/v4/accounts/{account_id}/workers/dispatch/namespaces/{namespace_name}/scripts?tags={filter}` | Gets all Worker scripts that have tags that match the filter specified. The filter must be comma separated pairs of tag names to a yes or no value depending if the tag should act as an allowlist or blocklist. |
| `PUT https://api.cloudflare.com/client/v4/accounts/{account_id}/workers/dispatch/namespaces/{namespace_name}/scripts/{script_name}/tags` | Sets the tags associated with the worker to match the tags specified in the body. If there are tags already associated with the Worker script that are not in the request, they will be removed. |
| `PUT https://api.cloudflare.com/client/v4/accounts/{account_id}/workers/dispatch/namespaces/{namespace_name}/scripts/{script_name}/tags/{tag}` | Adds the single specified tag to the list of tags associated with the Worker script. |
| `DELETE https://api.cloudflare.com/client/v4/accounts/{account_id}/workers/dispatch/namespaces/{namespace_name}/scripts/{script_name}/tags/{tag}` | Deletes the single specified tag from the list of tags associated with the Worker script. |
| `DELETE https://api.cloudflare.com/client/v4/accounts/{account_id}/workers/dispatch/namespaces/{namespace_name}/scripts?tags={filter}` | Deletes all Worker scripts matching the filter. For example, `tags=testing:yes` would delete all scripts tagged with `testing`. |
---
# Local development
URL: https://developers.cloudflare.com/cloudflare-for-platforms/workers-for-platforms/get-started/developing-with-wrangler/
import { Render, PackageManagers, WranglerConfig } from "~/components";
To test your [Dispatch Worker](/cloudflare-for-platforms/workers-for-platforms/reference/how-workers-for-platforms-works/#dynamic-dispatch-worker), [user Worker](/cloudflare-for-platforms/workers-for-platforms/reference/how-workers-for-platforms-works/#user-workers) and [Outbound Worker](/cloudflare-for-platforms/workers-for-platforms/configuration/outbound-workers/) before deploying to production, you can use [Wrangler](/workers/wrangler) for development and testing.
:::note
Support for Workers for Platforms with `wrangler dev` in local mode is experimental and may change in the future. Use the prerelease branch: `wrangler@dispatch-namespaces-dev` to try Workers for Platforms locally.
:::
## 1. Create a user worker
Then, move into the newly created directory:
```sh
cd customer-worker-1
```
Update the `src/index.js` file for customer-worker-1:
```javascript
export default {
async fetch(request) {
// make a subrequest to the internet
const response = await fetch("https://example.com");
return new Response(
`user worker got "${await response.text()}" from fetch`,
);
},
};
```
Update the Wrangler file for customer-worker-1 and add the dispatch namespace:
```toml
# ... other content above ...
dispatch_namespace = "my-namespace"
```
## 2. Create a dispatch worker
Then, move into the newly created directory:
```sh
cd dispatch-worker
```
Update the `src/index.js` file for dispatch-worker:
```javascript
export default {
async fetch(request, env) {
// get the user Worker, specifying parameters that the Outbound Worker will see when it intercepts a user worker's subrequest
const customerScript = env.DISPATCH_NAMESPACE.get(
"customer-worker-1",
{},
{
outbound: {
paramCustomerName: "customer-1",
},
},
);
// invoke user Worker
return await customerScript.fetch(request);
},
};
```
Update the Wrangler file for dispatch-worker and add the dispatch namespace binding:
```toml
# ... other content above ...
[[dispatch_namespaces]]
binding = "DISPATCH_NAMESPACE"
namespace = "my-namespace"
outbound = { service = "outbound-worker", parameters = ["paramCustomerName"] }
```
## 3. Create an Outbound Worker
Then, move into the newly created directory:
```sh
cd outbound-worker
```
Update the `src/index.js` file for outbound-worker:
```javascript
export default {
async fetch(request, env) {
const { paramCustomerName } = env;
// use the parameters passed by the dispatcher to know what this user this request is for
// and return custom content back to the user worker
return new Response(
`intercepted a request for ${paramCustomerName} by the outbound`,
);
},
};
```
## 4. Start local dev session for your Workers
In separate terminals, start a local dev session for each of your Workers.
For your dispatcher Worker:
```sh
cd dispatch-worker
npx wrangler@dispatch-namespaces-dev dev --port 8600
```
For your outbound Worker:
```sh
cd outbound-worker
npx wrangler@dispatch-namespaces-dev dev --port 8601
```
And for your user Worker:
```sh
cd customer-worker-1
npx wrangler@dispatch-namespaces-dev dev --port 8602
```
## 5. Test your requests
Send a request to your dispatcher Worker:
```sh
curl http://localhost:8600
```
```sh output
# -> user worker got "intercepted a request for customer-1 by the outbound" from fetch
```
---
# Create a dynamic dispatch Worker
URL: https://developers.cloudflare.com/cloudflare-for-platforms/workers-for-platforms/get-started/dynamic-dispatch/
After you have created a dispatch namespace, you can fetch any user Workers in the namespace using a dynamic dispatch Worker. The dynamic dispatch Worker has a namespace binding.
Use any method of routing to a namespaced Worker (reading the subdomain, request header, or lookup in a database). Ultimately you need the name of the user Worker.
In the following example, routing to a user Worker is done through reading the subdomain `.example.com/*`. For example, `my-customer.example.com` will run the script uploaded to `PUT accounts//workers/dispatch/namespaces/my-dispatch-namespace/scripts/my-customer`.
```js
export default {
async fetch(request, env) {
try {
// parse the URL, read the subdomain
let workerName = new URL(request.url).host.split('.')[0];
let userWorker = env.dispatcher.get(workerName);
return await userWorker.fetch(request);
} catch (e) {
if (e.message.startsWith('Worker not found')) {
// we tried to get a worker that doesn't exist in our dispatch namespace
return new Response('', { status: 404 });
}
// this could be any other exception from `fetch()` *or* an exception
// thrown by the called worker (e.g. if the dispatched worker has
// `throw MyException()`, you could check for that here).
return new Response(e.message, { status: 500 });
}
},
};
```
---
# Get started
URL: https://developers.cloudflare.com/cloudflare-for-platforms/workers-for-platforms/get-started/
import { DirectoryListing } from "~/components"
---
# Uploading User Workers
URL: https://developers.cloudflare.com/cloudflare-for-platforms/workers-for-platforms/get-started/user-workers/
[User Workers](/cloudflare-for-platforms/workers-for-platforms/reference/how-workers-for-platforms-works/#user-workers) contain code written by your end users (end developers).
## Upload User Workers
You can upload user Workers to a namespace via Wrangler or the Cloudflare API. Workers uploaded to a namespace will not appear on the **Workers & Pages** section of the Cloudflare dashboard. Instead, they will appear in a namespace under the [Workers for Platforms](https://dash.cloudflare.com/?to=/:account/workers-for-platforms) tab.
To run Workers uploaded to a namespace, you will need to first create a [dispatch Worker](/cloudflare-for-platforms/workers-for-platforms/reference/how-workers-for-platforms-works/#dynamic-dispatch-worker)
with a [dispatch namespace binding](/workers/wrangler/configuration/#dispatch-namespace-bindings-workers-for-platforms).
### Upload user Workers via Wrangler
Uploading user Workers is supported through [wrangler](/workers/wrangler/) by running the following command:
```sh
npx wrangler deploy --dispatch-namespace
```
For simplicity, start with wrangler when [getting started](/cloudflare-for-platforms/workers-for-platforms/get-started/configuration/).
### Upload user Workers via the API
Since you will be deploying Workers on behalf of your users, you will likely want to use the [Workers for Platforms script upload APIs](/api/resources/workers_for_platforms/subresources/dispatch/subresources/namespaces/subresources/scripts/subresources/content/methods/update/) directly instead of Wrangler to have more control over the upload process. The Workers for Platforms script upload API is the same as the [Worker upload API](/api/resources/workers/subresources/scripts/methods/update/), but it will upload the Worker to a [dispatch namespace](/cloudflare-for-platforms/workers-for-platforms/reference/how-workers-for-platforms-works/#dispatch-namespace) instead of to your account directly.
## Bindings
You can use any Workers [bindings](/workers/runtime-apis/bindings/) with the dynamic dispatch Worker or any user Workers.
Bindings for your user Workers can be defined on [multipart script uploads](/api/resources/workers_for_platforms/subresources/dispatch/subresources/namespaces/subresources/scripts/subresources/content/methods/update/) in the [metadata](/workers/configuration/multipart-upload-metadata/) part.
---
# Changelog
URL: https://developers.cloudflare.com/cloudflare-for-platforms/workers-for-platforms/platform/changelog/
import { ProductReleaseNotes } from "~/components";
Workers for Platforms users might also be interested in [the Workers changelog](/workers/platform/changelog/) which has detailed changes to the Workers runtime and the various configuration options available to your dispatch and user Workers.
{/* */}
---
# Platform
URL: https://developers.cloudflare.com/cloudflare-for-platforms/workers-for-platforms/platform/
import { DirectoryListing } from "~/components"
---
# Limits
URL: https://developers.cloudflare.com/cloudflare-for-platforms/workers-for-platforms/platform/limits/
import { Render } from "~/components"
## Script limits
Cloudflare provides an unlimited number of scripts for Workers for Platforms customers.
## `cf` object
The [`cf` object](/workers/runtime-apis/request/#the-cf-property-requestinitcfproperties) contains Cloudflare-specific properties of a request. This field is not accessible in [user Workers](/cloudflare-for-platforms/workers-for-platforms/reference/how-workers-for-platforms-works/#user-workers) because some fields in this object are sensitive and can be used to manipulate Cloudflare features (eg.`cacheKey`, `resolveOverride`, `scrapeShield`.)
## Durable Object namespace limits
Workers for Platforms do not have a limit for the number of Durable Object namespaces.
## Cache API
For isolation, `caches.default` is disabled for namespaced scripts. To learn more about the cache, refer to [How the cache Works](/workers/reference/how-the-cache-works/).
## Tags
You can set a maximum of eight tags per script. Avoid special characters like `,` and `&` when naming your tag.
## Gradual Deployments
[Gradual Deployments](/workers/configuration/versions-and-deployments/gradual-deployments/) is not supported yet for user Workers. Changes made to user Workers create a new version that deployed all-at-once to 100% of traffic.
## API Rate Limits
---
# Pricing
URL: https://developers.cloudflare.com/cloudflare-for-platforms/workers-for-platforms/platform/pricing/
The Workers for Platforms Paid plan is **$25 monthly**. Workers for Platforms can be purchased through the [Cloudflare dashboard](https://dash.cloudflare.com/?to=/:account/workers-for-platforms).
Workers for Platforms comes with the following usage allotments and overage pricing.
| | Requests12 | Duration | CPU time2 | Scripts |
| - | --------------------------------------------------------------------------------- | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------ |
| | 20 million requests included per month
+$0.30 per additional million | No charge or limit for duration | 60 million CPU milliseconds included per month
+$0.02 per additional million CPU milliseconds
Max of 30 seconds of CPU time per invocation Max of 15 minutes of CPU time per [Cron Trigger](/workers/configuration/cron-triggers/) or [Queue Consumer](/queues/configuration/javascript-apis/#consumer) invocation | 1000 scripts
+$0.02 per additional script |
1 Inbound requests to your Worker. Cloudflare does not bill for [subrequests](/workers/platform/limits/#subrequests) you make from your Worker. 2 Workers for Platforms only charges for 1 request across the chain of [dispatch Worker](/cloudflare-for-platforms/workers-for-platforms/reference/how-workers-for-platforms-works/#dynamic-dispatch-worker) -> [user Worker](/cloudflare-for-platforms/workers-for-platforms/reference/how-workers-for-platforms-works/#user-workers) -> [outbound Worker](/cloudflare-for-platforms/workers-for-platforms/configuration/outbound-workers/). CPU time is charged across these Workers.
## Example pricing:
A Workers for Platforms project that serves 100 million requests per month, uses an average of 10 milliseconds (ms) of CPU time per request and uses 1200 scripts would have the following estimated costs:
| | Monthly Costs | Formula |
| ---------------- | ------------- | ----------------------------------------------------------------------------------------------------------- |
| **Subscription** | $25.00 | |
| **Requests** | $24.00 | (100,000,000 requests - 20,000,000 included requests) / 1,000,000 \* $0.30 |
| **CPU time** | $18.80 | ((10 ms of CPU time per request \* 100,000,000 requests) - 60,000,000 included CPU ms) / 1,000,000 \* $0.02 |
| **Scripts** | $4.00 | 1200 scripts - 1000 included scripts \* $0.02 |
| **Total** | $71.80 | |
:::note[Custom limits]
Set [custom limits](/cloudflare-for-platforms/workers-for-platforms/configuration/custom-limits/) for user Workers to get control over your Cloudflare bill, prevent accidental runaway bills or denial-of-wallet attacks. Configure the maximum amount of CPU time that can be used per invocation by [defining custom limits in your dispatch Worker](/cloudflare-for-platforms/workers-for-platforms/configuration/custom-limits/#set-custom-limits).
:::
---
# How Workers for Platforms works
URL: https://developers.cloudflare.com/cloudflare-for-platforms/workers-for-platforms/reference/how-workers-for-platforms-works/
Workers for Platforms is built on top of [Cloudflare Workers](/workers/). The same [security and performance models used by Workers](/workers/reference/security-model/) apply to applications that use Workers for Platforms.
The Workers configuration API was initially built around managing a relatively small number of Workers on each account. This leads to some difficulties when using Workers as a platform for your own users, including:
* Frequently needing to increase script limits.
* Adding an ever-increasing number of routes.
* Managing logic in a central place if your own logic is supposed to come before your customers' logic.
Workers for Platforms extends the capabilities of Workers for SaaS businesses that want to deploy Worker scripts on behalf of their customers or that want to let their users write Worker scripts directly.
## Architecture
Workers for Platforms introduces a new architecture model as outlined on this page.
### Dispatch namespace
A dispatch namespace is composed of a collection of user Workers. With dispatch namespaces, a dynamic dispatch Worker can be used to call any user Worker in a namespace.
:::note[Best practice]
Having a production and staging namespace is useful to test changes that you have made to your dynamic dispatch Worker.
If you have multiple distinct services you are providing your customers, you should split these out into different dispatch workers and namespaces. We discourage creating a new namespace for each customer.
:::
### Dynamic dispatch Worker
A dynamic dispatch Worker is written by Cloudflare’s platform customers to run their own logic before dispatching (routing) the request to user Workers. In addition to routing, it can be used to run authentication, create boilerplate functions and sanitize responses.
The dynamic dispatch Worker calls user Workers from the dispatch namespace and executes them. The dynamic dispatch Worker is configured with a [dispatch namespace binding](/cloudflare-for-platforms/workers-for-platforms/get-started/configuration/#4-create-a-dynamic-dispatch-worker). The binding is the entrypoint for all requests to user Workers.
### User Workers
User Workers are written by your end users (end developers). End developers deploy user Workers to script automated actions, create integrations or modify response payloads to return custom content.
### Request lifecycle
Below you will find an example request lifecycle in the Workers for Platforms architecture.

In the above diagram:
1. Request for `customer-a.example.com/api` will first hit the dynamic dispatch Worker (`api-prod`).
2. The dispatcher (`env.dispatcher.get(customer-a)`) configured in your dynamic dispatch Worker code will handle routing logic to user Workers.
3. The subdomain (`customer-a.example.com`) of the incoming request is used to route to the user Worker with the same name (`customer-a`).
## Workers for Platforms versus Service bindings
Both Workers for Platforms and Service bindings enable Worker-to-Worker communication.
Service bindings explicitly link two Workers together. They are meant for use cases where you know exactly which Workers need to communicate with each other. Service bindings do not work in the Workers for Platforms model because user Workers are uploaded as needed by your end users.
In the Workers for Platforms model, a dynamic dispatch Worker can be used to call any user Worker (similar to how Service bindings work) in a dispatch namespace but without needing to explicitly pre-define the relationship.
Service bindings and Workers for Platforms can be used simultaneously when building applications.
## [Cache API](/workers/runtime-apis/cache/)
Workers for Platforms user Workers have access to namespaced cache through the [cache API](/workers/runtime-apis/cache/). Namespaced cache is isolated across user Workers. For isolation, `caches.default` is disabled for namespaced scripts.
To learn more about the cache, refer to [How the cache Works](/workers/reference/how-the-cache-works/).
---
# Reference
URL: https://developers.cloudflare.com/cloudflare-for-platforms/workers-for-platforms/reference/
import { DirectoryListing } from "~/components"
---
# Argo Smart Routing for SaaS
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/performance/argo-for-saas/
Argo Smart Routing uses real-time global network information to route traffic on the fastest possible path across the Internet. Regardless of geographic location, this allows Cloudflare to optimize routing to make it faster, more reliable, and more secure. As a SaaS provider, you may want to emphasize the quickest traffic delivery for your end customers. To do so, [enable Argo Smart Routing](/argo-smart-routing/get-started/).
---
# Cache for SaaS
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/performance/cache-for-saas/
Cloudflare makes customer websites faster by storing a copy of the website’s content on the servers of our globally distributed data centers. Content can be either static or dynamic: static content is “cacheable” or eligible for caching, and dynamic content is “uncacheable” or ineligible for caching. The cached copies of content are stored physically closer to users, optimized to be fast, and do not require recomputing.
As a SaaS provider, enabling caching reduces latency on your custom domains. For more information, refer to [Cache](/cache/). If you would like to enable caching, review [Getting Started with Cache](/cache/get-started/).
---
# Early Hints for SaaS
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/performance/early-hints-for-saas/
[Early Hints](/cache/advanced-configuration/early-hints/) allows the browser to begin loading resources while the origin server is compiling the full response. This improves webpage’s loading speed for the end user. As a SaaS provider, you may prioritize speed for some of your custom hostnames. Using custom metadata, you can [enable Early Hints](/cache/advanced-configuration/early-hints/#enable-early-hints) per custom hostname.
***
## Prerequisites
Before you can employ Early Hints for SaaS, you need to create a custom hostname. Review [Get Started with Cloudflare for SaaS](/cloudflare-for-platforms/cloudflare-for-saas/start/getting-started/) if you have not already done so.
***
## Enable Early Hints per custom hostname via the API
1. [Locate your zone ID](/fundamentals/setup/find-account-and-zone-ids/), available in the Cloudflare dashboard.
2. Locate your Authentication Key by selecting **My Profile** > **API tokens** > **Global API Key**.
3. If you are [creating a new custom hostname](/api/resources/custom_hostnames/methods/create/), make an API call such as the example below, specifying `"early_hints": "on"`:
```bash
curl "https://api.cloudflare.com/client/v4/zones/{zone_id}/custom_hostnames" \
--header "X-Auth-Email: " \
--header "X-Auth-Key: " \
--header "Content-Type: application/json" \
--data '{
"hostname": "{hostname}",
"ssl": {
"method": "http",
"type": "dv",
"settings": {
"http2": "on",
"min_tls_version": "1.2",
"tls_1_3": "on",
"early_hints": "on"
},
"bundle_method": "ubiquitous",
"wildcard": false
}
}'
```
4. For an existing custom hostname, locate the `id` of that hostname via a `GET` call:
```bash
curl "https://api.cloudflare.com/client/v4/zones/{zone_id}/custom_hostnames?hostname={hostname}" \
--header "X-Auth-Email: " \
--header "X-Auth-Key: "
```
5. Then make an API call such as the example below, specifying `"early_hints": "on"`:
```bash
curl --request PATCH \
"https://api.cloudflare.com/client/v4/zones/{zone_id}/custom_hostnames/{id}" \
--header "X-Auth-Email: " \
--header "X-Auth-Key: " \
--header "Content-Type: application/json" \
--data '{
"ssl": {
"method": "http",
"type": "dv",
"settings": {
"http2": "on", // Note: These settings will be set to default if not included when updating early hints
"min_tls_version": "1.2",
"tls_1_3": "on",
"early_hints": "on"
}
}
}'
```
Currently, all options within `settings` are required in order to prevent those options from being set to default. You can pull the current settings state prior to updating Early Hints by leveraging the output that returns the `id` for the hostname.
---
# Performance
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/performance/
Cloudflare for SaaS allows you to deliver the best performance to your end customers by helping enable you to reduce latency through:
* [Argo Smart Routing for SaaS](/cloudflare-for-platforms/cloudflare-for-saas/performance/argo-for-saas/) calculates and optimizes the fastest path for requests to travel to your origin.
* [Early Hints for SaaS](/cloudflare-for-platforms/cloudflare-for-saas/performance/early-hints-for-saas/) provides faster loading speeds for individual custom hostnames by allowing the browser to begin loading responses while the origin server is compiling the full response.
* [Cache for SaaS](/cloudflare-for-platforms/cloudflare-for-saas/performance/cache-for-saas/) makes customer websites faster by storing a copy of the website’s content on the servers of our globally distributed data centers.
* By using Cloudflare for SaaS, your customers automatically inherit the benefits of Cloudflare's vast [anycast network](https://www.cloudflare.com/network/).
---
# Backoff schedule
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/domain-support/hostname-validation/backoff-schedule/
After you create a custom hostname, Cloudflare has to [validate that hostname](/cloudflare-for-platforms/cloudflare-for-saas/domain-support/hostname-validation/).
Attempts to validate a Custom Hostname are distributed over 7 days (a total of 75 retries). At the end of this schedule, if the validation is unsuccessful, the custom hostname will be deleted. The function that determines the next check varies based on the number of attempts:
* For the first 10 attempts:
```txt
now() + min((floor(60 * pow(1.05, retry_attempt)) * INTERVAL '1 second'), INTERVAL '4 hours')
```
* For the remaining 65 attempts:
```txt
now() + min((floor(60 * pow(1.15, retry_attempt)) * INTERVAL '1 second'), INTERVAL '4 hours')
```
The first 10 checks complete within 2 minutes and most checks complete in the first 4 hours. The check back off is capped to a maximum of 4 hours to avoid exponential growth. The back off behavior causes larger gaps between check intervals towards the end of the back off schedule:
| Retry Attempt | In Seconds | In Minutes | In Hours |
| ------------- | ---------- | ---------- | -------- |
| 0 | 60 | 1 | 0.016667 |
| 1 | 63 | 1.05 | 0.0175 |
| 2 | 66 | 1.1 | 0.018333 |
| 3 | 69 | 1.15 | 0.019167 |
| 4 | 72 | 1.2 | 0.02 |
| 5 | 76 | 1.266667 | 0.021111 |
| 6 | 80 | 1.333333 | 0.022222 |
| 7 | 84 | 1.4 | 0.023333 |
| 8 | 88 | 1.466667 | 0.024444 |
| 9 | 93 | 1.55 | 0.025833 |
| 10 | 242 | 4.033333 | 0.067222 |
| 11 | 279 | 4.65 | 0.0775 |
| 12 | 321 | 5.35 | 0.089167 |
| 13 | 369 | 6.15 | 0.1025 |
| 14 | 424 | 7.066667 | 0.117778 |
| 15 | 488 | 8.133333 | 0.135556 |
| 16 | 561 | 9.35 | 0.155833 |
| 17 | 645 | 10.75 | 0.179167 |
| 18 | 742 | 12.366667 | 0.206111 |
| 19 | 853 | 14.216667 | 0.236944 |
| 20 | 981 | 16.35 | 0.2725 |
| 21 | 1129 | 18.816667 | 0.313611 |
| 22 | 1298 | 21.633333 | 0.360556 |
| 23 | 1493 | 24.883333 | 0.414722 |
| 24 | 1717 | 28.616667 | 0.476944 |
| 25 | 1975 | 32.916667 | 0.548611 |
| 26 | 2271 | 37.85 | 0.630833 |
| 27 | 2612 | 43.533333 | 0.725556 |
| 28 | 3003 | 50.05 | 0.834167 |
| 29 | 3454 | 57.566667 | 0.959444 |
| 30 | 3972 | 66.2 | 1.103333 |
| 31 | 4568 | 76.133333 | 1.268889 |
| 32 | 5253 | 87.55 | 1.459167 |
| 33 | 6041 | 100.683333 | 1.678056 |
| 34 | 6948 | 115.8 | 1.93 |
| 35 | 7990 | 133.166667 | 2.219444 |
| 36 | 9189 | 153.15 | 2.5525 |
| 37 | 10567 | 176.116667 | 2.935278 |
| 38 | 12152 | 202.533333 | 3.375556 |
| 39 | 13975 | 232.916667 | 3.881944 |
| 40 | 14400 | 240 | 4 |
| 41 | 14400 | 240 | 4 |
| 42 | 14400 | 240 | 4 |
| 43 | 14400 | 240 | 4 |
| 44 | 14400 | 240 | 4 |
| 45 | 14400 | 240 | 4 |
| 46 | 14400 | 240 | 4 |
| 47 | 14400 | 240 | 4 |
| 48 | 14400 | 240 | 4 |
| 49 | 14400 | 240 | 4 |
| 50 | 14400 | 240 | 4 |
| 51 | 14400 | 240 | 4 |
| 52 | 14400 | 240 | 4 |
| 53 | 14400 | 240 | 4 |
| 54 | 14400 | 240 | 4 |
| 55 | 14400 | 240 | 4 |
| 56 | 14400 | 240 | 4 |
| 57 | 14400 | 240 | 4 |
| 58 | 14400 | 240 | 4 |
| 59 | 14400 | 240 | 4 |
| 60 | 14400 | 240 | 4 |
| 61 | 14400 | 240 | 4 |
| 62 | 14400 | 240 | 4 |
| 63 | 14400 | 240 | 4 |
| 64 | 14400 | 240 | 4 |
| 65 | 14400 | 240 | 4 |
| 66 | 14400 | 240 | 4 |
| 67 | 14400 | 240 | 4 |
| 68 | 14400 | 240 | 4 |
| 69 | 14400 | 240 | 4 |
| 70 | 14400 | 240 | 4 |
| 71 | 14400 | 240 | 4 |
| 72 | 14400 | 240 | 4 |
| 73 | 14400 | 240 | 4 |
| 74 | 14400 | 240 | 4 |
| 75 | 14400 | 240 | 4 |
---
# Error codes
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/domain-support/hostname-validation/error-codes/
When you [validate a custom hostname](/cloudflare-for-platforms/cloudflare-for-saas/domain-support/hostname-validation/), you might encounter the following error codes.
| Error | Cause |
| --------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Zone does not have a fallback origin set. | Fallback is not active. |
| Fallback origin is in a status of `initializing`, `pending_deployment`, `pending_deletion`, or `deleted`. | Fallback is not active. |
| Custom hostname does not `CNAME` to this zone. | Zone does not have [apex proxying entitlement](/cloudflare-for-platforms/cloudflare-for-saas/start/advanced-settings/apex-proxying/) and custom hostname does not CNAME to zone. |
| None of the `A` or `AAAA` records are owned by this account and the pre-generated ownership validation token was not found. | Account has [apex proxying enabled](/cloudflare-for-platforms/cloudflare-for-saas/start/advanced-settings/apex-proxying/) but the custom hostname failed the hostname validation check on the `A` record. |
| This account and the pre-generated ownership validation token was not found. | Hostname does not `CNAME` to zone or none of the `A`/`AAAA` records match reserved IPs for zone. |
---
# Hostname validation
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/domain-support/hostname-validation/
import { DirectoryListing } from "~/components";
Before Cloudflare can proxy traffic through a custom hostname, we need to verify your customer's ownership of that hostname.
:::note
If a custom hostname is already on Cloudflare, using the [pre-validation methods](/cloudflare-for-platforms/cloudflare-for-saas/domain-support/hostname-validation/pre-validation/) will not shift the traffic to the SaaS zone. That will only happen once the [DNS target](/cloudflare-for-platforms/cloudflare-for-saas/start/getting-started/#3-have-customer-create-cname-record) of the custom hostnames changes to point to the SaaS zone.
:::
## Options
If minimizing downtime is more important to you, refer to our [pre-validation methods](/cloudflare-for-platforms/cloudflare-for-saas/domain-support/hostname-validation/pre-validation/).
If ease of use for your customers is more important, review our [real-time validation methods](/cloudflare-for-platforms/cloudflare-for-saas/domain-support/hostname-validation/realtime-validation/).
## Limitations
Custom hostnames using another CDN are not compatible with Cloudflare for SaaS. Since Cloudflare must be able to validate your customer's ownership of the hostname you add, if their usage of another CDN obfuscates their DNS records, hostname validation will fail.
## Related resources
---
# Real-time validation
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/domain-support/hostname-validation/realtime-validation/
import { Render } from "~/components"
When you use a real-time validation method, Cloudflare verifies your customer's hostname when your customers adds their [DNS routing record](/cloudflare-for-platforms/cloudflare-for-saas/start/getting-started/#3-have-customer-create-cname-record) to their authoritative DNS.
## Use when
Real-time validation methods put less burden on your customers because it does not require any additional actions.
However, it may cause some downtime since Cloudflare takes a few seconds to iterate over DNS records. This downtime also can increase - due to the increasing [validation backoff schedule](/cloudflare-for-platforms/cloudflare-for-saas/domain-support/hostname-validation/backoff-schedule/) - if your customer takes additional time to add their DNS routing record.
To minimize this downtime, you can continually send no-change [`PATCH` requests](/api/resources/custom_hostnames/methods/edit/) for the specific custom hostname until it validates (which resets the validation backoff schedule).
To avoid any chance of downtime, use a [pre-validation method](/cloudflare-for-platforms/cloudflare-for-saas/domain-support/hostname-validation/pre-validation/)
## How to
Real-time validation occurs automatically when your customer adds their [DNS routing record](/cloudflare-for-platforms/cloudflare-for-saas/start/getting-started/#3-have-customer-create-cname-record).
The exact record depends on your Cloudflare for SaaS setup.
### Normal setup (CNAME target)
### Apex proxying
---
# Pre-validation
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/domain-support/hostname-validation/pre-validation/
Pre-validation methods help verify domain ownership before your customer's traffic is proxying through Cloudflare.
## Use when
Use pre-validation methods when your customers cannot tolerate any downtime, which often occurs with production domains.
The downside is that these methods require an additional setup step for your customers. Especially if you already need them to add something to their domain for [certificate validation](/cloudflare-for-platforms/cloudflare-for-saas/security/certificate-management/issue-and-validate/validate-certificates/), pre-validation might make their onboarding more complicated.
If your customers can tolerate a bit of downtime and you want their setup to be simpler, review our [real-time validation methods](/cloudflare-for-platforms/cloudflare-for-saas/domain-support/hostname-validation/realtime-validation/).
## How to
### TXT records
TXT validation is when your customer adds a `TXT` record to their authoritative DNS to verify domain ownership.
:::note
If your customer cannot update their authoritative DNS, you could also use [HTTP validation](#http-tokens).
:::
To set up `TXT` validation:
1. When you [create a custom hostname](/api/resources/custom_hostnames/methods/create/), save the `ownership_verification` information.
```json null {11-12}
{
"result": [
{
"id": "3537a672-e4d8-4d89-aab9-26cb622918a1",
"hostname": "app.example.com",
// ...
"status": "pending",
"verification_errors": ["custom hostname does not CNAME to this zone."],
"ownership_verification": {
"type": "txt",
"name": "_cf-custom-hostname.app.example.com",
"value": "0e2d5a7f-1548-4f27-8c05-b577cb14f4ec"
},
"created_at": "2020-03-04T19:04:02.705068Z"
}
]
}
```
2. Have your customer add a `TXT` record with that `name` and `value` at their authoritative DNS provider.
3. After a few minutes, you will see the hostname status become **Active** in the UI.
4. Once you activate the custom hostname, your customer can remove the `TXT` record.
### HTTP tokens
HTTP validation is when you or your customer places an HTTP token on their origin server to verify domain ownership.
To set up HTTP validation:
When you [create a custom hostname](/cloudflare-for-platforms/cloudflare-for-saas/security/certificate-management/issue-and-validate/issue-certificates/) using the API, Cloudflare provides an HTTP `ownership_verification` record in the response.
To get and use the `ownership_verification` record:
1. Make an API call to [create a Custom Hostname](/api/resources/custom_hostnames/methods/create/).
2. In the response, copy the `http_url` and `http_body` from the `ownership_verification_http` object:
```json title="Example response (truncated)" {8-9}
{
"result": [
{
"id": "24c8c68e-bec2-49b6-868e-f06373780630",
"hostname": "app.example.com",
// ...
"ownership_verification_http": {
"http_url": "http://app.example.com/.well-known/cf-custom-hostname-challenge/24c8c68e-bec2-49b6-868e-f06373780630",
"http_body": "48b409f6-c886-406b-8cbc-0fbf59983555"
},
"created_at": "2020-03-04T20:06:04.117122Z"
}
]
}
```
3. Have your customer place the `http_url` and `http_body` on their origin web server.
```txt title="Example response (truncated)"
location "/.well-known/cf-custom-hostname-challenge/24c8c68e-bec2-49b6-868e-f06373780630" {
return 200 "48b409f6-c886-406b-8cbc-0fbf59983555\n";
}
```
Cloudflare will access this token by sending `GET` requests to the `http_url` using `User-Agent: Cloudflare Custom Hostname Verification`.
:::note
If you can serve these tokens on behalf of your customers, you can simplify their overall setup.
:::
4. After a few minutes, you will see the hostname status become **Active** in the UI.
5. Once the hostname is active, your customer can remove the token from their origin server.
---
# Validation status
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/domain-support/hostname-validation/validation-status/
When you [validate a custom hostname](/cloudflare-for-platforms/cloudflare-for-saas/domain-support/hostname-validation/), that hostname can be in several different statuses.
| Status | Description |
| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Pending | Custom hostname is pending hostname validation. |
| Active | Custom hostname has completed hostname validation and is active. |
| Active re-deploying | Customer hostname is active and the changes have been processed. |
| Blocked | Custom hostname cannot be added to Cloudflare at this time. Custom hostname was likely associated with Cloudflare previously and flagged for abuse.
If you are an Enterprise customer, contact your Customer Success Manager. Otherwise, email `abusereply@cloudflare.com` with the name of the web property and a detailed explanation of your association with this web property. |
| Moved | Custom hostname is not active after **Pending** for the entirety of the [Validation Backoff Schedule](/cloudflare-for-platforms/cloudflare-for-saas/domain-support/hostname-validation/backoff-schedule/) or it no longer points to the fallback origin. |
| Deleted | Custom hostname was deleted from the zone. Occurs when status is **Moved** for more than seven days. |
## Refresh validation
To run the custom hostname validation check again, select **Refresh** on the dashboard or send a `PATCH` request to the [Edit custom hostname endpoint](/api/resources/custom_hostnames/methods/edit/). If using the API, make sure that the `--data` field contains an `ssl` object with the same `method` and `type` as the original request.
If the hostname is in a **Moved** or **Deleted** state, the refresh will set the custom hostname back to **Pending validation**.
---
# Custom CSRs
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/reference/status-codes/custom-csrs/
## Success codes
| Endpoint | Method | HTTP Status Code |
| --------------------------------------------------- | ------ | ---------------- |
| `/api/v4/zones/:zone_id/custom_csrs` | POST | 201 Created |
| `/api/v4/zones/:zone_id/custom_csrs` | GET | 200 OK |
| `/api/v4/zones/:zone_id/custom_csrs/:custom_csr_id` | GET | 200 OK |
| `/api/v4/zones/:zone_id/custom_csrs/:custom_csr_id` | DELETE | 200 OK |
## Error codes
| HTTP Status Code | API Error Code | Error Message |
| ---------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| 400 | 1400 | Unable to decode the JSON request body. Check your input and try again. |
| 400 | 1401 | Zone ID is required. Check your input and try again. |
| 400 | 1402 | The request has no Authorization header. Check your input and try again. |
| 400 | 1405 | Country field is required. Check your input and try again. |
| 400 | 1406 | State field is required. Check your input and try again. |
| 400 | 1407 | Locality field is required. Check your input and try again. |
| 400 | 1408 | Organization field is required. Check your input and try again. |
| 400 | 1409 | Common Name field is required. Check your input and try again. |
| 400 | 1410 | The specified Common Name is too long. Maximum allowed length is %d characters. Check your input and try again. |
| 400 | 1411 | At least one subject alternative name (SAN) is required. Check your input and try again. |
| 400 | 1412 | Invalid subject alternative name(s) (SAN). SANs have to be smaller than 256 characters in length, cannot be IP addresses, cannot contain any special characters such as ~`!@#$%^&*()=+{}[] |
| 400 | 1413 | Subject Alternative Names (SANs) with non-ASCII characters are not supported. Check your input and try again. |
| 400 | 1414 | Reserved top domain subject alternative names (SAN), such as 'test', 'example', 'invalid' or 'localhost', is not supported. Check your input and try again. |
| 400 | 1415 | Unable to parse subject alternative name(s) (SAN) - :reason. Check your input and try again. Reasons: publicsuffix: cannot derive eTLD+1 for domain %q; publicsuffix: invalid public suffix %q for domain %q; |
| 400 | 1416 | Subject Alternative Names (SANs) ending in example.com, example.net, or example.org are prohibited. Check your input and try again. |
| 400 | 1417 | Invalid key type. Only 'rsa2048' or 'p256v1' is accepted. Check your input and try again. |
| 400 | 1418 | The custom CSR ID is invalid. Check your input and try again. |
| 401 | 1000 | Unable to extract bearer token |
| 401 | 1001 | Unable to parse JWT token |
| 401 | 1002 | Bad JWT header |
| 401 | 1003 | Failed to verify JWT token |
| 401 | 1004 | Failed to get claims from JWT token |
| 401 | 1005 | JWT token does not have required claims |
| 403 | 1403 | No quota has been allocated for this zone. If you are already a paid Cloudflare for SaaS customer, contact your Customer Success Manager for additional provisioning. If you are not yet enrolled, [fill out this contact form](https://www.cloudflare.com/plans/enterprise/contact/) and our sales team will contact you. |
| 403 | 1404 | Access to generating CSRs has not been granted for this zone. If you are already a paid Cloudflare for SaaS customer, contact your Customer Success Manager for additional provisioning. If you are not yet enrolled, [fill out this contact form](https://www.cloudflare.com/plans/enterprise/contact/) and our sales team will contact you. |
| 404 | 1419 | The custom CSR was not found. |
| 409 | 1420 | The custom CSR is associated with an active certificate pack. You will need to delete all associated active certificate packs before you can delete the custom CSR. |
| 500 | 1500 | Internal Server Error |
---
# Custom hostnames
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/reference/status-codes/custom-hostnames/
***
## Success codes
| Endpoint | Method | Code |
| --------------------------------------------------------- | ------ | ------------ |
| `/v4/zones/:zone_id/custom_hostnames` | POST | 201 Created |
| `/v4/zones/:zone_id/custom_hostnames/:custom_hostname_id` | GET | 200 OK |
| `/v4/zones/:zone_id/custom_hostnames` | GET | 200 OK |
| `/v4/zones/:zone_id/custom_hostnames/:custom_hostname_id` | DELETE | 200 OK |
| `/v4/zones/:zone_id/custom_hostnames/:custom_hostname_id` | PATCH | 202 Accepted |
***
## Error codes
| HTTP Status Code | API Error Code | Error Message |
| ---------------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 400 | 1400 | Unable to decode the JSON request body. Check your input and try again. |
| 400 | 1401 | Unable to encode the Custom Metadata as JSON. Check your input and try again. |
| 400 | 1402 | Zone ID is required. Check your input and try again. |
| 400 | 1403 | The request has no Authorization header. Check your input and try again. |
| 400 | 1407 | Invalid custom hostname. Custom hostnames have to be smaller than 256 characters in length, cannot be IP addresses, cannot contain any special characters such as \`\`\~\`!@#$%^&\*()=+{}\[]\\ |
| 400 | 1408 | Custom hostnames with non-ASCII characters are not supported. Check your input and try again. |
| 400 | 1409 | Reserved top domain custom hostnames, such as 'test', 'example', 'invalid' or 'localhost', is not supported. Check your input and try again. |
| 400 | 1410 | Unable to parse custom hostname - `:reason`. Check your input and try again. **Reasons:** publicsuffix: cannot derive eTLD+1 for domain `:domain` publicsuffix: invalid public suffix `:suffix` for domain `:domain` |
| 400 | 1411 | Custom hostnames ending in example.com, example.net, or example.org are prohibited. Check your input and try again. |
| 400 | 1412 | Custom metadata for wildcard custom hostnames is not supported. Check your input and try again. |
| 400 | 1415 | Invalid custom origin hostname. Custom origin hostnames have to be smaller than 256 characters in length, cannot be IP addresses, cannot contain any special characters such as ~~\`\`~~\`!@#$%^&\*()=+{}\[]\\ |
| 400 | 1416 | Custom origin hostnames with non-ASCII characters are not supported. Check your input and try again. |
| 400 | 1417 | Reserved top domain custom origin hostnames, such as 'test', 'example', 'invalid' or 'localhost', is not supported. Check your input and try again. |
| 400 | 1418 | Unable to parse custom origin hostname - `:reason`. Check your input and try again. **Reasons:** publicsuffix: cannot derive eTLD+1 for domain `:domain` publicsuffix: invalid public suffix`:suffix`for domain`:domain` |
| 400 | 1419 | Custom origin hostnames ending in example.com, example.net, or example.org are prohibited. Check your input and try again. |
| 400 | 1420 | Wildcard custom origin hostnames are not supported. Check your input and try again. |
| 400 | 1421 | The custom origin hostname you specified does not exist on Cloudflare as a DNS record (A, AAAA or CNAME) in your zone:`:zone\_tag`. Check your input and try again. |
| 400 | 1422 | Invalid `http2`setting. Only 'on' or 'off' is accepted. Check your input and try again. |
| 400 | 1423 | Invalid`tls\_1\_2\_only`setting. Only 'on' or 'off' is accepted. Check your input and try again. |
| 400 | 1424 | Invalid`tls\_1\_3`setting. Only 'on' or 'off' is accepted. Check your input and try again. |
| 400 | 1425 | Invalid`min\_tls\_version`setting. Only '1.0','1.1','1.2' or '1.3' is accepted. Check your input and try again. |
| 400 | 1426 | The certificate that you uploaded cannot be parsed. Check your input and try again. |
| 400 | 1427 | The certificate that you uploaded is empty. Check your input and try again. |
| 400 | 1428 | The private key you uploaded cannot be parsed. Check your input and try again. |
| 400 | 1429 | The private key you uploaded does not match the certificate. Check your input and try again. |
| 400 | 1430 | The custom CSR ID is invalid. Check your input and try again. |
| 404 | 1431 | The custom CSR was not found. |
| 400 | 1432 | The validation method is not supported. Only`http`, `email`, or `txt` are accepted. Check your input and try again. |
| 400 | 1433 | The validation type is not supported. Only 'dv' is accepted. Check your input and try again. |
| 400 | 1434 | The SSL attribute is invalid. Refer to the API documentation, check your input and try again. |
| 400 | 1435 | The custom hostname ID is invalid. Check your input and try again. |
| 404 | 1436 | The custom hostname was not found. |
| 400 | 1437 | Invalid hostname.contain query parameter. The hostname.contain query parameter has to be smaller than 256 characters in length, cannot be IP addresses, cannot contain any special characters such as \`\`\~\`!@#$%^&\*()=+{}\[]\\ |
| 400 | 1438 | Cannot specify other filter parameters in addition to `id`. Only one must be specified. Check your input and try again. |
| 409 | 1439 | Modifying the custom hostname is not supported. Check your input and try again. |
| 400 | 1440 | Both validation type and validation method are required. Check your input and try again. |
| 400 | 1441 | The certificate that you uploaded is having trouble bundling against the public trust store. Check your input and try again. |
| 400 | 1442 | Invalid `ciphers` setting. Refer to the documentation for the list of accepted cipher suites. Check your input and try again. |
| 400 | 1443 | Cipher suite selection is not supported for a minimum TLS version of 1.3. Check your input and try again. |
| 400 | 1444 | The certificate chain that you uploaded has multiple leaf certificates. Check your input and try again. |
| 400 | 1445 | The certificate chain that you uploaded has no leaf certificates. Check your input and try again. |
| 400 | 1446 | The certificate that you uploaded does not include the custom hostname - `:custom_hostname`. Review your input and try again. |
| 400 | 1447 | The certificate that you uploaded does not use a supported signature algorithm. Only SHA-256/ECDSA, SHA-256/RSA, and SHA-1/RSA signature algorithms are supported. Review your input and try again. |
| 400 | 1448 | Custom hostnames with wildcards are not supported for certificates managed by Cloudflare. Review your input and try again. |
| 400 | 1449 | The request input `bundle_method` must be one of: ubiquitous, optimal, force. |
| 401 | 1000 | Unable to extract bearer token |
| 401 | 1001 | Unable to parse JWT token |
| 401 | 1002 | Bad JWT header |
| 401 | 1003 | Failed to verify JWT token |
| 401 | 1004 | Failed to get claims from JWT token |
| 401 | 1005 | JWT token does not have required claims |
| 403 | 1404 | No quota has been allocated for this zone. If you are already a paid Cloudflare for SaaS customer, contact your Customer Success Manager for additional provisioning. If you are not yet enrolled, [fill out this contact form](https://www.cloudflare.com/plans/enterprise/contact/) and our sales team will reach out to you. |
| 403 | 1405 | Quota exceeded. If you are already a paid Cloudflare for SaaS customer, contact your Customer Success Manager for additional provisioning. If you are not yet enrolled, [fill out this contact form](https://www.cloudflare.com/plans/enterprise/contact/) and our sales team will reach out to you. |
| 403 | 1413 | No [custom metadata](/cloudflare-for-platforms/cloudflare-for-saas/domain-support/custom-metadata/) access has been allocated for this zone. If you are already a paid customer, contact your Customer Success Manager for additional provisioning. If you are not yet enrolled, [fill out this contact form](https://www.cloudflare.com/plans/enterprise/contact/) and our sales team will reach out to you. |
| 403 | 1414 | Access to setting a custom origin server has not been granted for this zone. If you are already a paid Cloudflare for SaaS customer, contact your Customer Success Manager for additional provisioning. If you are not yet enrolled, [fill out this contact form](https://www.cloudflare.com/plans/enterprise/contact/) and our sales team will reach out to you. |
| 409 | 1406 | Duplicate custom hostname found. |
| 500 | 1500 | Internal Server Error |
---
# Status codes
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/reference/status-codes/
import { DirectoryListing } from "~/components";
Cloudflare uses many different status codes for Cloudflare for SaaS. They can be related to:
---
# BigCommerce
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/saas-customers/provider-guides/bigcommerce/
import { Render } from "~/components"
## Benefits
## How it works
For more details about how O2O is different than other Cloudflare setups, refer to [How O2O works](/cloudflare-for-platforms/cloudflare-for-saas/saas-customers/how-it-works/).
## Enable
BigCommerce customers can enable O2O on any Cloudflare zone plan.
To enable O2O on your account, [create](/dns/manage-dns-records/how-to/create-dns-records/#create-dns-records) a `CNAME` DNS record.
| Type | Name | Target | Proxy status |
| ------- | ----------------- | ------------------------- | ------------ |
| `CNAME` | `` | `shops.mybigcommerce.com` | Proxied |
:::note
For more details about a BigCommerce setup, refer to their [support guide](https://support.bigcommerce.com/s/article/Cloudflare-for-Performance-and-Security?language=en_US#orange-to-orange).
If you cannot activate your domain using [proxied DNS records](/dns/proxy-status/), reach out to your account team.
:::
## Product compatibility
## Additional support
---
# HubSpot
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/saas-customers/provider-guides/hubspot/
import { Render } from "~/components"
## Benefits
## How it works
For more details about how O2O is different than other Cloudflare setups, refer to [How O2O works](/cloudflare-for-platforms/cloudflare-for-saas/saas-customers/how-it-works/).
## Enable
O2O is enabled per hostname, so to enable O2O for a specific hostname within your Cloudflare zone, [create](/dns/manage-dns-records/how-to/create-dns-records/#create-dns-records) a Proxied `CNAME` DNS record with a target of your corresponding HubSpot CNAME. Which HubSpot CNAME is targeted will depend on your current [HubSpot proxy settings](https://developers.hubspot.com/docs/cms/developer-reference/reverse-proxy-support#configure-the-proxy).
| Type | Name | Target | Proxy status |
| ------- | ----------------- | -------------------------------------- | ------------ |
| `CNAME` | `` | `.sites-proxy.hscoscdn<##>.net` | Proxied |
:::note
For questions about your HubSpot setup, refer to [HubSpot's reverse proxy support guide](https://developers.hubspot.com/docs/cms/developer-reference/reverse-proxy-support).
:::
## Product compatibility
## Zone hold
Because you have your own Cloudflare zone, you have access to the zone hold feature, which is a toggle that prevents your domain name from being created as a zone in a different Cloudflare account. Additionally, if the zone hold feature is enabled, it prevents the activation of custom hostnames onboarded to HubSpot. HubSpot would receive the following error message for your custom hostname: `The hostname is associated with a held zone. Please contact the owner of this domain to have the hold removed.`
To successfully activate the custom hostname on HubSpot, the owner of the zone needs to [temporarily release the hold](/fundamentals/setup/account/account-security/zone-holds/#release-zone-holds). If you are only onboarding a subdomain as a custom hostname to HubSpot, only the subfeature titled `Also prevent Subdomains` needs to be temporarily disabled.
Once the zone hold is temporarily disabled, follow HubSpot's instructions to refresh the custom hostname and it should activate.
## Additional support
---
# Kinsta
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/saas-customers/provider-guides/kinsta/
import { Render } from "~/components"
## Benefits
## How it works
For additional detail about how traffic routes when O2O is enabled, refer to [How O2O works](/cloudflare-for-platforms/cloudflare-for-saas/saas-customers/how-it-works/).
## Enable
Kinsta customers can enable O2O on any Cloudflare zone plan.
To enable O2O for a specific hostname within a Cloudflare zone, [create](/dns/manage-dns-records/how-to/create-dns-records/#create-dns-records) a Proxied `CNAME` DNS record with your Kinsta site name as the target. Kinsta’s domain addition setup will walk you through other validation steps.
| Type | Name | Target | Proxy status |
| ------- | ----------------- | ------------------------------- | ------------ |
| `CNAME` | `` | `sitename.hosting.kinsta.cloud` | Proxied |
## Product compatibility
## Additional support
### Resolving SSL errors using Cloudflare Managed Certificates
If you encounter SSL errors when attempting to activate a Cloudflare Managed Certificate, verify if you have a `CAA` record on your domain name with command `dig +short example.com CAA`.
If you do have a `CAA` record, verify that it permits SSL certificates to be issued by the [certificate authorities supported by Cloudflare](/ssl/reference/certificate-authorities/).
---
# Provider guides
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/saas-customers/provider-guides/
import { DirectoryListing } from "~/components";
---
# Shopify
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/saas-customers/provider-guides/shopify/
import { Render } from "~/components"
## Benefits
O2O routing also enables you to take advantage of Cloudflare zones specifically customized for Shopify traffic.
## How it works
For more details about how O2O is different than other Cloudflare setups, refer to [How O2O works](/cloudflare-for-platforms/cloudflare-for-saas/saas-customers/how-it-works/).
## Enable
You can enable O2O on any Cloudflare zone plan.
To enable O2O on your account, [create](/dns/manage-dns-records/how-to/create-dns-records/#create-dns-records) a `CNAME` DNS record.
| Type | Name | Target | Proxy status |
| ------- | -------------------- | --------------------- | ------------ |
| `CNAME` | `` | `shops.myshopify.com` | Proxied |
:::note
For questions about Shopify setup, refer to their [support guide](https://help.shopify.com/en/manual/domains/add-a-domain/connecting-domains/connect-domain-manual).
If you cannot activate your domain using [proxied DNS records](/dns/proxy-status/), reach out to your account team or the [Cloudflare Community](https://community.cloudflare.com).
:::
## Product compatibility
## Additional support
### DNS CAA records
Shopify issues SSL/TLS certificates for merchant domains using Let’s Encrypt. If you add any DNS CAA records, you must select Let’s Encrypt as the Certificate Authority (CA) or HTTPS connections may fail.
For more details, refer to [CAA records](/ssl/edge-certificates/caa-records/#caa-records-added-by-cloudflare).
---
# Salesforce Commerce Cloud
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/saas-customers/provider-guides/salesforce-commerce-cloud/
import { Details, Render } from "~/components";
## Benefits
## How it works
For additional detail about how traffic routes when O2O is enabled, refer to [How O2O works](/cloudflare-for-platforms/cloudflare-for-saas/saas-customers/how-it-works/).
## Enable
To enable O2O requires the following:
1. You must configure your SFCC environment as an "SFCC Proxy Zone". If you currently have an "SFCC Legacy Zone", you cannot enable O2O.
- For more details on the different types of SFCC configurations, refer to the [Salesforce FAQ on SFCC Proxy Zones](https://help.salesforce.com/s/articleView?id=cc.b2c_ecdn_proxy_zone_faq.htm&type=5).
- For instructions on how to migrate your SFCC environment to an "SFCC Proxy Zone", refer to the [SFCC Legacy Zone to SFCC Proxy Zone migration guide](https://help.salesforce.com/s/articleView?id=cc.b2c_migrate_legacy_zone_to_proxy_zone.htm&type=5).
2. Your own Cloudflare zone on an Enterprise plan.
If you meet the above requirements, O2O can then be enabled per hostname. To enable O2O for a specific hostname within your Cloudflare zone, [create](/dns/manage-dns-records/how-to/create-dns-records/#create-dns-records) a Proxied CNAME DNS record with a target of the CNAME provided by SFCC Business Manager, which is the dashboard used by SFCC customers to configure their storefront environment.
The CNAME provided by SFCC Business Manager will resemble `commcloud.prod-abcd-example-com.cc-ecdn.net` and contains 3 distinct parts. For each hostname routing traffic to SFCC, be sure to update each part of the example CNAME to match your SFCC environment:
1. **Environment**: `prod` should be changed to `prod` or `dev` or `stg`.
2. **Realm**: `abcd` should be changed to the Realm ID assigned to you by SFCC.
3. **Domain Name**: `example-com` should be changed to match your domain name in a hyphenated format.
| Type | Name | Target | Proxy status |
| ------- | ----------------- | --------------------------------------------- | ------------ |
| `CNAME` | `` | `commcloud.prod-abcd-example-com.cc-ecdn.net` | Proxied |
For O2O to be configured properly, make sure your Proxied DNS record targets your SFCC CNAME **directly**. Do not indirectly target the SFCC CNAME by targeting another Proxied DNS record in your Cloudflare zone which targets the SFCC CNAME.
For example, if the hostnames routing traffic to SFCC are `www.example.com` and `preview.example.com`, the following is a **correct** configuration in your Cloudflare zone:
| Type | Name | Target | Proxy status |
| ------- | --------------------- | --------------------------------------------- | ------------ |
| `CNAME` | `www.example.com` | `commcloud.prod-abcd-example-com.cc-ecdn.net` | Proxied |
| `CNAME` | `preview.example.com` | `commcloud.prod-abcd-example-com.cc-ecdn.net` | Proxied |
And, the following is an **incorrect** configuration because `preview.example.com` indirectly targets the SFCC CNAME via the `www.example.com` Proxied DNS record, which means O2O will not be properly enabled for hostname `preview.example.com`:
| Type | Name | Target | Proxy status |
| ------- | --------------------- | --------------------------------------------- | ------------ |
| `CNAME` | `www.example.com` | `commcloud.prod-abcd-example-com.cc-ecdn.net` | Proxied |
| `CNAME` | `preview.example.com` | `www.example.com` | Proxied |
## Product compatibility
## Additional support
### Resolving SSL errors using Cloudflare Managed Certificates
If you encounter SSL errors when attempting to activate a Cloudflare Managed Certificate, verify if you have a `CAA` record on your domain name with command `dig +short example.com CAA`.
If you do have a `CAA` record, verify that it permits SSL certificates to be issued by the [certificate authorities supported by Cloudflare](/ssl/reference/certificate-authorities/).
### Best practice Zone-level configuration
1. Set **Minimum TLS version** to **TLS 1.2**
1. Navigate to **SSL/TLS > Edge Certificates**, scroll down the page to find **Minimum TLS Version**, and set it to _TLS 1.2_. This setting applies to every Proxied DNS record in your Zone.
2. Match the **Security Level** set in **SFCC Business Manager**
1. _Option 1: Zone-level_ - Navigate to **Security > Settings**, find **Security Level** and set **Security Level** to match what is configured in **SFCC Business Manager**. This setting applies to every Proxied DNS record in your Cloudflare zone.
2. _Option 2: Per Proxied DNS record_ - If the **Security Level** differs between the Proxied DNS records targeting your SFCC environment and other Proxied DNS records in your Cloudflare zone, use a **Configuration Rule** to set the **Security Level** specifically for the Proxied DNS records targeting your SFCC environment. For example:
1. Create a new **Configuration Rule** by navigating to **Rules** > **Overview** and selecting **Create rule** next to **Configuration Rules**:
1. **Rule name:** `Match Security Level on SFCC hostnames`
2. **Field:** _Hostname_
3. **Operator:** _is in_ (this will match against multiple hostnames specified in the **Value** field)
4. **Value:** `www.example.com` `dev.example.com`
5. Scroll down to **Security Level** and click **+ Add**
1. **Select Security Level:** _Medium_ (this should match the **Security Level** set in **SFCC Business Manager**)
6. Scroll to the bottom of the page and click **Deploy**
3. Disable **Browser Integrity Check**
1. _Option 1: Zone-level_ - Navigate to **Security > Settings**, find **Browser Integrity Check** and toggle it off to disable it. This setting applies to every Proxied DNS record in your Cloudflare zone.
2. _Option 2: Per Proxied DNS record_ - If you want to keep **Browser Integrity Check** enabled for other Proxied DNS records in your Cloudflare zone but want to disable it on Proxied DNS records targeting your SFCC environment, keep the Zone-level **Browser Integrity Check** feature enabled and use a **Configuration Rule** to disable **Browser Integrity Check** specifically for the hostnames targeting your SFCC environment. For example:
1. Create a new **Configuration Rule** by navigating to **Rules** > **Overview** and selecting **Create rule** next to **Configuration Rules**:
1. **Rule name:** `Disable Browser Integrity Check on SFCC hostnames`
2. **Field:** _Hostname_
3. **Operator:** _is in_ (this will match against multiple hostnames specified in the **Value** field)
4. **Value:** `www.example.com` `dev.example.com`
5. Scroll down to **Browser Integrity Check** and click the **+ Add** button:
1. Set the toggle to **Off** (a grey X will be displayed)
6. Scroll to the bottom of the page and click **Deploy**
4. Bypass **Cache** on Proxied DNS records targeting your SFCC environment
1. Your SFCC environment, also called a **Realm**, will contain one to many SFCC Proxy Zones, which is where caching will always occur. In the corresponding SFCC Proxy Zone for your domain, SFCC performs their own cache optimization, so it is recommended to bypass the cache on the Proxied DNS records in your Cloudflare zone which target your SFCC environment to prevent a "double caching" scenario. This can be accomplished with a **Cache Rule**.
2. If the **Cache Rule** is not created, caching will occur in both your Cloudflare zone and your corresponding SFCC Proxy Zone, which can cause issues if and when the cache is invalidated or purged in your SFCC environment.
1. Additional information on caching in your SFCC environment can be found in [SFCC's Content Cache Documentation](https://developer.salesforce.com/docs/commerce/b2c-commerce/guide/b2c-content-cache.html)
3. Create a new **Cache Rule** by navigating to **Rules** > **Overview** and selecting **Create rule** next to **Cache Rules**:
1. **Rule name:** `Bypass cache on SFCC hostnames`
2. **Field:** _Hostname_
3. **Operator:** _is in_ (this will match against multiple hostnames specified in the **Value** field)
4. **Value:** `www.example.com` `dev.example.com`
5. **Cache eligibility:** Select **Bypass cache**.
6. Scroll to the bottom of the page and select **Deploy**.
5. _Optional_ - Upload your Custom Certificate from **SFCC Business Manager** to your Cloudflare zone:
1. The Custom Certificate you uploaded via **SFCC Business Manager** or **SFCC CDN-API**, which exists within your corresponding SFCC Proxy Zone, will terminate TLS connections for your SFCC storefront hostnames. Because of that, it is optional if you want to upload the same Custom Certificate to your own Cloudflare zone. Doing so will allow Cloudflare users with specific roles in your Cloudflare account to receive expiration notifications for your Custom Certificates. Please read [renew custom certificates](/ssl/edge-certificates/custom-certificates/renewing/#renew-custom-certificates) for further details.
2. Additionally, since you now have your own Cloudflare zone, you have access to Cloudflare's various edge certificate products which means you could have more than one certificate covering the same SANs. In that scenario, a certificate priority process occurs to determine which certificate to serve at the Cloudflare edge. If you find your SFCC storefront hostnames are presenting a different certificate compared to what you uploaded via **SFCC Business Manager** or **SFCC CDN-API**, the certificate priority process is likely the reason. Please read [certificate priority](/ssl/reference/certificate-and-hostname-priority/#certificate-deployment) for further details.
---
# WP Engine
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/saas-customers/provider-guides/wpengine/
import { Render } from "~/components"
## Benefits
## How it works
For more details about how O2O is different than other Cloudflare setups, refer to [How O2O works](/cloudflare-for-platforms/cloudflare-for-saas/saas-customers/how-it-works/).
## Enable
WP Engine customers can enable O2O on any Cloudflare zone plan.
To enable O2O for a specific hostname within a Cloudflare zone, [create](/dns/manage-dns-records/how-to/create-dns-records/#create-dns-records) a Proxied `CNAME` DNS record with a target of one of the following WP Engine CNAMEs. Which WP Engine CNAME is used will depend on your current [WP Engine network type](https://wpengine.com/support/network/).
| Type | Name | Target | Proxy status |
| ------- | ----------------- | --------------------------------------------------------------------------------------------- | ------------ |
| `CNAME` | `` | `wp.wpewaf.com` (Global Edge Security) or `wp.wpenginepowered.com` (Advanced Network) | Proxied |
:::note
For questions about WP Engine setup, refer to their [support guide](https://wpengine.com/support/wordpress-best-practice-configuring-dns-for-wp-engine/#Point_DNS_Using_CNAME_Flattening).
If you cannot activate your domain using [proxied DNS records](/dns/proxy-status/), reach out to your account team.
:::
## Product compatibility
## Additional support
### Resolving SSL errors
If you encounter SSL errors, check if you have a `CAA` record.
If you do have a `CAA` record, check that it permits SSL certificates to be issued by `letsencrypt.org`.
For more details, refer to [CAA records](/ssl/edge-certificates/troubleshooting/caa-records/#what-caa-records-are-added-by-cloudflare).
---
# TLS Management
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/security/certificate-management/enforce-mtls/
import { AvailableNotifications, Details, Render } from "~/components"
[Mutual TLS (mTLS)](https://www.cloudflare.com/learning/access-management/what-is-mutual-tls/) adds an extra layer of protection to application connections by validating certificates on the server and the client. When building a SaaS application, you may want to enforce mTLS to protect sensitive endpoints related to payment processing, database updates, and more.
[Minimum TLS Version](/ssl/edge-certificates/additional-options/minimum-tls/) allows you to choose a cryptographic standard per custom hostname. Cloudflare recommends TLS 1.2 to comply with the Payment Card Industry (PCI) Security Standards Council.
[Cipher suites](/ssl/edge-certificates/additional-options/cipher-suites/) are a combination of ciphers used to negotiate security settings during the [SSL/TLS handshake](https://www.cloudflare.com/learning/ssl/what-happens-in-a-tls-handshake/). As a SaaS provider, you can [specify configurations for cipher suites](#cipher-suites) on your zone as a whole and cipher suites on individual custom hostnames via the API.
:::caution
When you [issue a custom hostname certificate](/cloudflare-for-platforms/cloudflare-for-saas/security/certificate-management/issue-and-validate/) with wildcards enabled, any cipher suites or Minimum TLS settings applied to that hostname will only apply to the direct hostname.
However, if you want to update the Minimum TLS settings for all wildcard hostnames, you can change Minimum TLS version at the [zone level](/ssl/edge-certificates/additional-options/minimum-tls/).
:::
## Enable mTLS
Once you have [added a custom hostname](/cloudflare-for-platforms/cloudflare-for-saas/start/getting-started/), you can enable mTLS by using Cloudflare Access. Go to [Cloudflare Zero Trust](https://one.dash.cloudflare.com/) and [add mTLS authentication](/cloudflare-one/identity/devices/access-integrations/mutual-tls-authentication/) with a few clicks.
:::note
Currently, you cannot add mTLS policies for custom hostnames using [API Shield](/api-shield/security/mtls/).
:::
## Enable Minimum TLS Version
1. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com) and navigate to your account and website.
2. Select **SSL/TLS** > **Custom Hostnames**.
3. Find the hostname to which you want to apply Minimum TLS Version. Select **Edit**.
4. Choose the desired TLS version under **Minimum TLS Version** and click **Save**.
:::note
While TLS 1.3 is the most recent and secure version, it is not supported by some older devices. Refer to Cloudflare's recommendations when [deciding what version to use](/ssl/reference/protocols/#decide-which-version-to-use).
:::
## Cipher suites
For security and regulatory reasons, you may want to only allow connections from certain cipher suites. Cloudflare provides recommended values and full cipher suite reference in our [Cipher suites documentation](/ssl/edge-certificates/additional-options/cipher-suites/).
Refer to [Edit zone setting](/api/resources/zones/subresources/settings/methods/edit/) and use `ciphers` as the setting name in the URI path.
Refer to [SSL properties of a custom hostname](/api/resources/custom_hostnames/methods/edit/).
## Alerts for mutual TLS certificates
You can configure alerts to receive notifications before your mutual TLS certificates expire.
---
# Certificate management
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/security/certificate-management/
import { DirectoryListing } from "~/components";
Cloudflare for SaaS takes away the burden of certificate issuance and management from you, as the SaaS provider, by proxying traffic through Cloudflare's edge. You can choose between Cloudflare managing all the certificate issuance and renewals on your behalf, or maintain control over your TLS private keys by uploading your customers' own certificates.
## Resources
---
# WAF for SaaS
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/security/waf-for-saas/
[Web Application Firewall (WAF)](/waf/) allows you to create additional security measures through Cloudflare. As a SaaS provider, you can link custom rules, rate limiting rules, and managed rules to your custom hostnames. This provides more control to keep your domains safe from malicious traffic.
As a SaaS provider, you may want to apply different security measures to different custom hostnames. With WAF for SaaS, you can create multiple WAF configuration that you can apply to different sets of custom hostnames. This added flexibility and security leads to optimal protection across the domains of your end customers.
---
## Prerequisites
Before you can use WAF for SaaS, you need to create a custom hostname. Review [Get started with Cloudflare for SaaS](/cloudflare-for-platforms/cloudflare-for-saas/start/getting-started/) if you have not already done so.
You can also create a custom hostname through the API:
```bash
curl "https://api.cloudflare.com/client/v4/zones/{zone_id}/custom_hostnames" \
--header "X-Auth-Email: " \
--header "X-Auth-Key: " \
--header "Content-Type: application/json" \
--data '{"Hostname":"example.com"}, "Ssl":{wildcard:false}}'
```
## 1. Associate custom metadata to a custom hostname
To apply WAF to your custom hostname, you need to create an association between your customer's domain and the WAF configuration that you would like to attach to it. Cloudflare's product, [custom metadata](/cloudflare-for-platforms/cloudflare-for-saas/domain-support/custom-metadata/) allows you to do this via the API.
1. [Locate your zone ID](/fundamentals/setup/find-account-and-zone-ids/), available in the Cloudflare dashboard.
2. Locate your Authentication Key by selecting **My Profile** > **API tokens** > **Global API Key**.
3. Locate your custom hostname ID by making a `GET` call in the API:
```bash
curl "https://api.cloudflare.com/client/v4/zones/{zone_id}/custom_hostnames" \
--header "X-Auth-Email: " \
--header "X-Auth-Key: "
```
4. Plan your [custom metadata](/cloudflare-for-platforms/cloudflare-for-saas/domain-support/custom-metadata/). It is fully customizable. In the example below, we have chosen the tag `"security_level"` to which we expect to assign three values (low, medium, and high).
:::note
One instance of low, medium, and high rules could be rate limiting. You can specify three different thresholds: low - 100 requests/minute, medium - 85 requests/minute, high - 50 requests/minute, for example. Another possibility is a WAF custom rule in which low challenges requests and high blocks them.
:::
5. Make an API call in the format below using your Cloudflare email and the IDs gathered above:
```bash
curl --request PATCH \
"https://api.cloudflare.com/client/v4/zones/{zone_id}/custom_hostnames/{custom_hostname_id}" \
--header "X-Auth-Email: "
--header "X-Auth-Key: " \
--header "Content-Type: application/json" \
--data '{
"custom_metadata": {
"customer_id": "12345",
"security_level": "low"
}
}'
```
This assigns custom metadata to your custom hostname so that it has a security tag associated with its ID.
## 2. Trigger security products based on tags
1. Locate the custom metadata field in the Ruleset Engine where the WAF runs. This can be used to trigger different configurations of products such as [WAF custom rules](/waf/custom-rules/), [rate limiting rules](/waf/rate-limiting-rules/), and [Transform Rules](/rules/transform/).
2. Build your rules either [through the dashboard](/waf/custom-rules/create-dashboard/) or via the API. An example rate limiting rule, corresponding to `"security_level"` low, is shown below as an API call.
```bash
curl --request PUT \
"https://api.cloudflare.com/client/v4/zones/{zone_id}/rulesets/phases/http_ratelimit/entrypoint" \
--header "Authorization: Bearer " \
--header "Content-Type: application/json" \
--data '{
"rules": [
{
"action": "block",
"ratelimit": {
"characteristics": [
"cf.colo.id",
"ip.src"
],
"period": 10,
"requests_per_period": 2,
"mitigation_timeout": 60
},
"expression": "lookup_json_string(cf.hostname.metadata, \"security_level\") eq \"low\" and http.request.uri contains \"login\""
}
]
}'
```
To build rules through the dashboard:
1. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com) and navigate to your account and website.
2. Select **Security** > **WAF**.
3. Follow the instructions on the dashboard specific to custom rules, rate limiting rules, or managed rules, depending on your security goal.
4. Once the rule is active, you should see it under the applicable tab (custom rules, rate limiting, or managed rules).

---
# Webhook definitions
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/security/certificate-management/webhook-definitions/
import { AvailableNotifications, Render } from "~/components"
When you [create a webhook notification](/notifications/get-started/configure-webhooks/) for **SSL for SaaS Custom Hostnames**, you may want to automate responses to specific events (certificate issuance, failed validation, etc.).
The following section details the data Cloudflare sends to a webhook destination.
## Certificate validation
Before a Certificate Authority will issue a certificate for a domain, the requester must prove they have control over that domain. This process is known as [domain control validation (DCV)](/cloudflare-for-platforms/cloudflare-for-saas/security/certificate-management/issue-and-validate/validate-certificates/).
### Validation succeeded
Cloudflare sends this alert when certificates move from a status of `pending_validation` to `pending_issuance`.
```json
{
"metadata": {
"event": {
"id": "<",
"type": "ssl.custom_hostname_certificate.validation.succeeded",
"created_at": "2022-02-09T00:03:28.385080Z"
},
"account": {
"id": "<"
},
"zone": {
"id": "<"
}
},
"data": {
"id": "<",
"hostname": "blog.com",
"ssl": {
"id": "<",
"type": "dv",
"method": "cname",
"status": "pending_issuance",
"settings": {
"min_tls_version": "1.2",
"http2": "on"
}
},
"custom_metadata": {
"key1": "value1",
"key2": "value2"
},
"custom_origin_server": "0001.blog.com"
}
}
```
### Validation failed
Cloudflare sends this alert each time a certificate remains in a `pending_validation` status during [DCV retries](/ssl/edge-certificates/changing-dcv-method/validation-backoff-schedule/).
```json
{
"metadata": {
"event": {
"id": "<",
"type": "ssl.custom_hostname_certificate.validation.failed",
"created_at": "2018-02-09T00:03:28.385080Z"
},
"account": {
"id": "<"
},
"zone": {
"id": "<"
}
},
"data": {
"id": "<",
"hostname": "blog.com",
"ssl": {
"id": "<",
"type": "dv",
"method": "cname",
"status": "pending_validation",
"cname": "_ca3-64ce913ebfe74edeb2e8813e3928e359.app.example2.com",
"cname_target": "dcv.digicert.com",
"validation_errors": [
{
"message": "blog.example.com reported as potential risk: google_safe_browsing"
}
],
"settings": {
"min_tls_version": "1.2",
"http2": "on"
}
},
"custom_metadata": {
"key1": "value1",
"key2": "value2"
},
"custom_origin_server": "0001.blog.com"
}
}
```
***
## Certificate issuance
Once validated, certificates are issued by Cloudflare in conjunction with your chosen [certificate authority](/ssl/reference/certificate-authorities/).
### Issuance succeeded
Cloudflare sends this alert when certificates move from a status of `pending_validation` or `pending_issuance` to `pending_deployment`.
```json
{
"metadata": {
"event": {
"id": "<",
"type": "ssl.custom_hostname_certificate.issuance.succeeded",
"created_at": "2022-02-09T00:03:28.385080Z"
},
"account": {
"id": "<"
},
"zone": {
"id": "<"
}
},
"data": {
"id": "<",
"hostname": "blog.com",
"ssl": {
"id": "<",
"type": "dv",
"method": "cname",
"status": "pending_deployment",
"settings": {
"min_tls_version": "1.2",
"http2": "on"
}
},
"custom_metadata": {
"key1": "value1",
"key2": "value2"
},
"custom_origin_server": "0001.blog.com"
}
}
```
### Issuance failed
Cloudflare sends this alert each time a certificate remains in a status of `pending_issuance` during [DCV retries](/ssl/edge-certificates/changing-dcv-method/validation-backoff-schedule/).
```json
{
"metadata": {
"event": {
"id": "<",
"type": "ssl.custom_hostname_certificate.issuance.failed",
"created_at": "2022-02-09T00:03:28.385080Z"
},
"account": {
"id": "<"
},
"zone": {
"id": "<"
}
},
"data": {
"id": "<",
"hostname": "blog.com",
"ssl": {
"id": "<",
"type": "dv",
"method": "cname",
"status": "pending_issuance",
"cname": "_ca3-64ce913ebfe74edeb2e8813e3928e359.app.example2.com",
"cname_target": "dcv.digicert.com",
"validation_errors": [
{
"message": "caa_error: blog.example.com"
}
],
"settings": {
"min_tls_version": "1.2",
"http2": "on"
}
},
"custom_metadata": {
"key1": "value1",
"key2": "value2"
},
"custom_origin_server": "0001.blog.com"
}
}
```
***
## Certificate deployment
Once issued, certificates are deployed to Cloudflare's global edge network.
### Deployment succeeded
Cloudflare sends this alert when certificates move from a status of `pending_deployment` to `active`.
```json
{
"metadata": {
"event": {
"id": "<",
"type": "ssl.custom_hostname_certificate.deployment.succeeded",
"created_at": "2022-02-09T00:03:28.385080Z"
},
"account": {
"id": "<"
},
"zone": {
"id": "<"
}
},
"data": {
"id": "<",
"hostname": "blog.com",
"ssl": {
"id": "<",
"type": "dv",
"method": "cname",
"status": "active",
"settings": {
"min_tls_version": "1.2",
"http2": "on"
}
},
"custom_metadata": {
"key1": "value1",
"key2": "value2"
},
"custom_origin_server": "0001.blog.com"
}
}
```
### Deployment failed
Cloudflare sends this alert each time a certificate remains in a status of `pending_deployment` during [DCV retries](/ssl/edge-certificates/changing-dcv-method/validation-backoff-schedule/).
```json
{
"metadata": {
"event": {
"id": "<",
"type": "ssl.custom_hostname_certificate.deployment.failed",
"created_at": "2022-02-09T00:03:28.385080Z"
},
"account": {
"id": "<"
},
"zone": {
"id": "<"
}
},
"data": {
"id": "<",
"hostname": "blog.com",
"ssl": {
"id": "<",
"type": "dv",
"method": "cname",
"status": "pending_deployment",
"settings": {
"min_tls_version": "1.2",
"http2": "on"
}
},
"custom_metadata": {
"key1": "value1",
"key2": "value2"
},
"custom_origin_server": "0001.blog.com"
}
}
```
***
## Certificate deletion
### Deletion succeeded
Cloudflare sends this alert when certificates move from a status of `pending_deletion` to `deleted`.
```json
{
"metadata": {
"event": {
"id": "<",
"type": "ssl.custom_hostname_certificate.deletion.succeeded",
"created_at": "2022-02-09T00:03:28.385080Z"
},
"account": {
"id": "<"
},
"zone": {
"id": "<"
}
},
"data": {
"id": "<",
"hostname": "blog.com",
"ssl": {
"id": "<",
"type": "dv",
"method": "cname",
"status": "deleted"
},
"custom_metadata": {
"key1": "value1",
"key2": "value2"
},
"custom_origin_server": "0001.blog.com"
}
}
```
### Deletion failed
Cloudflare sends this alert each time a certificate remains in status of `pending_deletion` during [DCV retries](/ssl/edge-certificates/changing-dcv-method/validation-backoff-schedule/).
```json
{
"metadata": {
"event": {
"id": "<",
"type": "ssl.custom_hostname_certificate.deletion.failed",
"created_at": "2022-02-09T00:03:28.385080Z"
},
"account": {
"id": "<"
},
"zone": {
"id": "<"
}
},
"data": {
"id": "<",
"hostname": "blog.com",
"ssl": {
"id": "<",
"type": "dv",
"method": "cname",
"status": "pending_deletion"
},
"custom_metadata": {
"key1": "value1",
"key2": "value2"
},
"custom_origin_server": "0001.blog.com"
}
}
```
***
## Certificate renewal
Once issued, certificates are valid for a period of time depending on the [certificate authority](/ssl/reference/certificate-validity-periods/).
The actions that you need to perform to renew certificates depend on your [validation method](/cloudflare-for-platforms/cloudflare-for-saas/security/certificate-management/issue-and-validate/renew-certificates/).
### Upcoming renewal
```json
{
"metadata": {
"event": {
"id": "<",
"type": "ssl.custom_hostname_certificate.renewal.upcoming_certificate_expiration_notification",
"created_at": "2022-02-09T00:03:28.385080Z"
},
"account": {
"id": "<"
},
"zone": {
"id": "<"
}
},
"data": {
"id": "<",
"hostname": "blog.com",
"ssl": {
"id": "<",
"status": "active",
"hosts": ["blog.example.com"],
"issuer": "DigiCertInc",
"serial_number": "1001172778337169491",
"signature": "ECDSAWithSHA256",
"uploaded_on": "2021-11-17T04:33:54.561747Z",
"expires_on": "2022-11-21T12:00:00Z",
"custom_csr_id": "7b163417-1d2b-4c84-a38a-2fb7a0cd7752",
"settings": {
"min_tls_version": "1.2",
"http2": "on"
}
},
"custom_metadata": {
"key1": "value1",
"key2": "value2"
},
"custom_origin_server": "0001.blog.com"
}
}
```
### Renewal succeeded
Cloudflare sends this alert when certificates move from a status of `active` to `pending_deployment`.
```json
{
"metadata": {
"event": {
"id": "<",
"type": "ssl.custom_hostname_certificate.renewal.succeeded",
"created_at": "2022-02-09T00:03:28.385080Z"
},
"account": {
"id": "<"
},
"zone": {
"id": "<"
}
},
"data": {
"id": "<",
"hostname": "blog.com",
"ssl": {
"id": "<",
"type": "dv",
"method": "cname",
"status": "pending_deployment",
"settings": {
"min_tls_version": "1.2",
"http2": "on"
}
},
"custom_metadata": {
"key1": "value1",
"key2": "value2"
},
"custom_origin_server": "0001.blog.com"
}
}
```
### Renewal failed
Cloudflare sends this alert when certificates move from a status of `active` to `pending_issuance`.
```json
{
"metadata": {
"event": {
"id": "<",
"type": "ssl.custom_hostname_certificate.renewal.failed",
"created_at": "2022-02-09T00:03:28.385080Z"
},
"account": {
"id": "<"
},
"zone": {
"id": "<"
}
},
"data": {
"id": "<",
"hostname": "blog.com",
"ssl": {
"id": "<",
"type": "dv",
"method": "cname",
"status": "pending_issuance",
"cname": "_ca3-64ce913ebfe74edeb2e8813e3928e359.app.example2.com",
"cname_target": "dcv.digicert.com",
"validation_errors": [
{
"message": "caa_error: blog.example.com"
}
],
"settings": {
"min_tls_version": "1.2",
"http2": "on"
}
},
"custom_metadata": {
"key1": "value1",
"key2": "value2"
},
"custom_origin_server": "0001.blog.com"
}
}
```
## Troubleshooting
Occasionally, you may see webhook notifications that do not include a corresponding `<>` and `hostname` values.
This behavior is because each custom hostname can only have one certificate attached to it. Previously attached certificates can still emit webhook events but will not include the associated hostname and ID values.
## Alerts
You can configure alerts to receive notifications for changes in your custom hostname certificates.
---
# Custom origin server
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/start/advanced-settings/custom-origin/
import { GlossaryTooltip, Render } from "~/components"
## Requirements
To use a custom origin server, you need to meet the following requirements:
* You have purchased the [Cloudflare for SaaS Enterprise plan](/cloudflare-for-platforms/cloudflare-for-saas/plans/) and the feature is properly entitled to your account.
* Each custom origin needs to be a valid hostname with a proxied (orange-clouded) A, AAAA, or CNAME record in your account's DNS. You cannot use an IP address.
* The DNS record for the custom origin server does not currently support wildcard values.
## Use a custom origin
To use a custom origin, select that option when [creating a new custom hostname](/cloudflare-for-platforms/cloudflare-for-saas/security/certificate-management/issue-and-validate/) in the dashboard or include the `"custom_origin_server": your_custom_origin_server` parameter when using the API [POST command](/api/resources/custom_hostnames/methods/create/).
## SNI rewrites
When Cloudflare establishes a connection to your default origin server, the `Host` header and SNI will both be the value of the original custom hostname.
However, if you configure that custom hostname with a custom origin, the value of the SNI will be that of the custom origin and the `Host` header will be the original custom hostname. Since these values will not match, you will not be able to use the [Full (strict)](/ssl/origin-configuration/ssl-modes/full-strict/) on your origins.
To solve this problem, you can contact your account team to request an entitlement for **SNI rewrites**.
### SNI rewrite options
Choose how your custom hostname populates the SNI value with SNI rewrites:
* **Origin server name** (default): Set SNI to the custom origin
* If custom origin is `custom-origin.example.com`, then the SNI is `custom-origin.example.com`.
* **Host header**: Set SNI to the host header (or a host header override)
* If wildcards are not enabled and the hostname is `example.com`, then the SNI is `example.com`.
* If wildcards are enabled, the hostname is `example.com`, and a request comes to `www.example.com`, then the SNI is `www.example.com`.
* **Subdomain of zone**: Choose what to set as the SNI value (custom hostname or any subdomain)
* If wildcards are not enabled and a request comes to `example.com`, choose whether to set the SNI as `example.com` or `www.example.com`.
* If wildcards are enabled, you set the SNI to `example.com`, and a request comes to `www.example.com`, then the SNI is `example.com`.
:::caution[Important]
* Currently, SNI Rewrite is not supported for wildcard custom hostnames. Subdomains covered by a wildcard custom hostname send the custom origin server name as the SNI value.
* SNI overrides defined in an [Origin Rule](/rules/origin-rules/) will take precedence over SNI rewrites.
* SNI Rewrite usage is subject to the [Service-Specific Terms](https://www.cloudflare.com/service-specific-terms-application-services/#ssl-for-saas-terms).
:::
### Set an SNI rewrite
To set an SNI rewrite in the dashboard, choose your preferred option from **Origin SNI value** when [creating a custom hostname](/cloudflare-for-platforms/cloudflare-for-saas/security/certificate-management/issue-and-validate/).
To set an SNI rewrite via the API, set the `custom_origin_sni` parameter when [creating a custom hostname](/api/resources/custom_hostnames/methods/create/):
* **Custom origin name** (default): Applies if you do not set the parameter
* **Host header**: Specify `":request_host_header:"`
* **Subdomain of zone**: Set to `"example.com"` or another subdomain of the custom hostname
---
# Managed Rulesets
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/security/waf-for-saas/managed-rulesets/
If you are interested in [WAF for SaaS](/cloudflare-for-platforms/cloudflare-for-saas/security/waf-for-saas/) but unsure of where to start, Cloudflare recommends using WAF Managed Rules. The Cloudflare security team creates and manages a variety of rules designed to detect common attack vectors and protect applications from vulnerabilities. These rules are offered in [managed rulesets](/waf/managed-rules/), like Cloudflare Managed and OWASP, which can be deployed with different settings and sensitivity levels.
***
## Prerequisites
WAF for SaaS is available for customers on an Enterprise plan.
If you would like to deploy a managed ruleset at the account level, refer to the [Ruleset Engine documentation](/ruleset-engine/managed-rulesets/deploy-managed-ruleset/).
Ensure you have reviewed [Get Started with Cloudflare for SaaS](/cloudflare-for-platforms/cloudflare-for-saas/start/getting-started/) and familiarize yourself with [WAF for SaaS](/cloudflare-for-platforms/cloudflare-for-saas/security/waf-for-saas/).
Customers can automate the [custom metadata](/cloudflare-for-platforms/cloudflare-for-saas/domain-support/custom-metadata/) tagging by adding it to the custom hostnames at creation. For more information on tagging a custom hostname with custom metadata, refer to the [API documentation](/api/resources/custom_hostnames/methods/edit/).
***
## 1. Choose security tagging system
1. Outline `security_tag` buckets. These are fully customizable with no strict limit on quantity. For example, you can set `security_tag` to `low`,`medium`, and `high` as a default, with one tag per custom hostname.
2. If you have not already done so, [associate your custom metadata to custom hostnames](/cloudflare-for-platforms/cloudflare-for-saas/security/waf-for-saas/#1-associate-custom-metadata-to-a-custom-hostname) by including the `security_tag`in the custom metadata associated with the custom hostname. The JSON blob associated with the custom hostname is fully customizable.
:::note
After the association is complete, the JSON blob is added to the defined custom hostname. This blob is then associated to every incoming request and exposed in the WAF through the new field `cf.hostname.metadata`. In the rule, you can access `cf.hostname.metadata` and get whatever data you need from that blob.
:::
***
## 2. Deploy Rulesets
1. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com/) and navigate to your account.
2. Select Account Home > **WAF**.
:::note
**WAF** at the account level will only be visible on Enterprise plans. If you do not see this option, contact your account manager.
:::
3. Select **Deploy a managed ruleset**.
4. Under **Field**, Select *Hostname*. Set the operator as *equals*. The complete expression should look like this, plus any logic you would like to add:

5. Beneath **Value**, add the custom hostname.
6. Select **Next**.
7. Find the **Cloudflare Managed Ruleset** card and select **Use this Ruleset**.
8. Click the checkbox next to each rule you want to deploy.
9. Toggle the **Status** button next to each rule to enable or disable it. Then select **Next**.
10. On the review page, give your rule a descriptive name. You can modify the ruleset configuration by changing, for example, what rules are enabled or what action should be the default.
11. Select **Deploy**.
:::note
While this tutorial uses Cloudflare Managed Rulesets, you can also create a custom ruleset and deploy on your custom hostnames. To do this, select **Browse Rulesets** > **Create new ruleset**. For examples of a low/medium/high ruleset, refer to [WAF for SaaS](/cloudflare-for-platforms/cloudflare-for-saas/security/waf-for-saas/).
:::
---
# Advanced Settings
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/start/advanced-settings/
import { DirectoryListing } from "~/components";
---
# Workers as your fallback origin
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/start/advanced-settings/worker-as-origin/
If you are building your application on [Cloudflare Workers](/workers/), you can use a Worker as the origin for your SaaS zone (also known as your fallback origin).
1. In your SaaS zone, [create and set a fallback origin](/cloudflare-for-platforms/cloudflare-for-saas/start/getting-started/#1-create-fallback-origin). Ensure the fallback origin only has an [originless DNS record](/dns/troubleshooting/faq/#what-ip-should-i-use-for-parked-domain--redirect-only--originless-setup):
* **Example**: `service.example.com AAAA 100::`
2. In that same zone, navigate to **Workers Routes**.
3. Click **Add route**.
4. Decide whether you want traffic bound for your SaaS zone (`example.com`) to go to that Worker:
* If *yes*, set the following values:
* **Route**: `*/*` (routes everything — including custom hostnames — to the Worker).
* **Worker**: Select the Worker used for your SaaS application.
* If *no*, set the following values:
* **Route**: `*..com/*` (only routes custom hostname traffic to the Worker)
* **Worker**: **None**
5. Click **Save**.
---
# Certificate signing requests (CSRs)
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/security/certificate-management/custom-certificates/certificate-signing-requests/
import { Render } from "~/components"
Once the CSR has been generated, provide it to your customer. Your customer will then pass it along to their preferred CA to obtain a certificate and return it to you. After you receive the certificate, you should upload it to Cloudflare and reference the unique CSR ID that was provided to you during CSR creation.
***
## Generate the private key and CSR
### 1. Build the CSR payload
All fields except for organizational\_unit and key\_type are required. If you do not specify a `key_type`, the default of `rsa2048` (RSA 2048 bit) will be used; the other option is `p256v1` (NIST P-256).
Common names are restricted to 64 characters and subject alternative names (SANs) are limited to 255 characters, [per RFC 5280](https://tools.ietf.org/html/rfc5280). You must specify at least one SAN, and the list of SANs should include the common name.
```bash
request_body=$(< <(cat <" \
--header "X-Auth-Key: " \
--header "Content-Type: application/json" \
--data "$request_body"
# Response:
{
"result": {
"id": "7b163417-1d2b-4c84-a38a-2fb7a0cd7752",
"country": "US",
"state": "MA",
"locality": "Boston",
"organization": "City of Boston",
"organizational_unit": "Championship Parade Detail",
"common_name": "app.example.com",
"sans": [
"app.example.com",
"www.example.com",
"blog.example.com",
"example.com",
],
"key_type": "p256v1",
"csr": "-----BEGIN CERTIFICATE REQUEST-----\nMIIBSzCB8gIBADBiMQswaQYDVQQGEwJVUzELMAkGA1UECBMCTUExDzANBgNVBAcT\nBkJvc3RvbjEaMBgGA1UEChMRQ2l0eSBvZiBDaGFtcGlvbnMxGTAXBgNVBAMTEGNz\nci1wcm9kLnRscy5mdW4wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAaTKf70NYlwr\n20P6P8xj8/4mTN5q28dbZR/gM3u4m/RPs24+PxAfMZCNvkVKAPVWYfUAadZI4Ha/\ndxLh5Q6X5bhIoC4wLAYJKoZIhvcNAQkOMR8wHTAbBqNVHREEFDASghBjc3ItcHJv\nZC50bHMuZnVuMAoGCCqGSM49BAMCA0gAMEUCIQDgtFUZav466SbT2FGBsIBlahDI\nVkg4y+u+V/K5DlY1+gIgQ9xLfUSKnSnJYbM9TwWr4Z964+lBtB9af4O5pp7/PSA=\n-----END CERTIFICATE REQUEST-----\n"
},
"success": true
}
```
Replace the `\n` characters with actual newlines before passing to your customer. This can be accomplished by piping the output of the prior call to a tool like jq and perl, such as:
```bash
curl https://api.cloudflare.com/client/v4/zones/{zone_id}/custom_csrs \
--header "X-Auth-Email: " \
--header "X-Auth-Key: " \
--header "Content-Type: application/json" \
--data "$request_body" | jq .result.csr | perl -npe s'/\\n/\n/g; s/"//g' > csr.txt
```
### 3. Customer obtains certificate
Your customer will take the provided CSR and work with their CA to obtain a signed, publicly trusted certificate.
### 4. Upload the certificate
Upload the certificate and reference the ID that was provided when you generated the CSR.
You should replace newlines in the certificate with literal `\n` characters, as illustrated above in the custom certificate upload example. After doing so, build the request body and provide the ID that was returned in a previous step.
Cloudflare only accepts publicly trusted certificates. If you attempt to upload a self-signed certificate, it will be rejected.
```bash
$ MYCERT="$(cat app_example_com.pem|perl -pe 's/\r?\n/\\n/'|sed -e 's/..$//')"
$ request_body=$(< <(cat <
## Use cases
This situation commonly occurs when your customers use Extended Validation (EV) certificates (the “green bar”) or when their information security policy prohibits third parties from generating private keys on their behalf.
## Limitations
If you use custom certificates, you are responsible for the entire certificate lifecycle (initial upload, renewal, subsequent upload).
Cloudflare also only accepts publicly trusted certificates of these types:
* `SHA256WithRSA`
* `SHA1WithRSA`
* `ECDSAWithSHA256`
If you attempt to upload another type of certificate or a certificate that has been self-signed, it will be rejected.
---
# Manage custom certificates
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/security/certificate-management/custom-certificates/uploading-certificates/
import { Render, TabItem, Tabs } from "~/components"
Learn how to manage custom certificates for your Cloudflare for SaaS custom hostnames. For use cases and limitations, refer to [custom certificates](/cloudflare-for-platforms/cloudflare-for-saas/security/certificate-management/custom-certificates/).
## Upload certificates
This section describes the general process for uploading a custom certificate corresponding to one of the [supported types](/cloudflare-for-platforms/cloudflare-for-saas/security/certificate-management/custom-certificates/#limitations).
:::note
If you must support both RSA and ECDSA refer to [certificate packs](#use-certificate-packs-rsa-and-ecdsa) below.
:::
To upload a custom certificate in the dashboard, select **Custom certificate** while [creating your custom hostname](/cloudflare-for-platforms/cloudflare-for-saas/domain-support/create-custom-hostnames/).
For information about the **bundle method** options, refer to the [Cloudflare SSL/TLS documentation](/ssl/edge-certificates/custom-certificates/bundling-methodologies/).
The call below will upload a certificate for use with `app.example.com`.
Note that if you are using an ECC key generated by OpenSSL, you will need to first remove the `-----BEGIN EC PARAMETERS-----...-----END EC PARAMETERS-----` section of the file.
1. Update the file and build the payload
```bash
$ echo $MYCERT
-----BEGIN CERTIFICATE-----\nMIIFJDCCBAygAwIBAgIQD0ifmj/Yi5NP/2gdUySbfzANBgkqhkiG9w0BAQsFADBN\nMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMScwJQYDVQQDEx5E...SzSHfXp5lnu/3V08I72q1QNzOCgY1XeL4GKVcj4or6cT6tX6oJH7ePPmfrBfqI/O\nOeH8gMJ+FuwtXYEPa4hBf38M5eU5xWG7\n-----END CERTIFICATE-----\n
$ request_body=$(< <(cat <
## Use certificate packs: RSA and ECDSA
A certificate pack allows you to upload up to one RSA and one ECDSA custom certificates to a custom hostname. This process is currently only supported via API.
To upload an RSA and ECDSA certificate to a custom hostname, set the `bundle_method` to `force` and define the `custom_cert_bundle` property when [creating a custom hostname via API](/api/resources/custom_hostnames/methods/create/).
You can also use `"bundle_method": "force"` and `custom_cert_bundle` with a `PATCH` request to the [Edit Custom Hostname](/api/resources/custom_hostnames/methods/edit/) endpoint.
### Delete a custom certificate and private key
Use the [Delete Single Certificate And Key For Custom Hostname](/api/resources/custom_hostnames/subresources/certificate_pack/subresources/certificates/methods/delete/) endpoint to remove one of the custom certificates and corresponding key from a certificate pack.
You cannot delete a certificate if it is the only remaining certificate in the pack.
### Replace a custom certificate and private key
To replace a single custom certificate within a certificate pack that contains two bundled certificates, use the [Replace Custom Certificate And Custom Key In Custom Hostname](/api/resources/custom_hostnames/subresources/certificate_pack/subresources/certificates/methods/update/) endpoint.
You can only replace an RSA certificate with another RSA certificate, or an ECDSA certificate with another ECDSA certificate.
***
## Move to a Cloudflare certificate
If you want to switch from maintaining a custom certificate to using one issued by Cloudflare, you can migrate that certificate with zero downtime.
Send a [`PATCH` request](/api/resources/custom_hostnames/methods/edit/) to your custom hostname with a value for the DCV `method`. As soon as the [certificate is validated](/cloudflare-for-platforms/cloudflare-for-saas/security/certificate-management/issue-and-validate/) and the [hostname is validated](/cloudflare-for-platforms/cloudflare-for-saas/domain-support/hostname-validation/), Cloudflare will remove the old custom certificate and begin serving the new one.
---
# Issue and validate certificates
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/security/certificate-management/issue-and-validate/
import { DirectoryListing } from "~/components";
Once you have [set up your Cloudflare for SaaS application](/cloudflare-for-platforms/cloudflare-for-saas/start/getting-started/), you can start issuing and validating certificates for your customers.
---
# Issue
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/security/certificate-management/issue-and-validate/issue-certificates/
import { Render } from "~/components"
Cloudflare automatically issues certificates when you [create a custom hostname](/cloudflare-for-platforms/cloudflare-for-saas/domain-support/create-custom-hostnames/).
:::note
There are several required steps before a custom hostname and its certificate can become active. For more details, refer to our [Get started guide](/cloudflare-for-platforms/cloudflare-for-saas/start/getting-started/).
:::
## Certificate authorities
If you create the custom hostname via API, you can leave the `certificate_authority` parameter empty to set it to “default CA”. With this option, Cloudflare checks the CAA records before requesting the certificates, which helps ensure the certificates can be issued from the CA.
Refer to [this certificate authorities reference page](/ssl/reference/certificate-authorities/) to learn more about the CAs that Cloudflare uses to issue SSL/TLS certificates.
## Certificate details and compatibility
---
# Renew
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/security/certificate-management/issue-and-validate/renew-certificates/
import { Render } from "~/components"
The exact method for certificate renewal depends on whether that hostname is proxying traffic through Cloudflare and whether it is a wildcard certificate.
Custom hostnames certificates have a 90-day validity period and are available for renewal 30 days before their expiration.
## Non-wildcard hostnames
If you are using a non-wildcard hostname and proxying traffic through Cloudflare, Cloudflare will try to perform DCV automatically on the hostname’s behalf by serving the [HTTP token](/cloudflare-for-platforms/cloudflare-for-saas/security/certificate-management/issue-and-validate/validate-certificates/http/).
If the custom hostname is not proxying traffic through Cloudflare, then the custom hostname domain owner will need to add the TXT or HTTP DCV token for the new certificate to validate and issue. As the SaaS provider, you will be responsible for sharing this token with the custom hostname domain owner.
## Wildcard hostnames
After this step, follow the normal steps for [TXT validation](/cloudflare-for-platforms/cloudflare-for-saas/security/certificate-management/issue-and-validate/validate-certificates/txt/).
---
# Apex proxying
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/start/advanced-settings/apex-proxying/
import { Render } from "~/components";
Apex proxying allows your customers to use their apex domains (`example.com`) with your SaaS application.
## Benefits
In a normal Cloudflare for SaaS [setup](/cloudflare-for-platforms/cloudflare-for-saas/start/getting-started/), your customers route traffic to your hostname by creating a `CNAME` record pointing to your CNAME target.
However, most DNS providers do not allow `CNAME` records at the zone's root[^1]. This means that your customers have to use a subdomain as a vanity domain (`shop.example.com`) instead of their domain apex (`example.com`).
This limitation does not apply with apex proxying. Cloudflare assigns a set of IP prefixes - cost associated, reach out to your account team - to your account (or uses your own if you have [BYOIP](/byoip/)). This means that customers can create a standard `A` record to route traffic to your domain, which can support the domain apex.
[^1]: Cloudflare offers this functionality through [CNAME flattening](/dns/cname-flattening/).
## Setup
- [Set up Apex Proxying](/cloudflare-for-platforms/cloudflare-for-saas/start/advanced-settings/apex-proxying/setup/)
---
# Setup
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/start/advanced-settings/apex-proxying/setup/
import { Render } from "~/components"
To set up Cloudflare for SaaS for [apex proxying](/cloudflare-for-platforms/cloudflare-for-saas/start/advanced-settings/apex-proxying/) - as opposed to the [normal setup](/cloudflare-for-platforms/cloudflare-for-saas/start/getting-started/) - perform the following steps.
***
***
## Initial setup
### 1. Get IP range
With apex proxying, you can either [bring your own IP range](/byoip/) or use a set of IP addresses provided by Cloudflare.
For more details on this step, reach out to your account team.
:::caution
These IP addresses are different than those associated with your Cloudflare zone.
:::
### 2. Create fallback origin
***
## Per-hostname setup
### 3. Have customer create DNS record
To finish the custom hostname setup, your customer can set up either an A or CNAME record at their authoritative DNS provider.
:::note
If you want your customers to be able to use CNAME records, you will need to complete the [normal setup process](/cloudflare-for-platforms/cloudflare-for-saas/start/getting-started/) as well.
:::
#### A record
If your customer uses an A record at their authoritative DNS provider, they need to point their hostname to the IP prefixed allocated for your account.
Your customer's A record might look like the following:
```txt
example.com. 60 IN A 192.0.2.1
```
#### CNAME record
If your customer uses a CNAME record at their authoritative DNS, they need to point their hostname to your [CNAME target](/cloudflare-for-platforms/cloudflare-for-saas/start/getting-started/#2-optional-create-cname-target) [^1].
Your customer's CNAME record might look like the following:
```txt
mystore.com CNAME customers.saasprovider.com
```
[^1]:
#### Service continuation
---
# Delegated
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/security/certificate-management/issue-and-validate/validate-certificates/delegated-dcv/
import { Render } from "~/components"
Delegated DCV allows SaaS providers to delegate the DCV process to Cloudflare.
DCV Delegation requires your customers to place a one-time record at their authoritative DNS that allows Cloudflare to auto-renew all future certificate orders, so that there is no manual intervention from you or your customers at the time of the renewal.
***
## When to use
### HTTP DCV
### TXT DCV
* [DCV Delegation](#setup) (generally recommended)
* [Manual](/cloudflare-for-platforms/cloudflare-for-saas/security/certificate-management/issue-and-validate/validate-certificates/txt/)
***
## Setup
To set up Delegated DCV:
1. Add a [custom hostname](/cloudflare-for-platforms/cloudflare-for-saas/domain-support/create-custom-hostnames/) for your zone, choosing `TXT` as the **Certificate validation method**.
2. On **SSL/TLS** > **Custom Hostnames**, go to **DCV Delegation for Custom Hostnames**.
3. Copy the hostname value.
4. For each hostname, the domain owner needs to place a `CNAME` record at their authoritative DNS. In this example, the SaaS zone is `example.com`.
```txt
_acme-challenge.example.com CNAME example.com..
```
Once this is complete, Cloudflare will place two TXT DCV records - one for `example.com` and one for `*.example.com` - at the `example.com.` hostname. The CNAME record will need to stay in place in order to allow Cloudflare to continue placing the records for the renewals.
If desired, you could also manually fetch the DCV tokens and share them with your customers.
## Moved domains
If you [move your SaaS zone to another account](/fundamentals/setup/manage-domains/move-domain/), you will need to update the `CNAME` record with a new hostname value.
---
# HTTP
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/security/certificate-management/issue-and-validate/validate-certificates/http/
import { Details, Render } from "~/components"
HTTP validation involves adding a DCV token to your customer's origin.
***
## Non-wildcard custom hostnames
If your custom hostname does not include a wildcard, Cloudflare will always and automatically attempt to complete DCV through [HTTP validation](#http-automatic), even if you have selected **TXT** for your validation method.
This HTTP validation should succeed as long as your customer is pointing to your custom hostname and they do not have any [CAA records](/cloudflare-for-platforms/cloudflare-for-saas/security/certificate-management/issue-and-validate/validate-certificates/troubleshooting/#certificate-authority-authorization-caa-records) blocking your chosen certificate authority.
## Wildcard custom hostnames
HTTP DCV validation is not allowed for wildcard certificates. You must use [TXT validation](/cloudflare-for-platforms/cloudflare-for-saas/security/certificate-management/issue-and-validate/validate-certificates/txt/) instead.
***
## Validation methods
### HTTP (automatic)
If you value simplicity and your customers can handle a few minutes of downtime, you can rely on Cloudflare automatic HTTP validation.
Once you [create a new hostname](/cloudflare-for-platforms/cloudflare-for-saas/security/certificate-management/issue-and-validate/issue-certificates/) and choose the `http` validation method, all your customers have to do is add a CNAME to your `$CNAME_TARGET` and Cloudflare will take care of the rest.
:::note
Cloudflare is able to serve a random token from our edge due to the fact that `site.example.com` has a CNAME in place to `$CNAME_TARGET`, which ultimately resolves to Cloudflare IPs. If your customer has not yet added the CNAME, the CA will not be able to retrieve the token and the process will not complete.
We will attempt to retry this validation check for a finite period before timing out. Refer to [Validation Retry Schedule](/ssl/edge-certificates/changing-dcv-method/validation-backoff-schedule/) for more details.
:::
If you would like to complete the issuance process before asking your customer to update their CNAME (or before changing the resolution of your target CNAME to be proxied by Cloudflare), choose another validation method.
### HTTP (manual)
* [**API**](/api/resources/custom_hostnames/methods/get/): Within the `ssl` object, store the values present in the `validation_records` array (specifically `http_url` and `http_body`).
* **Dashboard**: When viewing an individual certificate at **SSL/TLS** > **Custom Hostnames**, refer to the values for **Certificate validation request** and **Certificate validation response**.
At your origin, make the `http_body` available in a TXT record at the path specified in `http_url`. This path should also be publicly accessible to anyone on the Internet so your CA can access it.
Here is an example NGINX configuration that would return a token:
```txt
location "/.well-known/pki-validation/ca3-0052344e54074d9693e89e27486692d6.txt" {
return 200 "ca3-be794c5f757b468eba805d1a705e44f6\n";
}
```
Once your configuration is live, test that the DCV text file is in place with `curl`:
```sh
$ curl "http://http-preval.example.com/.well-known/pki-validation/ca3-0052344e54074d9693e89e27486692d6.txt"
ca3-be794c5f757b468eba805d1a705e44f6
```
The token is valid for one check cycle. On the next check cycle, Cloudflare will ask the CA to recheck the URL, complete validation, and issue the certificate.
---
# Validate
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/security/certificate-management/issue-and-validate/validate-certificates/
import { Render } from "~/components"
## DCV situations
### Non-wildcard certificates
### Wildcard certificates
* [DCV Delegation](/cloudflare-for-platforms/cloudflare-for-saas/security/certificate-management/issue-and-validate/validate-certificates/delegated-dcv/) (auto-issuance)
* [Manual](/cloudflare-for-platforms/cloudflare-for-saas/security/certificate-management/issue-and-validate/validate-certificates/txt/)
### Minimize downtime
If you want to minimize downtime, explore one of the following methods to issue and deploy the certificate before onboarding your customers:
* [Delegated DCV](/cloudflare-for-platforms/cloudflare-for-saas/security/certificate-management/issue-and-validate/validate-certificates/delegated-dcv/): Place a one-time record at your authoritative DNS that allows Cloudflare to auto-renew all future certificate orders.
* [TXT validation](/cloudflare-for-platforms/cloudflare-for-saas/security/certificate-management/issue-and-validate/validate-certificates/txt/): Have your customers add a `TXT` record to their authoritative DNS.
* [Manual HTTP validation](/cloudflare-for-platforms/cloudflare-for-saas/security/certificate-management/issue-and-validate/validate-certificates/http/#http-manual): Add a `TXT` record at your origin.
### Minimize customer effort
If you value simplicity and your customers can handle a few minutes of downtime, you can rely on Cloudflare [automatic HTTP validation](/cloudflare-for-platforms/cloudflare-for-saas/security/certificate-management/issue-and-validate/validate-certificates/http/#http-automatic).
## Potential issues
To avoid or solve potential issues, refer to our [troubleshooting guide](/cloudflare-for-platforms/cloudflare-for-saas/security/certificate-management/issue-and-validate/validate-certificates/troubleshooting/).
---
# Troubleshooting
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/security/certificate-management/issue-and-validate/validate-certificates/troubleshooting/
## High-risk domains
Occasionally, a domain will be flagged as “high risk” by Cloudflare’s CA partners. Typically this is done only for domains with an Alexa ranking of 1-1,000 and domains that have been flagged for phishing or malware by Google’s Safe Browsing service.
If a domain is flagged by the CA, you need to contact Support before validation can finish. The API call will return indicating the failure, along with a link to where the ticket can be filed.
***
## Certificate Authority Authorization (CAA) records
CAA is a DNS resource record type defined in [RFC 6844](https://datatracker.ietf.org/doc/html/rfc6844) that allows a domain owner to indicate which CAs are allowed to issue certificates for them.
### For SaaS providers
If your customer has CAA records set on their domain, they will either need to add the following or remove CAA entirely:
```txt
example.com. IN CAA 0 issue "pki.goog"
example.com. IN CAA 0 issue "letsencrypt.org"
example.com. IN CAA 0 issue "ssl.com"
```
While it is possible for CAA records to be set on the subdomain your customer wishes to use with your service, it will usually be set on the domain apex. If they have CAA records on the subdomain, those will also have to be removed.
### For SaaS customers
In some cases, the validation may be prevented because your hostname points to a CNAME target where CAA records are defined.
In this case you would need to either select a Certificate Authority whose CAA records are present at the target, or review the configuration with the service provider that owns the target.
***
## Time outs
If a certificate issuance times out, the error message will indicate where the timeout occurred:
* Timed Out (Initializing)
* Timed Out (Validation)
* Timed Out (Issuance)
* Timed Out (Deployment)
* Timed Out (Deletion)
To fix this error, send a [PATCH request](/api/resources/custom_hostnames/methods/edit/) through the API or select **Refresh** for the specific custom hostname in the dashboard. If using the API, make sure that the `--data` field contains an `ssl` object with the same `method` and `type` as the original request.
If these return an error, delete and recreate the custom hostname.
***
## Immediate validation checks
You can send a [PATCH request](/api/resources/custom_hostnames/methods/edit/) to request an immediate validation check on any certificate. The PATCH data should include the same `ssl` object as the original request.
***
---
# TXT
URL: https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/security/certificate-management/issue-and-validate/validate-certificates/txt/
import { Render, TabItem, Tabs } from "~/components";
## When to use
Generally, you should use TXT-based DCV when you cannot use [HTTP validation](/cloudflare-for-platforms/cloudflare-for-saas/security/certificate-management/issue-and-validate/validate-certificates/http/) or [Delegated DCV](/cloudflare-for-platforms/cloudflare-for-saas/security/certificate-management/issue-and-validate/validate-certificates/delegated-dcv/).
### Non-wildcard custom hostnames
If your custom hostname does not include a wildcard, Cloudflare will always and automatically attempt to complete DCV through [HTTP validation](/cloudflare-for-platforms/cloudflare-for-saas/security/certificate-management/issue-and-validate/validate-certificates/http/#http-automatic), even if you have selected **TXT** for your validation method.
This HTTP validation should succeed as long as your customer is pointing to your custom hostname and they do not have any [CAA records](/cloudflare-for-platforms/cloudflare-for-saas/security/certificate-management/issue-and-validate/validate-certificates/troubleshooting/#certificate-authority-authorization-caa-records) blocking your chosen certificate authority.
### Wildcard custom hostnames
This means that - if you choose to use wildcard custom hostnames - you will need a way to share these DCV tokens with your customer.
---
### 1. Get TXT tokens
Once you [create a new hostname](/cloudflare-for-platforms/cloudflare-for-saas/security/certificate-management/issue-and-validate/issue-certificates/) and choose this validation method, your tokens will be ready after a few seconds.
### 2. Share with your customer
You will then need to share these TXT tokens with your customers.
### 3. Add DNS records (customer)
### 4. (Optional) Fetch new tokens
Your DCV tokens expire after a [certain amount of time](/cloudflare-for-platforms/cloudflare-for-saas/reference/token-validity-periods/), depending on your certificate authority.
This means that, if your customers take too long to place their tokens at their authoritative DNS provider, you may need to [get new tokens](#1-get-txt-tokens) and re-share them with your customer.
---