Rules
- Rules changelog
- Rules examples
- Cloudflare Rules
- Configure a Cloud Connector rule via API
- Configure a Cloud Connector rule in the dashboard
- Configure Cloud Connector rules using Terraform
- Cloud Connector
- Supported cloud providers in Cloud Connector
- Cloud Connector examples
- Route /images to an S3 Bucket using Terraform
- Route /images to an S3 Bucket
- Send EU visitors to a Google Cloud Storage bucket
- Serve /static-assets from Azure Blob Storage
- Create a compression rule via API
- Create a compression rule in the dashboard
- Compression Rules
- Compression Rules settings
- Disable Brotli compression
- Disable compression for AVIF images
- Enable Zstandard compression for default content types
- Use Gzip compression for CSV files
- Compression Rules examples
- Use only Brotli compression for a specific path
- Create a configuration rule via API
- Create a configuration rule in the dashboard
- Configuration Rules
- Response body inspection
- Configuration Rules settings
- Define a single configuration rule using Terraform
- Configuration Rules examples
- Common API calls for Custom Errors
- Create custom error rules
- Edit Error Pages
- Example custom error rules
- Custom Errors
- Troubleshoot Error Pages issues
- Error page types
- Error tokens
- Reference
- Custom Errors parameters
- URL normalization examples
- How URL normalization works
- URL normalization
- Configure URL normalization in the dashboard
- URL normalization settings
- Create an origin rule via API
- Create an origin rule in the dashboard
- Origin Rules FAQ
- Origin Rules settings
- Origin Rules
- Origin Rules API parameter reference
- Change the HTTP Host header and DNS record
- Change the destination port
- Define a single origin rule using Terraform
- Origin Rules examples
- Change URI path and Host header
- Origin Rules tutorials
- Point to Pages with a custom domain
- Point to R2 bucket with a custom domain
- Page Rules
- Manage Page Rules
- How to
- URL forwarding with Page Rules
- Additional reference for Page Rules
- Reference
- Recommended page rules
- Page Rules settings
- Wildcard matching in Page Rules
- Troubleshoot Page Rules - Billing and subscription
- Troubleshoot Page Rules - General
- Troubleshooting
- Reference
- Page Rules migration guide
- Troubleshoot Rules
- Rules simulator changelog
- Use Rules simulator
- Rules simulator
- Rules simulator limitations
- Configure Snippets via API
- Create a snippet in the dashboard
- Configure Snippets using Terraform
- Troubleshoot Snippets
- How Snippets work
- Cloudflare Snippets
- When to use Snippets vs Workers
- A/B testing with same-URL direct access
- Append dates to cookies to use with A/B testing
- Auth with headers
- Send Bot Management information to origin
- Send suspect bots to a honeypot
- Bulk redirect based on a map object
- Country code redirect
- Custom cache
- Debugging logs
- Define CORS headers
- Follow redirects from the origin
- Add HEX timestamp to a request header
- Snippets examples
- Validate JSON web tokens (JWT)
- Maintenance page
- Override a Set-Cookie header with a certain value
- Redirect 403 Forbidden to a different page
- Redirect from one domain to another
- Remove fields from API response
- Remove query strings before sending request to origin
- Remove response headers
- Return information about the incoming request
- Rewrite links on HTML pages
- Change origin and modify paths
- Set security headers
- Send timestamp to origin as a custom header
- Route to a different origin based on origin response
- Sign requests
- Slow down suspicious requests
- Transform Rules
- Troubleshoot Transform Rules
- Add a wildcard CORS response header
- Add a request header with the current bot score
- Add request header with a static value
- Add a request header for subrequests from other zones
- Add a response header with a static value
- Transform Rules examples
- Normalize encoded slashes in URL path
- Remove a request header
- Remove a response header
- Rewrite blog archive URLs
- Rewrite path of moved section of a website
- Rewrite path of archived blog posts
- Rewrite path for object storage bucket
- Rewrite image paths with several URL segments
- Rewrite URL query string
- Rewrite page path for visitors in specific countries
- Set a response header with the current bot score
- Set response header with a static value
- Configure Managed Transforms
- Managed Transforms
- Available Managed Transforms
- Create a request header transform rule via API
- Create a request header transform rule in the dashboard
- Request Header Transform Rules
- Available fields and functions
- Format of HTTP request header names and values
- Reference
- API parameter reference
- Create a response header transform rule via API
- Create a response header transform rule in the dashboard
- Response Header Transform Rules
- Available fields and functions
- Format of HTTP response header names and values
- Reference
- API parameter reference
- Create a URL rewrite rule via API
- Create a URL rewrite rule in the dashboard
- URL Rewrite Rules
- Available fields and functions
- Reference
- URL rewrite parameters
- Redirects
- Bulk Redirects concepts
- Create Bulk Redirects via API
- Create Bulk Redirects in the dashboard
- Bulk Redirects FAQ
- How Bulk Redirects work
- Bulk Redirects
- Configure Bulk Redirects using Terraform
- CSV file format for Bulk Redirects
- Available fields and functions
- Reference
- Bulk Redirects API JSON objects
- URL redirect parameters
- Supported URL components in Bulk Redirects
- Redirect examples
- Perform mobile redirects
- Redirect admin area requests to HTTPS
- Redirect requests from one domain to another
- Redirect requests from one country to a domain
- Redirect requests for a domain to a new domain
- Redirect requests to a different hostname
- Redirect local visitors to specific subdomains
- Redirect visitors to a new page URL
- Redirect from root to WWW
- Redirect from WWW to root
- Remove locale from URL path
- Create a redirect rule via API
- Create a redirect rule in the dashboard
- Single Redirects
- Single Redirects settings
- Create a redirect rule using Terraform
Rules changelog
import { ProductChangelog } from "~/components";
{/* */}
2024-09-20
Automatic DNS Validation for Cloudflare Rules
The Cloudflare dashboard now automatically validates DNS records and Cloudflare for SaaS custom hostnames for rules targeting specific hostnames or URLs. To prevent misconfigured rules and ensure smoother deployments, you will get proactive warnings for missing or misconfigured DNS records and custom hostnames.
2024-09-17
Compression Rules available to all plans with Zstandard support
Compression Rules now support Zstandard compression and are available in all Cloudflare plans. Users in the Free plan will gradually get access throughout 2024.
2024-09-13
Snippets now available in beta
Cloudflare Snippets have transitioned from alpha to beta.
2024-09-10
wildcard_replace() function now supported in URL rewrites
You can now use the wildcard_replace() function in rewrite expressions of URL rewrites.
2024-09-05
New Rules Templates for one-click rule creation
The new Rules > Templates page in the Cloudflare dashboard allows you to create common rules with a single click, featuring dozens of pre-built templates. You can also access these templates directly from each product's rule builder. Also, explore the Examples gallery in the developer docs for real-world use cases and inspiration.
2024-08-22
Simplified UI for Single Redirects with wildcard support
The simplified UI for Single Redirects is now available to all users, making URL redirects easier and more intuitive. This update builds on the recent wildcard support in Ruleset Engine products. Access the new UI under Rules > Redirect Rules. Learn more about wildcard support and our open-source Rust crate in the blog post.
2024-08-20
Cloud Connector now available to all customers
Cloud Connector (beta) is now available to all customers. For setup details, refer to the documentation, explore examples, and check out the blog post.
2024-08-16
Cloud Connector now available to all free customers
Cloud Connector (beta) is now available to all free and a subset of paid customers. This rollout will be gradually extended to all Cloudflare users, simplifying multi-cloud management and enhancing integration with Cloudflare's Connectivity Cloud. For more information, refer to the blog post.
2024-08-12
Cloudflare Snippets limits have been upgraded
Cloudflare Snippets (alpha) now allow multiple subrequests depending on your plan. For more information, refer to the Availability.
2024-07-31
Wildcard support added to Ruleset Engine products
Wildcards are now supported across our Ruleset Engine-based products, including Single Redirects, Cache Rules, Transform Rules, WAF, Waiting Room, and more:
- You can now use the
wildcardandstrict wildcardoperators with any string field in the Ruleset Engine, such as full URI, host, headers, cookies, user-agent, and country. For more details, refer to Operators and Wildcard matching. - In Single Redirects, the
wildcard_replace()function allows you to use segments matched by thewildcardandstrict wildcardoperators in redirect URL targets. For more information, refer to Functions.
2024-07-01
Cloudflare Snippets now available to all paid customers
Cloudflare Snippets (alpha) are now available to all paid customers.
2024-06-03
Cloudflare Snippets now available to all Enterprise customers
Cloudflare Snippets (alpha) are now available to all Enterprise customers. Customers in other paid plans will gradually get access throughout 2024.
2024-05-14
Page Rules migration
The Page Rules migration guide is now available for users interested in transitioning to modern Rules features instead of Page Rules. Explore the guide for detailed instructions on migrating your configurations.
2024-05-13
New Configuration Rules setting for Web Analytics (RUM)
You can now turn off Cloudflare Web Analytics, also known as Real User Monitoring (RUM), for specific requests using a configuration rule.
2024-04-29
New Configuration Rules setting for Cloudflare Fonts
You can now turn on or off Cloudflare Fonts for specific requests using a configuration rule.
2024-03-22
New TLS fields in rule expressions
Customers can now use new fields cf.tls_client_hello_length (the length of the client hello message sent in a TLS handshake), cf.tls_client_random (the value of the 32-byte random value provided by the client in a TLS handshake), and cf.tls_client_extensions_sha1 (the SHA-1 fingerprint of TLS client extensions) in various products built on Ruleset Engine.
2024-03-20
Origin Rules now allow port numbers in Host Header Override
Customers can now use arbitrary port numbers in Host Header Override in Origin Rules. Previously, only hostname was allowed as a value (for example, example.com). Now, you can set the value to hostname:port (for example, example.com:1234) as well.
Rules examples
import { ResourcesBySelector, GlossaryTooltip } from "~/components";
Explore the following examples for Rules.
:::note
We have a separate listing for Cache rules examples.
:::
<ResourcesBySelector directory="rules" types={["example"]} filterables={["products"]} />
Cloudflare Rules
import { CardGrid, Feature, LinkTitleCard, Plan, RelatedProduct, } from "~/components";
Cloudflare Rules allows you to control how Cloudflare handles traffic to your website. For example, redirecting visitors, rewriting URLs, overriding where requests are sent, or customizing Cloudflare settings for specific requests.
Rules features require that your domain (or subdomain) has its DNS records proxied through Cloudflare, meaning traffic passes through the Cloudflare network before reaching your origin server.
Features
Customize Cloudflare configuration settings for matching incoming requests. Customize the behavior of your website or application using short pieces of JavaScript code. Adjust the URI path, query string, and HTTP headers of requests and responses on the Cloudflare global network. Redirect visitors from a source URL to a target URL with a specific HTTP status code. Use Single Redirects or Bulk Redirects depending on your use case. Customize where the incoming traffic will go and with which parameters. Override request properties such as `Host` header, destination hostname, and destination port. Route matching incoming traffic from your website to a public cloud provider such as AWS, Google Cloud, and Azure. Customize the compression applied to responses from Cloudflare's global network to your website visitors, based on the file extension and content type. Trigger certain actions when a request matches a URL pattern.<Feature header="URL normalization" href="/rules/normalization/" cta="Configure URL normalization"
Modify the URLs of incoming requests so that they conform to a consistent
formatting standard.
<Feature header="Custom Errors" href="/rules/custom-errors/" cta="Configure Custom Errors"
Define what custom content to serve for errors returned by an origin server or
by a Cloudflare product, including Workers.
Related products
Control incoming traffic by filtering requests to a zone. You can block or challenge incoming requests according to rules you define.Define rate limits for requests matching an expression, and the action to
perform when those rate limits are reached.
Customize the cache properties of your HTTP requests.
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
<LinkTitleCard title="Plans" href="https://www.cloudflare.com/plans/#overview" icon="document"
Compare available Cloudflare plans
Configure a Cloud Connector rule via API
import { APIRequest } from "~/components";
You can configure Cloud Connector rules using the Cloudflare API.
Required permissions
The API token used in API requests to manage Cloud Connector rules must have at least the following permission:
- Zone > Cloud Connector > Write
:::note
A token with this permission is only valid for the Cloud Connector endpoints described in this page. You cannot use it to interact with the http_cloud_connector phase via Rulesets API.
:::
Endpoints
To obtain the complete endpoint, append the Cloud Connector endpoints listed below to the Cloudflare API base URL:
https://api.cloudflare.com/client/v4
The {zone_id} argument is the zone ID (a hexadecimal string). You can find this value in the Cloudflare dashboard.
The following table summarizes the available operations.
| Operation | Verb + Endpoint |
|---|---|
| List Cloud Connector rules | GET /zones/{zone_id}/cloud_connector/rules |
| Create/update/delete Cloud Connector rules | PUT /zones/{zone_id}/cloud_connector/rules |
Example API calls
List of Cloud Connector rules
The following example returns a list of existing Cloud Connector rules:
{
"result": [
{
"id": "<RULE_1_ID>",
"provider": "aws_s3",
"expression": "http.request.uri.path wildcard \"/images/*\"",
"description": "Connect to S3 bucket containing images",
"enabled": true,
"parameters": {
"host": "examplebucketwithimages.s3.north-eu.amazonaws.com"
}
}
],
"success": true,
"errors": [],
"messages": []
}
Create/update/delete Cloud Connector rules
:::caution To create a new rule and keep all existing rules, you must include them all in your request body. Omitting an existing rule in the request body will delete the corresponding Cloud Connector rule. :::
The following example request will replace all existing Cloud Connector rules with a single rule:
<APIRequest path="/zones/{zone_id}/cloud_connector/rules" method="PUT" json={[ { expression: 'http.request.uri.path wildcard "/images/*"', provider: "cloudflare_r2", description: "Connect to R2 bucket containing images", parameters: { host: "mybucketcustomdomain.example.com", }, }, ]} />
The required body parameters for each rule are: expression, provider, and parameters.host.
The provider value must be one of the following: cloudflare_r2, aws_s3, azure_storage, gcp_storage, and oci_storage.
Configure a Cloud Connector rule in the dashboard
import { Render, Steps, DashButton } from "~/components";
To configure a Cloud Connector rule in the dashboard:
-
In the Cloudflare dashboard, go to the Cloud Connector page.
-
Select your cloud provider (Cloudflare R2 or an external provider).
-
If you selected Cloudflare R2 in the previous step, select your bucket and your custom domain, and select Next.
If you selected a different storage provider, enter the bucket URL and select Next.:::caution The bucket URL must follow a specific format according to your provider. :::
-
Enter a descriptive name for the rule in Cloud Connector name.
-
Under If, select Custom filter expression and enter an expression to define the traffic that will be redirected to the bucket. For example:
- To route all requests matching
http*://example.com/images/*(HTTPS and HTTP requests) you could enter the following expression:
http.request.full_uri wildcard "http*://example.com/images/*" - To route all requests matching
http*://images.example.com/*(HTTPS and HTTP requests) you could enter the following expression:
http.request.full_uri wildcard "http*://images.example.com/*"
Alternatively, select All incoming requests to redirect all incoming traffic for your zone to the storage bucket you selected.
- To route all requests matching
-
To save and deploy your rule, select Deploy. If you are not ready to deploy the rule, select Save as Draft.
Configure Cloud Connector rules using Terraform
import { Render } from "~/components";
You can create Cloud Connector rules using the Terraform Cloudflare provider.
To get started with Terraform for Cloudflare configuration, refer to Get started.
Required permissions
The API token used by Terraform must have at least the following permission:
- Zone > Cloud Connector > Write
Example configuration
The following example Terraform configuration creates Cloud Connector rules for various supported providers to route traffic between them based on URI paths:
resource "cloudflare_cloud_connector_rules" "cloud_connector_rules" {
zone_id = "<ZONE_ID>"
rules {
description = "Route /data to GCP bucket"
enabled = true
expression = "(http.request.uri.path wildcard \"*/data/*\")"
provider = "gcp_storage"
parameters {
host = "mystorage.storage.googleapis.com"
}
}
rules {
description = "Route /resources to AWS bucket"
enabled = true
expression = "(http.request.uri.path wildcard \"*/resources/*\")"
provider = "aws_s3"
parameters {
host = "mystorage.s3.ams.amazonaws.com"
}
}
rules {
description = "Route /files to Azure bucket"
enabled = true
expression = "(http.request.uri.path wildcard \"*/files/*\")"
provider = "azure_storage"
parameters {
host = "mystorage.blob.core.windows.net"
}
}
rules {
description = "Route /images to R2 bucket"
enabled = true
expression = "(http.request.uri.path wildcard \"*/images/*\")"
provider = "cloudflare_r2"
parameters {
host = "mybucketcustomdomain.example.com"
}
}
}
More resources
Refer to the Terraform Cloudflare provider documentation for more information on the cloudflare_cloud_connector_rules resource.
Cloud Connector
import { ProductAvailabilityText, Render } from "~/components";
Cloud Connector allows you to route matching incoming traffic from your website to a public cloud provider that you define: Cloudflare R2 object storage or an external provider such as AWS, Google Cloud, Microsoft Azure, and Oracle Cloud. With Cloud Connector, you can manage traffic to cloud-hosted content through the same Cloudflare dashboard you use for the rest of your website, without having to configure additional rules.
<Render file="rules-requirements" product="rules" params={{ sentenceIntro: "Cloud Connector requires" }} />
How it works
First, you configure a Cloud Connector rule that specifies:
- The cloud provider and a supported cloud service that will accept traffic.
- The traffic that will be routed to that cloud service.
Then, Cloudflare will create the necessary configurations so that the content is accessible for requests matching your Cloud Connector rule. Your object storage bucket must be publicly accessible for Cloud Connector to work.
Cloud Connector rules are evaluated last in the request evaluation workflow. When a Cloud Connector rule matches and other rules have modified the same settings (such as the Host header), the Cloud Connector rule takes precedence.
Applied configurations
Cloud Connector will perform the following configurations automatically, depending on the cloud provider:
- Modify the
Hostheader. - Adjust SSL/TLS for bucket-related traffic (AWS S3 website endpoints only).
:::note[Additional configurations you may need]
Cloud Connector will not apply any of the following configurations:
- Cache content served from storage bucket: To define custom cache behavior — like when to cache returned objects and for how long — you will need to create a cache rule. For an example rule configuration, refer to Cache Level (Cache Everything).
- Create URL rewrites: To adjust the URL structure from what your website visitors use to obtain a resource and the folder structure being used in the storage bucket, you will need to create a URL rewrite. For example, you could create a URL rewrite to remove the
/filesprefix from URI paths before routing the request to your object storage bucket. For an example configuration, refer to Rewrite path for object storage bucket.
:::
Availability
Cloud Connector is available in beta to all customers. The maximum number of rules depends on your Cloudflare plan:
| Free | Pro | Business | Enterprise | |
|---|---|---|---|---|
| Availability | Yes | Yes | Yes | Yes |
| Number of rules | 10 | 25 | 50 | 300 |
Supported cloud providers in Cloud Connector
import { Render, Steps } from "~/components";
Cloud Connector currently supports the following cloud providers and services:
- Cloudflare R2
- Amazon Web Services - S3
- Google Cloud Platform - Cloud Storage
- Microsoft Azure - Blob Storage
- Oracle Cloud - Object Storage
Cloudflare R2
The Cloudflare R2 bucket must be public and exposed using a custom domain. Buckets exposed using an r2.dev subdomain are not supported.
Additionally, the custom domain must be defined in the same zone where you are configuring the Cloud Connector rule.
Amazon Web Services - S3
The hostname of your S3 bucket URL must have one of the following formats (where * is a wildcard character):
*s3.amazonaws.com*s3.<REGION>.amazonaws.com*s3-website.<REGION>.amazonaws.com*s3-website-<REGION>.amazonaws.com
Cloud Connector supports both subdomain and URI path-style URLs:
- Subdomain-style URLs: Set the hostname to
<BUCKET_NAME>.s3.amazonaws.com. In this case, your files are accessible directly under the root of the bucket. For example,https://example.com/index.htmlwill map tohttps://<BUCKET_NAME>.s3.amazonaws.com/index.html. When using Full (Strict) SSL/TLS mode, the<BUCKET_NAME>cannot include dots (use dashes instead). Refer to SSL connections to AWS S3 endpoints for details. - URI path-style URLs: Set the hostname to
s3.amazonaws.com. Here, your bucket name must be part of the URI path in your requests. For example, if your bucket name is<BUCKET_NAME>, files will be available on paths likehttps://example.com/<BUCKET_NAME>/index.html, and your Cloud Connector rule should filter traffic based on the URI path starting with/<BUCKET_NAME>.
SSL connections to AWS S3 endpoints
The SSL setting applied to requests between Cloud Connector and AWS S3 depends on the type of S3 endpoint you use:
- HTTPS-supported endpoints: For hostnames like
*s3.<REGION>.amazonaws.comand*s3.amazonaws.com, Cloudflare will connect to AWS S3 over HTTPS if you set your zone's SSL/TLS mode to Full or Full (Strict). When using Full (Strict), the bucket name cannot include dots (use dashes instead). - Non-HTTPS endpoints: For website-style hostnames such as
*s3-website.<REGION>.amazonaws.comor*s3-website-<REGION>.amazonaws.com, which do not support HTTPS, Cloudflare will default to Flexible SSL.
Get the bucket URL
- Go to the Amazon S3 console and select Buckets in the navigation pane.
- Select the bucket name.
- Go to the Properties tab.
- Select the Static Website Hosting card. The Endpoint field shows your bucket URL.
For more information, refer to the Amazon S3 documentation.
Google Cloud Platform - Cloud Storage
The hostname of your Cloud Storage bucket URL must be the following (where * is a wildcard character):
*storage.googleapis.com*storage.cloud.google.com
Cloud Connector supports both subdomain and URI path-style URLs:
- Subdomain-style URLs: Set the hostname to
<BUCKET_NAME>.storage.googleapis.com. In this case, your files are accessible directly under the root of the bucket. For example,https://example.com/index.htmlwill map tohttps://<BUCKET_NAME>.storage.googleapis.com/index.html. - URI path-style URLs: Set the hostname to
storage.googleapis.com. Here, your bucket name must be part of the URI path in your requests. For example, if your bucket name is<BUCKET_NAME>, files will be available on paths likehttps://example.com/<BUCKET_NAME>/index.html, and your Cloud Connector rule should filter traffic based on the URI path starting with/<BUCKET_NAME>.
Get the bucket URL
-
Go to the Google Cloud console and select Buckets.
-
Select the bucket name.
-
For one of the files already in the bucket, select the link icon in the Public column to copy the file's public URL to the clipboard. The file URL will have the following format:
https://storage.googleapis.com/<BUCKET_NAME>/<OBJECT_NAME>To obtain the subdomain bucket URL, refactor the file URL to
<BUCKET_NAME>.storage.googleapis.comformat.
To obtain the URI path bucket URL, removehttps://and/<BUCKET_NAME>/<OBJECT_NAME>from the file URL.
If the files in your bucket are not publicly accessible, you must change the bucket permissions. For details, refer to the Google Cloud Storage documentation.
Microsoft Azure - Blob Storage
The hostname of your Blob Storage bucket URL must have one of the following formats:
<BUCKET_NAME>.blob.core.windows.net<BUCKET_NAME>.web.core.windows.net
For Azure Blog Storage, Cloud Connector supports only subdomain URLs like <BUCKET_NAME>.blob.core.windows.net. This means that your files will be accessible directly under the root of the bucket. For example, https://example.com/index.html will map to https://<BUCKET_NAME>.blob.core.windows.net/index.html.
Get the bucket URL
- Go to the Azure portal and select your storage account.
- In the menu pane, under Settings, select Endpoints.
- Get your bucket URL from the Blob service endpoint or the Static website endpoint.
If the blob container is not configured for public access, you must change the container settings. For details, refer to the Azure Storage documentation.
Oracle Cloud Infrastructure Object Storage
Cloud Connector supports Oracle Cloud Infrastructure (OCI) Object Storage through the Amazon S3 Compatibility API.
:::caution[Public buckets only] Cloud Connector does not authenticate requests to OCI. Your bucket must allow anonymous object reads. Private buckets and pre-authenticated request URLs are not supported. :::
Enter an OCI hostname without a protocol, port, or path. Cloud Connector accepts the following formats:
| Addressing style | Hostname format | Request path |
|---|---|---|
| Path style (traditional) | <NAMESPACE>.compat.objectstorage.<REGION>.oraclecloud.com |
/<BUCKET_NAME>/<OBJECT_NAME> |
| Path style (dedicated) | <NAMESPACE>.compat.objectstorage.<REGION>.oci.customer-oci.com |
/<BUCKET_NAME>/<OBJECT_NAME> |
| Virtual-hosted style | <BUCKET_NAME>.vhcompat.objectstorage.<REGION>.oci.customer-oci.com |
/<OBJECT_NAME> |
For path-style endpoints, include the bucket name in the incoming request path. For example, https://example.com/<BUCKET_NAME>/index.html maps to the same path on the OCI endpoint.
For virtual-hosted endpoints, the bucket name is part of the hostname. An incoming request to https://example.com/index.html maps to /index.html on that bucket. OCI requires virtual-hosted bucket names to use a regional scope and a DNS-compatible name that is unique within the region.
For more information, refer to Object Storage Dedicated Endpoints, Amazon S3 Compatibility API Hosted Style Support in Object Storage, and Changing an Object Storage Bucket's Visibility.
Cloud Connector examples
import { ResourcesBySelector } from "~/components";
<ResourcesBySelector directory="rules/cloud-connector/examples/" types={["example"]} />
Route /images to an S3 Bucket using Terraform
import { Render } from "~/components";
The following example defines a single Cloud Connector rule for a zone using Terraform. The rule routes requests to /images on your domain to an AWS S3 bucket.
resource "cloudflare_cloud_connector_rules" "serve_images_in_aws" {
zone_id = "<ZONE_ID>"
rules {
description = "Route images to AWS S3 bucket"
enabled = true
expression = "http.request.full_uri wildcard \"https://<YOUR_HOSTNAME>/images/*\""
provider = "aws_s3"
parameters {
host = "<BUCKET_NAME>.s3.amazonaws.com"
}
}
}
Route /images to an S3 Bucket
import { Steps, DashButton } from "~/components";
To route requests to /images on your domain to an AWS S3 bucket:
-
In the Cloudflare dashboard, go to the Cloud Connector page.
-
Select Amazon S3 as your cloud provider.
-
Enter the bucket URL. You can structure the URL in two ways:
- Subdomain-style URL: Set the hostname to
<BUCKET_NAME>.s3.amazonaws.com. In this case, your files should be organized in the root of the bucket, meaning the URI path will map directly to the file. For example,https://<YOUR_HOSTNAME>/images/file.jpgwill map tohttps://<BUCKET_NAME>.s3.amazonaws.com/images/file.jpg. - URI path-style URL: Set the hostname to
s3.amazonaws.com. Here, your bucket must include a folder namedimages, and files should be placed inside this folder. The URI path will then include the bucket name, likehttps://<YOUR_HOSTNAME>/<BUCKET_NAME>/images/file.jpgmapping tohttps://s3.amazonaws.com/<BUCKET_NAME>/images/file.jpg.
- Subdomain-style URL: Set the hostname to
-
(Optional) Use URL Rewrite Rules to adjust the URL structure. For example, you can create a URL rewrite that changes
/imagesto/<BUCKET_NAME>/imagesto match the URI path-style URL structure. -
(Optional) Use Cache Rules to adjust the caching behavior for objects returned from the bucket. For example, you can create a cache rule that caches every returned object matching the
/images/*URI path for seven days:- If incoming requests match > Custom filter expression:
(starts_with(http.request.uri.path, "/images/")) - Cache eligibility: Eligible for cache
- Edge TTL > Ignore cache-control header and use this TTL: 7 days
- If incoming requests match > Custom filter expression:
-
Select Next and enter a descriptive name like
Route images to S3in Cloud Connector name. -
Under If, select Custom filter expression and enter the following expression:
http.request.full_uri wildcard "http*://<YOUR_HOSTNAME>/images/*"
Replace<YOUR_HOSTNAME>with desired hostname. -
Select Deploy to activate the rule.
This setup will route all traffic matching http*://<YOUR_HOSTNAME>/images/* (HTTPS and HTTP requests) to your S3 bucket. Make sure to replace <YOUR_HOSTNAME> with your actual hostname and adjust the example paths according to your setup.
Send EU visitors to a Google Cloud Storage bucket
import { Steps, DashButton } from "~/components";
To route requests from visitors in the European Union to a Google Cloud Storage bucket:
-
In the Cloudflare dashboard, go to the Cloud Connector page.
-
Select Google Cloud Storage as your cloud provider.
-
Enter the bucket URL. You can structure the URL in two ways:
- Subdomain-style URL: For
<BUCKET_NAME>.storage.googleapis.com, your files should be organized in the root of the bucket. For example,https://<YOUR_HOSTNAME>/index.htmlwill map tohttps://<BUCKET_NAME>.storage.googleapis.com/index.html. - URI path-style URL: If you set the hostname to
storage.googleapis.com, your bucket must include folders corresponding to the intended URI paths. For example, if you wanthttps://<YOUR_HOSTNAME>/eu/index.htmlto map to a file in your bucket, the file should be placed athttps://storage.googleapis.com/<BUCKET_NAME>/eu/index.html.
- Subdomain-style URL: For
-
(Optional) Use URL Rewrite Rules to adjust the URL structure. For example, you can create a URL rewrite that changes
/euto/<BUCKET_NAME>to match the URI path-style URL structure. -
(Optional) Use Cache Rules to adjust the caching behavior for objects returned from the bucket. For example, you can create a cache rule that caches every returned object matching the
/euURI path for seven days (defined through the Edge TTL setting). -
Select Next and enter a descriptive name like
Route EU visitors to GCPin Cloud Connector name. -
Under If, select Custom filter expression and enter the following expression:
(ip.src.is_in_european_union)
This expression targets traffic from European Union users. -
Select Deploy to activate the rule.
This configuration will route traffic from EU visitors to your Google Cloud Storage bucket. Make sure to replace <YOUR_HOSTNAME> with your actual hostname and adjust the example paths according to your setup.
Serve /static-assets from Azure Blob Storage
import { Steps, DashButton } from "~/components";
To serve static assets from an Azure Blob Storage container:
-
In the Cloudflare dashboard, go to the Cloud Connector page.
-
Select Microsoft Azure as your cloud provider.
-
Enter the bucket URL. Use the following URL structure:
- Subdomain-style URL: Set the hostname to
<BUCKET_NAME>.blob.core.windows.net. In this case, your bucket should include a folder namedstatic-assets, and files should be placed inside this folder. For example,https://<YOUR_HOSTNAME>/static-assets/style.csswill map tohttps://<BUCKET_NAME>.blob.core.windows.net/static-assets/style.css.
- Subdomain-style URL: Set the hostname to
-
(Optional) Use URL Rewrite Rules to adjust the URL structure. For example, you can create a URL rewrite that changes
/static-assetsto/my-pages-project/static-assetsto match the file structure of your object storage bucket. -
(Optional) Use Cache Rules to adjust the caching behavior for objects returned from the bucket. For example, you can create a cache rule that caches every returned object matching the
/static-assetsURI path for seven days (defined through the Edge TTL setting). -
Click Next and enter a descriptive name like
Serve static assets from Azurein Cloud Connector name. -
Under If, select Custom filter expression and enter the following expression:
http.request.full_uri wildcard "http*://<YOUR_HOSTNAME>/static-assets/*" -
Select Deploy to activate the rule.
This setup ensures that all traffic matching http*://<YOUR_HOSTNAME>/static-assets/* (HTTPS and HTTP requests) is served from your Azure Blob Storage container. Make sure to replace <YOUR_HOSTNAME> with your actual hostname and adjust the example paths according to your setup.
Create a compression rule via API
import { Render } from "~/components";
Use the Rulesets API to create a compression rule via API.
Basic rule settings
When creating a compression rule via API, make sure you:
- Set the rule action to
compress_response. - Define the parameters in the
action_parametersfield according to the settings you wish to override for matching requests. - Deploy the rule to the
http_response_compressionphase at the zone level.
Procedure
<Render file="rules-creation-workflow" product="rules" params={{ ruleName: "a compression rule", phaseName: "http_response_compression", }} />
Examples
For example API requests, refer to the Examples gallery.
Create a compression rule in the dashboard
import { Render, Steps, DashButton } from "~/components";
-
In the Cloudflare dashboard, go to the Rules Overview page.
-
Select Create rule > Compression Rule.
-
(Optional) Select one of the rule templates that address common use cases. Then, review and adjust the proposed rule configuration.
-
Enter a descriptive name for the rule in Rule name.
-
Under When incoming requests match, select if you wish to apply the rule to default content types (content types that Cloudflare compresses by default), or to requests that match a custom filter expression.
-
(Optional) To define a custom expression, use the Expression Builder (specifying one or more values for Field, Operator, and Value) or manually enter an expression using the Expression Editor. For more information, refer to Edit expressions in the dashboard.
-
Under Compression options, set the compression options.
-
To save and deploy your rule, select Deploy. If you are not ready to deploy your rule, select Save as Draft.
Compression Rules
import { Render } from "~/components";
Use Compression Rules to customize the compression applied to responses from Cloudflare's global network to your website visitors, based on the file extension and content type. Compression Rules are powered by the Ruleset Engine.
Cloudflare compresses some responses by default, based on the content type. With Compression Rules, you can customize the default behavior, which includes defining preferred compression algorithms for particular file types.
When a compression rule matches and lists several compression algorithms (such as gzip and Brotli), Cloudflare selects the first algorithm from your list that the visitor's browser supports. Cloudflare determines browser support from the accept-encoding HTTP request header, which browsers send automatically to indicate which compression formats they can decompress. If multiple compression rules match the same request, the last matching rule takes precedence.
<Render file="rules-requirements" product="rules" params={{ sentenceIntro: "Compression Rules require" }} />
Get started
Alternatively, follow the instructions in the following pages to get started:
Availability
Compression Rules are available in all Cloudflare plans.
| Free | Pro | Business | Enterprise | |
|---|---|---|---|---|
| Availability | Yes | Yes | Yes | Yes |
| Number of rules | 10 | 25 | 50 | 300 |
Relevant fields
The following fields are commonly used in expressions of compression rules:
| Field in Expression Builder | Field name |
|---|---|
| Media Type | http.response.content_type.media_type |
| File extension | http.request.uri.path.extension |
| N/A | raw.http.request.uri.path.extension |
Important remarks
-
If a compression rule matches but the visitor's browser does not support any of the compression algorithms configured in the rule (based on the
accept-encodingrequest header), the response will not be compressed. -
If a compression rule matches but the origin server's response includes a
cache-control: no-transformHTTP header, the compression rule will not modify the response. Origin servers use this header to indicate that intermediaries (like Cloudflare) should not alter the response body.
<Render file="troubleshoot-rules-with-trace" product="rules" params={{ rulesFeatureName: "Compression Rules" }} />
Compression Rules settings
import { Details, Badge } from "~/components";
Compression Rules support the configuration settings covered in the following sections.
Dashboard configuration settings
Enable Zstandard (Zstd) compression
Sets Zstandard as the preferred compression algorithm. If it is not supported, will automatically fall back to Brotli, Gzip, or uncompressed data.
Enable Brotli and Gzip compression
Enables Cloudflare's default compression setting. Brotli is the preferred compression algorithm. It will automatically fall back to Gzip or to uncompressed data.
Disable compression
Disables compression for matching requests. Also disables Cloudflare's default compression behavior.
Custom
Defines a custom order for compression algorithms.
Allowed values are the following:
- Gzip: Use the Gzip compression algorithm, if supported by the website visitor.
- Brotli: Use the Brotli compression algorithm, if supported by the website visitor.
- Zstandard: Use the Zstandard (Zstd) compression algorithm, if supported by the website visitor.
- Auto: Compress the response according to the algorithms supported by the website visitor (if any). Cloudflare will define the order of preference for the compression algorithms, which may change in the future. Has the same behavior of the Enable compression option.
- Default: Use Cloudflare's default compression behavior, which depends on the response content type.
If you specify only Gzip, Brotli, or Zstandard and no algorithm matches, the response will have no compression. To configure a fallback compression mechanism, add Auto to the list.
:::note The compression applied by the Default option takes into account any configured compression rules that match incoming requests. :::
API configuration settings
The configuration object supported by the compress_response action has the following format:
"action_parameters": {
"algorithms": [
{ "name": "<VALUE1>" },
{ "name": "<VALUE2>" },
// ...
]
}
The algorithms list must contain at least one item.
The supported algorithm values are:
gzip: Use the Gzip compression algorithm, if supported by the website visitor.brotli: Use the Brotli compression algorithm, if supported by the website visitor.zstd: Use the Zstandard compression algorithm, if supported by the website visitor.none: Do not use any compression algorithm.auto: Compress the response according to the algorithms supported by the website visitor (if any). Cloudflare will define the order of preference for the compression algorithms, which may change in the future.default: Use Cloudflare's default compression behavior, which depends on the response content type.
If you include none, default, or auto in the list, it must be the last value in the list.
When you specify only the gzip, brotli, or zstd algorithms, if no algorithm matches then the response will have no compression. To configure a fallback compression mechanism, add auto to the list.
For API examples, refer to the Examples gallery.
Disable Brotli compression
import { Example, TabItem, Tabs, APIRequest, Render } from "~/components";
The following example rule will disable Brotli compression for all incoming requests of a given zone. The only available compression algorithm will be Gzip.
When incoming requests match
- All incoming requests
Then
- Compression options: Custom
- Define a custom order for compression types:
Gzip
If the client does not support Gzip compression, the response will be uncompressed.
The following example sets the rules of an existing entry point ruleset (with ID {ruleset_id}) for the http_response_compression phase to a single compression rule, using the Update a zone ruleset operation:
<APIRequest path="/zones/{zone_id}/rulesets/{ruleset_id}" method="PUT" json={{ rules: [ { ref: "always_use_gzip", expression: "true", action: "compress_response", action_parameters: { algorithms: [{ name: "gzip" }], }, }, ], }} />
Disable compression for AVIF images
import { Example, TabItem, Tabs, APIRequest, Render } from "~/components";
The following example rule will disable compression for AVIF images, based on either the content type or the file extension specified in the request.
When incoming requests match
- Custom filter expression:
- Media Type equals
image/avifOR - File extension equals
avif
- Media Type equals
Then
- Compression options > Disable compression
The following example sets the rules of an existing entry point ruleset (with ID {ruleset_id}) for the http_response_compression phase to a single compression rule, using the Update a zone ruleset operation:
<APIRequest path="/zones/{zone_id}/rulesets/{ruleset_id}" method="PUT" json={{ rules: [ { ref: "disable_compression_for_avif", expression: 'http.response.content_type.media_type eq "image/avif" or http.request.uri.path.extension eq "avif"', action: "compress_response", action_parameters: { algorithms: [{ name: "none" }], }, }, ], }} />
Enable Zstandard compression for default content types
import { Example, TabItem, Tabs, APIRequest, Render } from "~/components";
The following example rule will turn on Zstandard compression for response content types where Cloudflare applies compression by default. If the client does not support Zstandard compression, it will use Brotli or Gzip compression as a fallback.
When incoming requests match
- Custom filter expression:
- Media Type is in
text/html, text/richtext, text/plain, text/css, text/x-script, text/x-component, text/x-java-source, text/x-markdown, application/javascript, application/x-javascript, text/javascript, text/js, image/x-icon, image/vnd.microsoft.icon, application/x-perl, application/x-httpd-cgi, text/xml, application/xml, application/rss+xml, application/vnd.api+json, application/x-protobuf, application/json, multipart/bag, multipart/mixed, application/xhtml+xml, font/ttf, font/otf, font/x-woff, image/svg+xml, application/vnd.ms-fontobject, application/ttf, application/x-ttf, application/otf, application/x-otf, application/truetype, application/opentype, application/x-opentype, application/font-woff, application/eot, application/font, application/font-sfnt, application/wasm, application/javascript-binast, application/manifest+json, application/ld+json, application/graphql+json, application/geo+json
- Media Type is in
Then
- Compression options: Custom
- Define a custom order for compression types:
Zstandard,Brotli,Gzip
The following example sets the rules of an existing entry point ruleset (with ID {ruleset_id}) for the http_response_compression phase to a single compression rule, using the Update a zone ruleset operation:
<APIRequest path="/zones/{zone_id}/rulesets/{ruleset_id}" method="PUT" json={{ rules: [ { ref: "use_zstd_compression", expression: '(http.response.content_type.media_type in {"text/html" "text/richtext" "text/plain" "text/css" "text/x-script" "text/x-component" "text/x-java-source" "text/x-markdown" "application/javascript" "application/x-javascript" "text/javascript" "text/js" "image/x-icon" "image/vnd.microsoft.icon" "application/x-perl" "application/x-httpd-cgi" "text/xml" "application/xml" "application/rss+xml" "application/vnd.api+json" "application/x-protobuf" "application/json" "multipart/bag" "multipart/mixed" "application/xhtml+xml" "font/ttf" "font/otf" "font/x-woff" "image/svg+xml" "application/vnd.ms-fontobject" "application/ttf" "application/x-ttf" "application/otf" "application/x-otf" "application/truetype" "application/opentype" "application/x-opentype" "application/font-woff" "application/eot" "application/font" "application/font-sfnt" "application/wasm" "application/javascript-binast" "application/manifest+json" "application/ld+json" "application/graphql+json" "application/geo+json"})', action: "compress_response", action_parameters: { algorithms: [{ name: "zstd" }, { name: "brotli" }, { name: "gzip" }], }, }, ], }} />
Use Gzip compression for CSV files
import { Example, TabItem, Tabs, APIRequest, Render } from "~/components";
The following example rule will configure Gzip compression as the preferred compression method for CSV files. If the visitor does not support this algorithm, Cloudflare will try to compress the response using a different algorithm supported by the visitor.
When incoming requests match
- Custom filter expression:
- File extension equals
csv
- File extension equals
Then
- Compression options: Custom
- Define a custom order for compression types:
Gzip,Auto
The following example sets the rules of an existing entry point ruleset (with ID {ruleset_id}) for the http_response_compression phase to a single compression rule, using the Update a zone ruleset operation:
<APIRequest path="/zones/{zone_id}/rulesets/{ruleset_id}" method="PUT" json={{ rules: [ { ref: "use_gzip_for_csv", expression: 'http.request.uri.path.extension eq "csv"', action: "compress_response", action_parameters: { algorithms: [{ name: "gzip" }, { name: "auto" }], }, }, ], }} />
Compression Rules examples
import { ResourcesBySelector } from "~/components";
<ResourcesBySelector directory="rules/compression-rules/examples/" types={["example"]} />
Use only Brotli compression for a specific path
import { Example, TabItem, Tabs, APIRequest, Render } from "~/components";
The following example rule will configure only Brotli compression for a specific URI path.
When incoming requests match
- Custom filter expression:
- URI Path equals
/download/assets.tar
- URI Path equals
Then
- Compression options: Custom
- Define a custom order for compression types:
Brotli
Since the rule configuration does not include Auto at the end of the custom algorithms list, the response will be uncompressed if the web visitor does not support Brotli.
The following example sets the rules of an existing entry point ruleset (with ID {ruleset_id}) for the http_response_compression phase to a single compression rule, using the Update a zone ruleset operation:
<APIRequest path="/zones/{zone_id}/rulesets/{ruleset_id}" method="PUT" json={{ rules: [ { ref: "use_only_brotli_for_assets_tar", expression: 'http.request.uri.path eq "/download/assets.tar"', action: "compress_response", action_parameters: { algorithms: [{ name: "brotli" }], }, }, ], }} />
Create a configuration rule via API
import { Details, Render, APIRequest } from "~/components";
Use the Rulesets API to create configuration rules via API.
Basic rule settings
When creating a configuration rule via API, make sure you:
- Set the rule action to
set_config. - Define the parameters in the
action_parametersfield according to the settings you wish to override for matching requests. - Deploy the rule to the
http_config_settingsphase at the zone level.
Procedure
<Render file="rules-creation-workflow" product="rules" params={{ ruleName: "a configuration rule", phaseName: "http_config_settings", }} />
Make sure your API token has the required permissions to perform the API operations.
Example requests
The following example sets the rules of an existing phase ruleset ({ruleset_id}) to a single configuration rule — enabling Email Obfuscation and Browser Integrity Check for the contacts page — using the Update a zone ruleset operation:
<APIRequest path="/zones/{zone_id}/rulesets/{ruleset_id}" method="PUT" json={{ rules: [ { ref: "enable_email_obfuscation_bic", expression: 'starts_with(http.request.uri.path, "/contact-us/")', description: "Obfuscates email addresses and enables BIC in contacts page", action: "set_config", action_parameters: { email_obfuscation: true, bic: true, }, }, ], }} />
The following example sets the rules of an existing phase ruleset ({ruleset_id}) to a single configuration rule — turning on Under Attack mode for the administration area — using the Update a zone ruleset operation:
<APIRequest path="/zones/{zone_id}/rulesets/{ruleset_id}" method="PUT" json={{ rules: [ { ref: "enable_under_attack_in_admin", expression: 'http.host eq "admin.example.com"', description: "Turn on Under Attack mode for admin area", action: "set_config", action_parameters: { security_level: "under_attack", }, }, ], }} />
Required API token permissions
The API token used in API requests to manage configuration rules must have at least the following permission:
- Zone > Config Rules > Edit
Create a configuration rule in the dashboard
import { Render, Steps, DashButton } from "~/components";
-
In the Cloudflare dashboard, go to the Rules Overview page.
-
Select Create rule > Configuration Rule.
-
(Optional) Select one of the rule templates that address common use cases. Then, review and adjust the proposed rule configuration.
-
Enter a descriptive name for the rule in Rule name.
-
Under When incoming requests match, select if you wish to apply the rule to all incoming requests or only to requests that match a custom filter expression.
-
(Optional) To define a custom expression, use the Expression Builder (specifying one or more values for Field, Operator, and Value) or manually enter an expression using the Expression Editor. For more information, refer to Edit expressions in the dashboard.
-
Under Then the settings are, add the configuration settings you wish to change for requests matching the rule expression.
-
To save and deploy your rule, select Deploy. If you are not ready to deploy your rule, select Save as Draft.
Configuration Rules
import { Render } from "~/components";
Configuration Rules allow you to customize certain Cloudflare configuration settings for matching incoming requests. For example, you can turn off specific features for certain URL paths or change settings based on the visitor's country.
Each configuration rule includes an expression that defines which requests the rule applies to, based on properties like URL path, hostname, or request header. For more information on expressions, refer to Expressions and Edit expressions in the dashboard.
<Render file="rules-requirements" product="rules" params={{ sentenceIntro: "Configuration Rules require" }} />
Rules templates
Availability
The number of available configuration rules varies according to your Cloudflare plan:
| Free | Pro | Business | Enterprise | |
|---|---|---|---|---|
| Availability | Yes | Yes | Yes | Yes |
| Number of rules | 10 | 25 | 50 | 300 |
Execution order
<Render file="troubleshoot-rules-with-trace" product="rules" params={{ rulesFeatureName: "Configuration Rules" }} />
Response body inspection
import { Steps } from "~/components";
When no enabled feature needs response content, Cloudflare can send data to the client as it arrives. Some features inspect or change response content at the edge.
Inspection can hold part of a response at the edge. This may increase time to first byte (TTFB) or delay incremental delivery. The amount of data held depends on the feature and response.
Features that inspect response bodies
Cloudflare features primarily inspect responses with a Content-Type of text/html. Some features change the body, while others only read it.
The following features can change HTML response bodies when turned on and applicable:
| Feature | Body change |
|---|---|
| AI Labyrinth | Adds invisible links for unauthorized AI crawlers |
| Always Online | Adds a banner to archived pages |
| Automatic HTTPS Rewrites | Rewrites eligible HTTP links to HTTPS |
| Cloudflare challenge features | Injects challenge scripts or returns challenge content |
| Cloudflare Fonts and Automatic Platform Optimization | Rewrites Google Fonts references |
| Email Address Obfuscation | Obfuscates email addresses in page content |
| Markdown for Agents | Converts HTML to Markdown for eligible requests |
| Replace insecure JavaScript libraries | Rewrites supported insecure library URLs |
| Rocket Loader | Changes script loading behavior |
| Web Analytics | Injects the Real User Monitoring beacon |
When Response Body Buffering is set to None, these features do not change the response body.
Security and AI features may also read HTML without changing it. Prefetch URLs reads URL manifests served as text/plain.
This list excludes explicit rules that inspect response content. Review each rule separately when troubleshooting.
Streaming considerations
Response body inspection most often affects progressive HTML and plain-text streams. It can affect other responses selected by explicit inspection rules. A client may receive data later or in larger groups than the origin sent it.
Set an accurate Content-Type at your origin. Do not serve streaming API responses as text/html or text/plain unless that media type is required.
The Cache-Control: no-transform response directive prevents body changes by supported features. It does not prevent read-only inspection. Refer to Cache-Control directives for other effects of this directive.
Isolate inspection issues
If a response stops streaming after you proxy it through Cloudflare:
- Verify that the origin sends data incrementally without Cloudflare.
- Check the origin response's
Content-TypeandCache-Controlheaders. - Review features and rules that apply to the response path.
- Create a path-specific Configuration Rule that sets Response Body Buffering to None.
- Test the response again through Cloudflare.
The None setting streams the body without inspection. It can prevent security, optimization, and analytics features from working on matching responses. Use the narrowest matching expression possible.
For setting values and API configuration, refer to Response Body Buffering.
Configuration Rules settings
import { Details, Render } from "~/components";
You can change the configuration settings described below in a configuration rule.
Automatic HTTPS Rewrites
Automatic HTTPS Rewrites prevents end users from seeing Mixed content errors by rewriting URLs from http to https for resources or links on your website that can be served with HTTPS.
Use this setting to turn on or off Automatic HTTPS Rewrites for matching requests.
API configuration property name: "automatic_https_rewrites" (boolean).
"action_parameters": {
"automatic_https_rewrites": true
}
Browser Integrity Check
Browser Integrity Check blocks access to pages based on specific HTTP headers commonly abused by spammers.
Use this setting to turn on or off Browser Integrity Check for matching requests.
API configuration property name: "bic" (boolean).
"action_parameters": {
"bic": true
}
Disable Real User Monitoring (RUM)
Cloudflare Web Analytics, also known as Real User Monitoring (RUM), is Cloudflare's free, privacy-first analytics for your website.
Use this setting to turn off Web Analytics for matching requests.
API configuration property name: "disable_rum" (boolean).
"action_parameters": {
"disable_rum": true
}
Disable Zaraz
Cloudflare Zaraz gives you complete control over third-party tools and services for your website, and allows you to offload them to the Cloudflare global network.
Use this setting to turn off Zaraz for matching requests.
API configuration property name: "disable_zaraz" (boolean).
"action_parameters": {
"disable_zaraz": true
}
Email Obfuscation
Email Obfuscation prevents spam by hiding email addresses from bots and harvesters while keeping them visible to human visitors to your site.
Use this setting to turn on or off Email Obfuscation for matching requests.
API configuration property name: "email_obfuscation" (boolean).
"action_parameters": {
"email_obfuscation": false
}
Fonts
Cloudflare Fonts rewrites Google Fonts to be delivered from a website's own origin, eliminating the need to rely on third-party font providers.
Use this setting to turn on or off Cloudflare Fonts for matching requests.
API configuration property name: "fonts" (boolean).
"action_parameters": {
"fonts": false
}
Hotlink Protection
Hotlink Protection prevents your images from being used by other sites, potentially reducing the bandwidth consumed by your origin server.
Use this setting to turn on or off Hotlink Protection for matching requests.
API configuration property name: "hotlink_protection" (boolean).
"action_parameters": {
"hotlink_protection": false
}
I'm Under Attack
When enabled, Under Attack mode performs additional security checks to help mitigate layer 7 DDoS attacks. Validated users access your website and suspicious traffic is blocked.
Use this setting to turn on or off Under Attack mode for matching requests.
API configuration property name: "security_level" (string).
API values: "off", "essentially_off", "under_attack".
"action_parameters": {
"security_level": "under_attack"
}
Markdown for Agents
Markdown for Agents automatically converts HTML to Markdown for requests that use content negotiation headers (Accept: text/markdown).
Use this setting to turn on or off Markdown for Agents for matching requests.
API configuration property name: "content_converter" (boolean).
"action_parameters": {
"content_converter": true
}
Opportunistic Encryption
Opportunistic Encryption allows browsers to access HTTP URIs over an encrypted TLS channel.
Use this setting to turn on or off Opportunistic Encryption for matching requests.
API configuration property name: "opportunistic_encryption" (boolean).
"action_parameters": {
"opportunistic_encryption": true
}
Polish
Cloudflare Polish is a one-click image optimization product that automatically optimizes images in your site.
Use this setting to configure Polish for matching requests:
- Off
- Lossless
- Lossy
- WebP
Refer to Compression options for more information on these values.
API configuration property name: "polish" (string).
API values: "off", "lossless", "lossy", "webp".
"action_parameters": {
"polish": "webp"
}
Request Body Buffering
Use the Request Body Buffering setting to configure the request body buffering mode for matching requests:
- Standard (default): Allows Cloudflare products to inspect a prefix of the request body when necessary for enabled functionality on your zone.
- Full: Buffers the entire request body before sending the request to your origin server.
- None: Strictly no buffering. The request body is streamed directly to the origin server without inspection.
:::caution Setting request body buffering to None may break functionality that requires body inspection. In particular, this can impact the effectiveness of the Web Application Firewall (WAF) and other security features that rely on analyzing request bodies to detect and block threats. :::
API configuration property name: "request_body_buffering" (string).
API values: "standard", "full", "none".
"action_parameters": {
"request_body_buffering": "full"
}
Response Body Buffering
Use the Response Body Buffering setting to configure the response body buffering mode for matching requests:
- Standard (default): Allows Cloudflare products to inspect a prefix of the response body when necessary for enabled functionality on your zone.
- None: Strictly no buffering. The response body is streamed directly to the client. Features that change HTML response bodies do not apply their changes.
For features that inspect response content and troubleshooting guidance, refer to Response body inspection.
:::caution Setting response body buffering to None may break functionality that requires body inspection. In particular, this can impact the effectiveness of the Web Application Firewall (WAF) and other security features that rely on analyzing response bodies to detect and block threats. :::
API configuration property name: "response_body_buffering" (string).
API values: "standard", "none".
"action_parameters": {
"response_body_buffering": "standard"
}
Rocket Loader
Rocket Loader prioritizes your website's content (such as text, images, and fonts) by deferring the loading of all your JavaScript code until after rendering.
Use this setting to turn on or off Rocket Loader for matching requests.
API configuration property name: "rocket_loader" (boolean).
"action_parameters": {
"rocket_loader": true
}
SSL
SSL/TLS encryption modes control the scheme (http:// or https://) that Cloudflare uses to connect to your origin web server and how SSL certificates presented by your origin will be validated.
Use this setting to configure the SSL/TLS encryption mode for matching requests:
- Off
- Flexible
- Full
- Strict
- Origin Pull
Refer to Available encryption modes for more information on these values.
API configuration property name: "ssl" (string).
API values: "off", "flexible", "full", "strict", "origin_pull".
"action_parameters": {
"ssl": "flexible"
}
Define a single configuration rule using Terraform
import { Render } from "~/components";
The following example defines a single configuration rule for a zone using Terraform. The rule disables Email Obfuscation and Browser Integrity Check for API requests.
# Disable a couple of Cloudflare settings for API requests
resource "cloudflare_ruleset" "http_config_rules_example" {
zone_id = "<ZONE_ID>"
name = "Config rules ruleset"
description = "Set configuration rules for incoming requests"
kind = "zone"
phase = "http_config_settings"
rules {
ref = "disable_obfuscation_bic"
description = "Disable email obfuscation and BIC for API requests"
expression = "(http.request.uri.path matches \"^/api/\")"
action = "set_config"
action_parameters {
email_obfuscation = false
bic = false
}
}
}
Configuration Rules examples
import { ResourcesBySelector } from "~/components";
<ResourcesBySelector directory="rules/configuration-rules/examples/" types={["example"]} />
Common API calls for Custom Errors
import { APIRequest } from "~/components";
The following sections provide examples of common API calls for managing custom error assets and Error Pages at the zone level.
To perform the same operations at the account level, use the corresponding account-level API endpoints.
Create a custom error asset
The following POST request creates a new custom error asset in a zone based on the provided URL:
curl "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/custom_pages/assets" \
--header "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
--json '{
"name": "500_error_template",
"description": "Standard 5xx error template page",
"url": "https://example.com/errors/500_template.html"
}'
{
"result": {
"name": "500_error_template",
"description": "Standard 5xx error template page",
"url": "https://example.com/errors/500_template.html",
"last_updated": "2025-02-10T11:36:07.810215Z",
"size_bytes": 2048
},
"success": true
}
To create an asset at the account level, use the account-level endpoint:
https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/custom_pages/assets
List custom error assets
The following GET request retrieves a list of custom error assets configured in the zone:
curl "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/custom_pages/assets" \
--header "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
{
"result": [
{
"name": "500_error_template",
"description": "Standard 5xx error template page",
"url": "https://example.com/errors/500_template.html",
"last_updated": "2025-02-10T11:36:07.810215Z",
"size_bytes": 2048
}
// ...
],
"success": true,
"errors": [],
"messages": [],
"result_info": {
"count": 2,
"page": 1,
"per_page": 20,
"total_count": 2,
"total_pages": 1
}
}
To retrieve a list of assets at the account level, use the account-level endpoint:
https://api.cloudflare.com/client/v4/accounts/$ZONE_ID/custom_pages/assets
Update a custom error asset
The following PUT request updates the URL of an existing custom error asset at the zone level named 500_error_template:
curl --request PUT \
"https://api.cloudflare.com/client/v4/zones/$ZONE_ID/custom_pages/assets/500_error_template" \
--header "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
--json '{
"description": "Standard 5xx error template page",
"url": "https://example.com/errors/500_new_template.html"
}'
{
"result": {
"name": "500_error_template",
"description": "Standard 5xx error template page",
"url": "https://example.com/errors/500_new_template.html",
"last_updated": "2025-02-10T13:13:07.810215Z",
"size_bytes": 3145
},
"success": true
}
You can update the asset description and URL. You cannot update the asset name after creation.
If you provide the same URL when updating an asset, Cloudflare will fetch the URL again, along with its resources.
To update an asset at the account level, use the account-level endpoint:
https://api.cloudflare.com/client/v4/accounts/{account_id}/custom_pages/assets/{asset_name}
Get a custom error asset
The following GET request retrieves the details of an existing custom error asset at the zone level named 500_error_template:
curl "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/custom_pages/assets/500_error_template" \
--header "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
{
"result": {
"name": "500_error_template",
"description": "Standard 5xx error template page",
"url": "https://example.com/errors/500_new_template.html",
"last_updated": "2025-02-10T13:13:07.810215Z",
"size_bytes": 3145
},
"success": true
}
To retrieve an asset at the account level, use the account-level endpoint:
https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/custom_pages/assets/$ASSET_NAME
Delete a custom error asset
The following DELETE request deletes an existing custom error asset at the zone level named 500_error_template:
curl --request DELETE \
"https://api.cloudflare.com/client/v4/zones/$ZONE_ID/custom_pages/assets/500_error_template" \
--header "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
If the request is successful, the response will have a 204 HTTP status code.
To delete an asset at the account level, use the account-level endpoint:
https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/custom_pages/assets/$ASSET_NAME
Get error page
This example obtains the current configuration for the Rate limiting block error page (with ID ratelimit_block).
<APIRequest path="/zones/{zone_id}/custom_pages/{identifier}" method="GET" parameters={{ identifier: "ratelimit_block", }} />
{
"result": {
"id": "ratelimit_block",
"description": "Rate limit Block",
"required_tokens": [],
"preview_target": "block:rate-limit",
"created_on": "2025-06-03T08:33:17.091587Z",
"modified_on": "2025-06-03T08:33:17.091587Z",
"url": null,
"state": "default"
},
"success": true,
"errors": [],
"messages": []
}
The response indicates that the page is currently set to the Cloudflare default page ("state": "default").
For a list of error page identifiers, refer to Error page types.
Update error page
This example defines a custom error page for Rate limiting block errors (with ID ratelimit_block) based on the provided URL.
<APIRequest path="/zones/{zone_id}/custom_pages/{identifier}" method="PUT" parameters={{ identifier: "ratelimit_block", }} json={{ state: "customized", url: "https://example.com/rate_limiting_block_error_page.html", }} />
{
"result": {
"id": "ratelimit_block",
"description": "Rate limit Block",
"required_tokens": [],
"preview_target": "block:rate-limit",
"created_on": "2025-06-03T08:33:17.091587Z",
"modified_on": "2025-06-03T08:35:32.639114Z",
"url": "https://example.com/rate_limiting_block_error_page.html",
"state": "customized"
},
"success": true,
"errors": [],
"messages": []
}
To set the error page back to the default page, use "state": "default" in the request body.
For a list of error page identifiers, refer to Error page types.
More resources
Create custom error rules
import { APIRequest, Render, Steps, DashButton } from "~/components";
In the dashboard
Create a custom error rule {/* create-a-custom-error-rule-dashboard */}
-
In the Cloudflare dashboard, go to the Rules Overview page.
-
Select Create rule > Custom Error Rule.
-
Enter a descriptive name for the rule in Rule name.
-
Under If incoming requests match, select one of the following options:
-
Custom filter expression: The rule will only apply to traffic matching a custom expression. Define the rule expression to configure which requests should be rewritten. Use either the Expression Builder or the Expression Editor to define the custom expression. For more information, refer to Edit expressions in the dashboard.
-
All incoming requests: The rule will apply to all responses with a
400status code or above, except for block and challenge actions issued by Cloudflare’s security products.
-
-
In Deliver a custom error response, select the response type (either Custom error asset or one of the available inline responses).
If you select Custom error asset, select an existing custom error asset in Asset, or select Create new asset to create a new custom error asset.
If you select JSON response, HTML response, Text response, or XML response, enter the custom error response you want to send to web site visitors in JSON response, HTML response, Text response, or XML response, respectively. The response can include error tokens that Cloudflare will replace with real values before sending the response to the visitor. The maximum response size is 10 KB.
-
(Optional) In Response code, enter the HTTP status code of the response (an integer value between
400and999). If provided, this value will override the current response status code. -
(Optional) Under Place at, define where to place the rule in the rules list: first rule in the list, last rule in the list, or in a custom position (after a given rule).
-
To save and deploy your rule, select Deploy. If you are not ready to deploy your rule, select Save as Draft.
Create a custom error asset {/* create-a-custom-error-asset-dashboard */}
- In the Create Custom Error Asset sidebar, enter a name for the asset in Asset name.
- (Optional) Enter a description for the asset in Description.
- In URL, enter the URL of the page you want to fetch and store in Cloudflare's global network. Cloudflare will fetch all the page resources and store a minified version of the page you can use in one or more custom error rules.
- Select Save.
To review existing custom error assets, go to Rules > Settings > Custom Error Assets tab.
Via API
To configure a custom error rule via API:
- (Optional) Create a custom error asset based on a URL you provide.
- Create a custom error rule in the
http_custom_errorsphase, using the Rulesets API.
Create a custom error asset {/* create-a-custom-error-asset-api */}
The following POST request creates new a custom error asset in a zone based on the provided URL:
curl "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/custom_pages/assets" \
--header "Authorization: Bearer <API_TOKEN>" \
--json '{
"name": "500_error_template",
"description": "Standard 5xx error template page",
"url": "https://example.com/errors/500_template.html"
}'
{
"result": {
"name": "500_error_template",
"description": "Standard 5xx error template page",
"url": "https://example.com/errors/500_template.html",
"last_updated": "2025-02-10T11:36:07.810215Z",
"size_bytes": 2048
},
"success": true
}
Create a custom error rule {/* create-a-custom-error-rule-api */}
When creating a custom error rule via API, make sure you:
- Set the rule action to
serve_error. - Define the rule parameters in the
action_parametersfield according to response type. - Deploy the rule to the
http_custom_errorsphase.
The first rule in the http_custom_errors phase ruleset that matches will be applied. No other rules in the ruleset will be matched or applied. Additionally, custom error rules defined at the zone level will have priority over rules defined at the account level.
General procedure
Follow this workflow to create a custom error rule for a given zone via API:
-
Use the List zone rulesets operation to check if there is already a ruleset for the
http_custom_errorsphase at the zone level. -
If the phase ruleset does not exist, create it using the Update a zone entry point ruleset operation, which allows you to create a ruleset if it does not exist and update all the rules in the ruleset. Create the ruleset in the
http_custom_errorsphase.If the phase ruleset already exists, use the Update a zone entry point ruleset operation to replace all the rules in the ruleset, or the Add a rule to a ruleset operation to add a rule to the existing rules in the ruleset.
To create a custom error rule at the account level, use the corresponding account-level API endpoints.
Example
This example configures a custom error rule returning a previously created custom error asset named 500_error_template for responses with a 500 HTTP status code.
<APIRequest path="/zones/{zone_id}/rulesets/phases/{ruleset_phase}/entrypoint" method="PUT" parameters={{ ruleset_phase: "http_custom_errors", }} json={{ rules: [ { ref: "serve_500_template", action: "serve_error", action_parameters: { asset_name: "500_error_template", content_type: "text/html", }, expression: "http.response.code eq 500", enabled: true, }, ], }} />
This PUT request, corresponding to the Update a zone entry point ruleset operation, replaces any existing rules in the http_custom_errors phase entry point ruleset.
Required API token permissions
The API token used in API requests to manage Custom Error Rules and Custom Error Assets must have at least the following permission:
- Custom Error Rules > Edit
Edit Error Pages
import { Render, Details, DashButton, Steps, Tabs, TabItem, } from "~/components";
You can define custom Error Pages for the following errors and challenges:
For more information on the different types of Error Pages, refer to Error page types.
To return custom error responses for requests that match specific conditions, use Custom Error Rules instead.
1. Design your custom error page
Before defining a custom error page in your Cloudflare account, you will need to design and code that page. It can be hosted on your own web server or using a Cloudflare product like Snippets.
When designing your custom error page, you can include page-specific custom error tokens. Each custom error token provides diagnostic information that appears on the error page.
To display a custom page for each error, create a separate page per error. For example, to create a custom error page for both IP/Country Block and WAF block, you must design and publish two separate pages.
:::note[Notes]
- Your custom error page should include a page-specific custom error token if applicable and cannot exceed 1.5 MB (1,500,000 bytes). Also, it must include HTML
<head>and</head>tags. - Make sure that the
referrermeta tag is not present in your custom error page's HTML code since it will disrupt Cloudflare challenges:<meta name="referrer" (...) />
:::
You can use the following template to start building your error page:
<html>
<head></head>
<body>
::[REPLACE WITH CUSTOM ERROR TOKEN NAME]::
</body>
</html>
The following HTML code is an example error page for 5XX errors without styling:
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>5XX Level Errors page</title>
</head>
<body>
<h1>5XX Level Errors</h1>
<h2>::CLOUDFLARE_ERROR_500S_BOX::</h2>
</body>
</html>
2. Update an error page in the dashboard
You can define an error page at the zone level or for your entire account. Zone-level error pages have priority over account-level error pages.
To edit a zone-level custom error page:
-
In the Cloudflare dashboard, go to the Error Pages page.
-
Identify your desired custom error page type.
-
(Optional) To preview the current error page (default or custom), select the link in the Show column.
-
To edit the error page, select the three dots > Edit next to the page type you previously identified.
-
To use Cloudflare's default page, select Cloudflare default page. To provide a custom error page, select Custom page and enter the URL of the custom error page you created.
-
Select Confirm.
To update an account-level custom error page:
-
In the Cloudflare dashboard, go to the Settings page.
-
Go to Error Pages and identify your desired custom error page type.
-
(Optional) To preview the current error page (default or custom), select the link in the Show column.
-
To edit the error page, select the three dots > Edit next to the page type you previously identified.
-
To use Cloudflare's default page, select Cloudflare default page. To provide a custom error page, select Custom page and enter the URL of the custom error page you created.
-
Select Confirm.
Fetch custom error page again
After successfully setting the content of the custom error page in Error Pages, you can remove the page from your origin server.
If in the future, you need to update your custom error page, you must fetch the page again, even if the page URL remains unchanged. In this case, next to the page type you want to update, select the three dots > Fetch custom page again.
Example custom error rules
import { Tabs, TabItem, APIRequest, Render } from "~/components";
The provided examples use the following fields in their rule expressions:
-
http.response.code(Response Status Code): Represents the HTTP status code returned to the client, either set by a Cloudflare product or returned by the origin server. Use this field to customize the response for error codes returned by the origin server or by a Cloudflare product such as a Worker. -
cf.response.1xxx_code: Contains the specific error code for Cloudflare-generated errors. This field will only work for Cloudflare-generated errors such as 52X and 1XXX.
Custom JSON response for all 5XX errors
This example configures a custom JSON error response for all 5XX errors (500-599) in a zone. The HTTP status code of the custom error response will be set to 530.
Custom error rule configuration:
-
Name:
Custom JSON response for all 5XX errors -
If incoming requests match > Custom filter expression:
Field Operator Value Logic Response Status Code greater than or equal to 500And Response Status Code less than or equal to 599If using the Expression Editor:
(http.response.code ge 500 and http.response.code le 599) -
Response type: JSON response
-
Response code:
530 -
JSON response:
{"message": "A server error occurred."}
<APIRequest path="/zones/{zone_id}/rulesets/phases/{ruleset_phase}/entrypoint" method="PUT" parameters={{ ruleset_phase: "http_custom_errors", }} json={{ rules: [ { ref: "json_response_for_5xx_errors", action: "serve_error", action_parameters: { content: '{"message": "A server error occurred."}', content_type: "application/json", status_code: 530, }, expression: "http.response.code ge 500 and http.response.code lt 600", enabled: true, }, ], }} />
This PUT request, corresponding to the Update a zone entry point ruleset operation, replaces any existing rules in the http_custom_errors phase entry point ruleset.
Custom HTML response with updated status code
This example configures a custom HTML error response for responses with a 500 HTTP status code, and redefines the response status code to 503.
Custom error rule configuration:
-
Name:
Custom HTML response for 500 errors -
If incoming requests match > Custom filter expression:
Field Operator Value Response Status Code equal to 500If using the Expression Editor:
(http.response.code eq 500) -
Response type: HTML response
-
Response code:
503 -
HTML response:
<!DOCTYPE html><html><head><meta charset="utf-8"><title>Application unavailable</title></head><body><h1>Application temporarily unavailable</h1><p>Please try again later.</p></body></html>
<APIRequest path="/zones/{zone_id}/rulesets/phases/{ruleset_phase}/entrypoint" method="PUT" parameters={{ ruleset_phase: "http_custom_errors", }} json={{ rules: [ { ref: "html_response_500_to_503", action: "serve_error", action_parameters: { content: '
Application temporarily unavailable
Please try again later.
', content_type: "text/html", status_code: 503, }, expression: "http.response.code eq 500", enabled: true, }, ], }} />This PUT request, corresponding to the Update a zone entry point ruleset operation, replaces any existing rules in the http_custom_errors phase entry point ruleset.
Custom HTML response for Cloudflare 1020 errors
This example configures a custom HTML error response for Cloudflare error 1020 (Access Denied).
Custom error rule configuration:
-
Name:
Custom HTML response for 1020 errors -
If incoming requests match > Custom filter expression
Use the Expression Editor:
(cf.response.1xxx_code eq 1020) -
Response type: HTML response
-
HTML response:
<!DOCTYPE html><html><head><meta charset="utf-8"><title>Access denied</title></head><body><h1>You do not have access to this page</h1><p>Contact us if you think this is an error.</p></body></html>
<APIRequest path="/zones/{zone_id}/rulesets/phases/{ruleset_phase}/entrypoint" method="PUT" parameters={{ ruleset_phase: "http_custom_errors", }} json={{ rules: [ { ref: "html_response_cf_1020", action: "serve_error", action_parameters: { content: '
You do not have access to this page
Contact us if you think this is an error.
', content_type: "text/html", }, expression: "cf.response.1xxx_code eq 1020", enabled: true, }, ], }} />This PUT request, corresponding to the Update a zone entry point ruleset operation, replaces any existing rules in the http_custom_errors phase entry point ruleset.
Custom error asset created from a URL
This example configures a custom error rule returning a previously created custom error asset named 500_error_template for responses with a 500 HTTP status code.
Custom error rule configuration:
-
Name:
Serve asset for HTTP 500 errors -
If incoming requests match > Custom filter expression:
Field Operator Value Response Status Code equal to 500If using the Expression Editor:
(http.response.code eq 500) -
Response type: Custom error asset
-
Asset:
500_error_template
<APIRequest path="/zones/{zone_id}/rulesets/phases/{ruleset_phase}/entrypoint" method="PUT" parameters={{ ruleset_phase: "http_custom_errors", }} json={{ rules: [ { ref: "serve_error_500_asset", action: "serve_error", action_parameters: { asset_name: "500_error_template", content_type: "text/html", }, expression: "http.response.code eq 500", enabled: true, }, ], }} />
This PUT request, corresponding to the Update a zone entry point ruleset operation, replaces any existing rules in the http_custom_errors phase entry point ruleset.
Custom Errors
import { Render, Steps } from "~/components";
Use Custom Errors to replace default Cloudflare error pages with your own custom content. Custom error content is shown to visitors when an HTTP error occurs, whether the error comes from your origin server, a Cloudflare product (including Cloudflare Workers), or a security challenge.
You can configure custom error content using the following methods:
- Error Page: An HTML page shown to website visitors when a specific error occurs (refer to the different error page types) or when showing a security challenge. Error Pages can be defined at the zone level and at the account level on paid plans, with zone-level configurations taking precedence.
- Custom Error Rule: Defines the conditions under which Cloudflare will serve a custom error response to visitors in case of HTTP errors (status codes
400and above), and the exact content that will be served. A matching custom error rule has priority over an Error Page configured at the account or at the zone level that would apply to the same error.
Custom Errors require that you proxy the DNS records of your domain (or subdomain) through Cloudflare.
How it works
Cloudflare has a set of default pages for presenting errors and challenges to your website visitors. You can customize those pages using Error Pages and Custom Error Rules.
When an error of a specific type occurs, Cloudflare does the following:
- Search for a configured Error Page at the account level for the specific error.
- Search for a configured Error Page at the zone level for the specific error (it will have priority over the account-level Error Page, if any).
- Search for a matching custom error rule at the account level. The rule will have priority over 500 class and 1000 class Error Pages at the account or zone level.
- Search for a matching custom error rule at the zone level. The rule will have priority over 500 class and 1000 class Error Pages at the account or zone level and over custom error rules at the account level.
- If a security rule like a WAF custom rule or a rate limiting rule triggers a custom block response instead of a default Cloudflare WAF block page, the rule-specific block response will have priority over Error Pages or a matching custom error rule.
- If any of the previous configurations apply, serve the custom error content to the visitor. If not, serve the default error page for the specific error type in the format the client requested (JSON, Markdown, or HTML). For more details on structured error responses (JSON or Markdown), refer to Error responses.
:::note To customize a challenge page or the default Cloudflare WAF block page, use an Error Page. Custom Error Rules do not apply to security actions originating from Cloudflare products (such as the default WAF block page) — they only override error responses from your origin server or custom WAF responses. A custom WAF response configured directly in a WAF rule action takes precedence over both an Error Page and a custom error rule. :::
Availability
Custom Errors are available to all paid plans. The exact features depend on your Cloudflare plan.
| Free | Pro | Business | Enterprise | |
|---|---|---|---|---|
| Availability | No | Yes | Yes | Yes |
| Number of rules | 0 | 25 | 50 | 300 |
| Number of assets | 0 | 25 | 50 | 300 |
| Error Pages | No | Yes | Yes | Yes |
| Origin Error Pages | No | No | No | Yes |
Error Pages
Cloudflare uses a wide range of error codes to identify issues in handling request traffic. By default, these error pages mention Cloudflare; however, you can create custom error pages to provide a consistent brand experience for your users.
Error Pages do not apply to responses with an HTTP status code of 500, 501, 503, or 505. These exceptions help avoid issues with specific API endpoints and other web applications. You can still customize responses for these status codes using Custom Error Rules.
If you are on a Cloudflare paid plan, you can create custom error pages at the zone level or for your entire account. Zone-level error pages have priority over account-level error pages.
Additionally, Enterprise customers can customize 5XX error pages (except errors 520-527) at their origin by turning on Origin Error Pages in Error Pages in the dashboard.
You can design custom error pages to appear during a security challenge or when an error occurs. For more information on the different error page types, refer to Error page types.
:::note
Cloudflare will return the default Cloudflare error page instead of your custom Error Pages if the incoming request does not contain an accept-encoding header. This does not apply to responses originating from Custom Error Rules.
:::
Custom Error Rules
A custom error rule defines the conditions under which Cloudflare will serve custom error content to visitors in case of HTTP errors (status codes 400 and above), and the exact content that will be served to visitors.
When defining the content to serve, you provide either an inline response or the URL of an existing webpage. The URL can point to a webpage or to a different resource such as JSON content.
When you provide a URL, Cloudflare will gather any required images, CSS, and JavaScript code and save a minified version of the full page in the Cloudflare global network. This resource is called a custom error asset, which you can use in one or more custom error rules in the same scope of the asset (zone or account).
When a custom error rule is triggered, Cloudflare will replace the body with the response you previously defined and (optionally) the response HTTP status code sent to the visitor. Cloudflare will keep any existing HTTP response headers except for Content-Type and Content-Length.
Additionally, you can configure Response Header Transform Rules for error responses to add, change, or remove HTTP headers from the response.
Custom error rules have priority over Error Pages.
Custom Error Assets
A custom error asset corresponds to a web resource such as an HTML web page (including any referenced images, CSS, and JavaScript code) that Cloudflare fetches and saves based on a URL you provide, to be served to visitors as an error page.
Once the custom error asset is stored in Cloudflare's global network, the URL you initially provided no longer needs to be available. You can update an existing custom error asset by fetching it again. The metadata associated with each custom error asset includes the timestamp when the last fetch occurred, and this information is displayed in the dashboard.
You can use a custom error asset in one or more custom error rules in the same scope where you defined the asset (zone or account).
Size limits
When you provide a URL for a custom error asset, Cloudflare fetches the page and inlines all referenced resources into the HTML. Images and other binary resources (including those referenced from CSS via url(...)) are inlined as base64-encoded data URLs. CSS and JavaScript files are inlined as plain text inside <style> and <script> tags. The processed page must not exceed approximately 1.5 MB.
If your custom error asset exceeds this size, reduce the number or size of referenced resources. You can also host large resources externally, as long as they remain accessible from Cloudflare's network when the asset is fetched.
:::caution[Large assets increase bandwidth]
Because a custom error asset is served to every visitor that triggers the associated rule, a large asset can generate significant egress traffic. To reduce bandwidth and improve load times, keep the asset small by reducing the size or number of referenced images, fonts, and other binary resources. Cloudflare inlines referenced resources when fetching the asset, so external URLs are fetched and inlined as well.
:::
Troubleshoot Error Pages issues
If Cloudflare cannot load your site or you have blocked the United States (US) via IP Access rules or WAF custom rules, publishing and previewing a custom error page might not work.
A common error might look like the following: Error fetching page: Fetch failed, https://example.com/ipcountryblock.html returned 403 (Code: 1202).
Make sure that no WAF rule is blocking or challenging Custom Errors product when it is fetching the content of your custom error page.
Error pages for blocked requests
If you block countries or IP addresses with an IP Access rule, affected visitors will get a 1005 error and your IP/Country Block custom page.
If you block countries or IP addresses with a WAF custom rule and you do not configure a custom error rule or a WAF custom response for blocked requests, affected visitors will get your WAF Block page.
If you block requests due to a rate limiting rule and you do not configure a custom error rule or a WAF custom response for blocked requests, affected visitors will get your 429 Errors page displaying a Cloudflare 1015 error.
If you block countries or IP addresses with a firewall rule (now deprecated), affected visitors will get your 1000 Class Errors page.
1XXX errors
You cannot customize the following 1XXX errors via Error Pages:
1001- Unable to resolve1003- Bad Host header1018- Unable to resolve because of ownership lookup failure1023- Unable to resolve because of feature lookup failure
Custom error page size
Your custom error page cannot be blank and the combined size of all page assets cannot exceed 1.5 MB (1,500,000 characters). To avoid exceeding the custom error page limit, preview your page to check its size before publishing.
General troubleshooting advice
If you encounter errors while attempting to preview or publish your custom error page, use an HTML validator to ensure that your code resolves properly.
More resources
Error page types
| Page type | Description | API identifier |
|---|---|---|
| WAF block | The page displayed when visitors are blocked by a Web Application Firewall rule. This page returns a 403 status code. |
waf_block |
| IP/Country block | The page displayed when a request originates from a blocked IP address or country. This page returns a 403 status code. |
ip_block |
| IP/Country challenge | Presents a challenge to visitors from specified IP addresses or countries. This page returns a 403 status code. For more information, refer to IP Access rules. |
country_challenge |
| 500 class errors | 500 class error pages are displayed when a web server is unable to process a request. For more information, refer to Cloudflare 5xx errors. | 500_errors |
| 1000 class errors | 1000 class error pages are displayed when a domain’s configuration, security settings, or origin setup prevents Cloudflare from completing a request. For more information, refer to Cloudflare 1xxx errors. | 1000_errors |
| Managed challenge / I'm Under Attack Mode | Presents different types of challenges to a visitor depending on the nature of their request and your security settings. This page returns a 403 status code. For more information, refer to Under Attack mode. |
managed_challenge |
| Rate limiting block | Displayed to visitors when they have been blocked by a rate limiting rule. This page returns a 429 status code. |
ratelimit_block |
Error tokens
Each custom error token provides diagnostic information or specific functionality that appears on the error page. Certain error pages require a page-specific custom error token.
To display a custom page for each error, create a separate page per error. For example, to create an error page for both IP/Country Block and Interactive Challenge, you must design and publish two separate pages.
The following custom error tokens are required by their respective error pages:
| Token | Required for |
|---|---|
::CAPTCHA_BOX:: |
Interactive Challenge Country Challenge (Managed Challenge) Managed Challenge / I'm Under Attack Mode (Interstitial Page) |
::IM_UNDER_ATTACK_BOX:: |
Non-Interactive Challenge |
::CLOUDFLARE_ERROR_500S_BOX:: |
5XX Errors |
::CLOUDFLARE_ERROR_1000S_BOX:: |
1XXX Errors |
Each custom error token has a default look and feel. However, you can use CSS to stylize each custom error tag using each tag's class ID. All the external resources like images, CSS, and scripts will be inlined during the process. As such, all external resources need to be available (that is, they must return 200 OK) otherwise an error will be thrown.
For Custom Error Assets, inline responses, and Error Pages
A custom error asset, inline response, or error page may also include the following error tokens, which will be replaced with their real values before sending the response to the visitor:
| Token | Description |
|---|---|
::CLIENT_IP:: |
The visitor's IP address. |
::RAY_ID:: |
A unique identifier given to every request that goes through Cloudflare. |
::GEO:: |
The country or region associated with the visitor's IP address. |
Reference
import { DirectoryListing } from "~/components";
This section contains reference information for Custom Errors.
Custom Errors parameters
import { Type, MetaInfo } from "~/components";
Custom error rules
Custom error rules define when a custom error gets triggered and the content that is served to visitors. Rule parameters are the following:
Response type
API name: N/A (handled via asset_name and content_type parameters)
The content type of the inline response to send to the website visitor (JSON, HTML, Text, or XML), or Custom error asset if sending the content of a custom error asset.
When using the API you must either set the asset_name or set both the content_type and content parameters. Refer to JSON response / HTML response / Text response / XML response.
Response code
API name: status_code
The HTTP status code of the response. If provided, this value will override the current response status code.
The status code must be between 400 and 999.
Asset
API name: asset_name
The name of the custom error asset you previously uploaded (in the dashboard, you can create an asset when creating the rule). The asset may include error tokens that will be replaced with real values before sending the error response to the visitor.
A custom error rule can only reference an asset defined in the same scope as the rule (that is, in the same zone or account).
In the dashboard, this parameter is only available when you select Custom error asset in Response type.
When using the API, you must provide either the asset_name or the content parameter.
JSON response / HTML response / Text response / XML response {/* response */}
API names: content and content_type
The response body to return. It can include error tokens that will be replaced with real values before sending the error response to the visitor.
You must provide either the asset_name or the content parameter.
The maximum content size is 10 KB.
When using the API you must also set the content_type parameter, which defines the content type of the returned response. The value must be one of the following:
text/htmltext/plainapplication/jsontext/xml
:::caution
If you create an HTML error response, make sure the referrer meta tag is not present in the HTML code since it will disrupt Cloudflare challenges:
<meta name="referrer" (...) />
:::
Custom error assets
A custom error asset corresponds to a web resource such as an HTML web page (including any referenced images, CSS, and JavaScript code) that Cloudflare fetches and saves based on a URL you provide, to be served to visitors as an error page.
Custom error assets have the following parameters:
Asset name
API name: name
The name of the custom error asset. Example value: "500_error_template".
An asset name can contain the following characters:
- Uppercase and lowercase letters (`A-Z` and `a-z`)
- Numbers (`0-9`)
- The underscore (`_`) character
The maximum length is 200 characters.
Description
API name: description
A string describing the custom error asset. Example value: "Standard 5xx error template page".
Asset address
API name: url
The URL of the page you want Cloudflare to fetch and store, to be served later to visitors as error pages according to the configured custom error rules. Example value: "https://example.com/errors/500.html".
When you create or update an asset and provide a URL, Cloudflare collects any images, CSS, and JavaScript code used in the page, minifies the content, and saves it internally.
The content of the page at the specified URL may include error tokens that will be replaced with real values before sending the error response to the visitor.
When using the dashboard, you can later trigger another fetch to get the latest version of the page along with its resources, and store it internally.
When using the API, if you update an asset and provide the same URL, Cloudflare will fetch the URL again, along with its resources, and store it internally.
The maximum asset size is 1.5 MB.
URL normalization examples
The following table shows how different URL normalization settings affect request URLs before they pass to other Cloudflare features and to the origin server:
| Incoming URL | Normalization type | Normalize incoming URLs | Normalize URLs to origin | URL at Cloudflare's network | URL passed to origin server |
|---|---|---|---|---|---|
www.example.com/hello |
(any) | Off | Off | www.example.com/hello |
www.example.com/hello |
www.example.com/hello |
(any) | On | Off | www.example.com/hello |
www.example.com/hello |
www.example.com/hello |
(any) | On | On | www.example.com/hello |
www.example.com/hello |
example.com/%68ello |
(any) | Off | Off | example.com/%68ello |
example.com/%68ello |
example.com/%68ello |
(any) | On | Off | example.com/hello |
example.com/%68ello |
example.com/%68ello |
(any) | On | On | example.com/hello |
example.com/hello |
example.com/%68ello//pa\th |
RFC-3986 | Off | Off | example.com/%68ello//pa\th |
example.com/%68ello//pa\th |
example.com/%68ello//pa\th |
RFC-3986 | On | Off | example.com/hello//pa%5Cth |
example.com/%68ello//pa\th |
example.com/%68ello//pa\th |
RFC-3986 | On | On | example.com/hello//pa%5Cth |
example.com/hello//pa%5Cth |
example.com/%68ello//pa\th |
Cloudflare | Off | Off | example.com/%68ello//pa\th |
example.com/%68ello//pa\th |
example.com/%68ello//pa\th |
Cloudflare | On | Off | example.com/hello/pa/th |
example.com/%68ello//pa\th |
example.com/%68ello//pa\th |
Cloudflare | On | On | example.com/hello/pa/th |
example.com/hello/pa/th |
example.com/hello//../path |
RFC-3986 | On | On | example.com/hello/path |
example.com/hello/path |
example.com/hello//../path |
Cloudflare | On | On | example.com/path |
example.com/path |
example.com/hello/\../path |
RFC-3986 | On | On | example.com/hello/%5C../path |
example.com/hello/%5C../path |
example.com/hello/\../path |
Cloudflare | On | On | example.com/path |
example.com/path |
How URL normalization works
import { Steps } from "~/components";
URL normalization modifies separators, encoded elements, and literal bytes in incoming URLs so that they conform to a consistent formatting standard.
For example, consider a WAF custom rule that blocks requests whose URLs match www.example.com/hello. The rule would not block a request containing an encoded element — www.example.com/%68ello. Normalizing incoming URLs on the Cloudflare global network helps simplify rules expressions containing URLs.
The two available types of URL normalization are:
The location where URL normalization will occur depends on the configured settings.
For examples of the different settings and their impact on request URLs, refer to the URL normalization examples.
RFC 3986 normalization
The URL normalization performed according to RFC 3986 is as follows:
- The following unreserved characters are percent decoded (converted from their
%XXencoded form back to the original character):- Alphabetical characters:
a-z,A-Z(decoded from%41-%5Aand%61-%7A) - Digit characters:
0-9(decoded from%30-%39) - hyphen
-(%2D), period.(%2E), underscore_(%5F), and tilde~(%7E)
- Alphabetical characters:
- These reserved characters are not encoded or decoded:
: / ? # [ ] @ ! $ & ' ( ) * + , ; = - Other characters, for example literal byte values, are percent encoded.
- Percent encoded representations are converted to upper case.
- URL paths are normalized according to the Remove Dot Segments protocol.
Cloudflare normalization
When using the Cloudflare URL normalization, some extra normalization techniques will be applied to URLs of incoming requests, in the following order:
- Normalize back slashes (
\) into forward slashes (/). - Merge successive forward slashes (for example,
//will be normalized to/). - Perform RFC 3986 normalization of the resulting URL.
URL normalization
import { GlossaryTooltip, Render } from "~/components";
Cloudflare provides a URL normalization feature to modify the URLs of incoming requests so that they conform to a consistent formatting standard. This is important because the same resource can be requested using different URL formats (for example, /hello and /%68ello refer to the same path), and without normalization, security rules and other features might not match all variations of a URL.
When you enable URL normalization, all incoming URLs are normalized before they pass to subsequent global network features that accept a URL input, such as WAF custom rules, Workers, and Access. Rule expressions that filter traffic based on URLs will therefore trigger correctly, regardless of the format of the incoming URL. When URL normalization is disabled, Cloudflare forwards the URL to origin in its original form.
:::caution
When traffic is proxied through Cloudflare, certain baseline URL normalization steps are always applied, even if you turn off URL normalization for your zone. For example, Cloudflare always converts two or more adjacent slashes into a single slash (such as //page to /page). You cannot disable these baseline normalizations.
:::
URL normalization does not perform any redirects, and therefore it will not change the address displayed in the visitor's browser. The normalization operation, when enabled, occurs on the global network and affects Cloudflare features executed later and (optionally) the URL received at the origin server.
<Render file="rules-requirements" product="rules" params={{ sentenceIntro: "URL normalization requires" }} />
Availability
URL normalization is available in all Cloudflare plans.
Get started
Learn more about URL normalization and how to configure URL normalization in the Cloudflare dashboard.
Configure URL normalization in the dashboard
import { Steps, DashButton } from "~/components";
-
In the Cloudflare dashboard, go to the Rules Settings page.
-
Go to the URL Normalization tab.
-
Configure the available URL normalization settings.

URL normalization settings
The Cloudflare dashboard provides the following settings to manage URL normalization:
Normalization type
Default value: RFC-3986
Selects the type of normalization to perform:
- RFC-3986 – Applies URL normalization strictly according to RFC 3986.
- Cloudflare – In addition to what is defined in RFC 3986, applies extra URL normalization techniques.
Normalize incoming URLs
Default value: On
Configures the URLs of all incoming traffic to Cloudflare:
- When enabled, all incoming URLs are normalized before they pass to subsequent Cloudflare features that can receive a URL as input, such as Page Rules, WAF custom rules, Workers, and Access.
- When disabled, incoming URLs are not normalized before passing to subsequent Cloudflare features.
Normalize URLs to origin
Default value: Off
Configures URLs sent to the origin:
- When enabled, requests sent to the origin are normalized.
- When disabled, requests sent to the origin are not modified.
You can only view and enable this option when Normalize incoming URLs is enabled.
For examples of how these settings affect URL normalization, refer to the URL normalization examples.
Create an origin rule via API
import { Details, Render, APIRequest } from "~/components";
Use the Rulesets API to create origin rules via API.
Basic rule settings
When creating an origin rule via API, make sure you:
- Set the rule action to
route. - Define the parameters in the
action_parametersfield according to the type of origin override. - Deploy the rule to the
http_request_originphase at the zone level.
Procedure
<Render file="rules-creation-workflow" product="rules" params={{ ruleName: "an origin rule", phaseName: "http_request_origin" }} />
Make sure your API token has the required permissions to perform the API operations.
Example requests
The following example sets the rules of an existing phase ruleset ($RULESET_ID) to a single origin rule — overriding the SNI value of incoming requests addressed at admin.example.com — using the Update a zone ruleset operation.
<APIRequest path="/zones/{zone_id}/rulesets/{ruleset_id}" method="PUT" json={{ rules: [ { ref: "override_sni_for_admin", expression: 'http.host eq "admin.example.com"', description: "SNI Override for the admin area", action: "route", action_parameters: { sni: { value: "sni.example.com", }, }, }, ], }} />
Required API token permissions
The API token used in API requests to manage origin rules must have at least the following permission:
- Zone > Origin Rules > Edit
Create an origin rule in the dashboard
import { Render, Steps, DashButton } from "~/components";
-
In the Cloudflare dashboard, go to the Rules Overview page.
-
Select Create rule > Origin Rule.
-
(Optional) Select one of the rule templates that address common use cases. Then, review and adjust the proposed rule configuration.
-
Enter a descriptive name for the rule in Rule name.
-
Under When incoming requests match, define the rule expression.
-
Under Set origin parameters, define the origin rule settings you wish to change for requests matching the rule expression.
-
To save and deploy your rule, select Deploy. If you are not ready to deploy your rule, select Save as Draft.
Origin Rules FAQ
import { Details, Example } from "~/components";
Below you will find answers to the most commonly asked questions regarding Origin Rules.
What happens if I use both an origin rule and a page rule to perform a Host header/DNS record override?
In this situation the origin rule parameters will override the page rule parameters.
Consider the following example scenarios:
- A page rule defines a Host header override, but not a resolve override (or DNS record override). An origin rule defines a DNS record override, but not a Host header override. The resulting request will have the
Hostheader defined by the page rule and the origin hostname defined by the origin rule. - A page rule defines a Host header override, and an origin rule also defines a Host header override. The resulting request will have the
Hostheader defined by the origin rule.
Will Cloudflare automatically migrate my Page Rules with Host header and DNS record overrides to origin rules?
Yes. Refer to the Page Rules migration guide for any updates on the migration process.
What happens if more than one origin rule matches the current request?
If two or more origin rules match a request, the configuration of those rules is merged. While merging two configurations, the settings of later rules will override the settings defined in previous rules, updating or adding configuration properties. The final configuration applied by Cloudflare will be this merged version.
For example, if you configure the following two origin rules and both rules match, Cloudflare will use the destination port set by the first rule, and the DNS hostname override and Host header value set by the second rule.
Origin rule #1
| Parameter | Value |
|---|---|
Set Host header |
example.com |
| Set destination port | 8081 |
Origin rule #2
| Parameter | Value |
|---|---|
Set Host header |
example.net |
| Set DNS hostname | example.net |
When using the API, you configure origin rule parameters in an action_parameters object.
{
"rules": [
{
"expression": "http.request.uri.query contains \"/eu/\"",
"description": "Origin rule #1",
"action": "route",
"action_parameters": {
"host_header": "example.com",
"origin": {
"port": 8081
}
}
},
{
"expression": "http.request.uri.query contains \"/eu/\"",
"description": "Origin rule #2",
"action": "route",
"action_parameters": {
"host_header": "example.net",
"origin": {
"host": "example.net"
}
}
}
]
}
The merged configuration to apply would be the following:
| Parameter | Value |
|---|---|
Set Host header |
example.net |
| Set destination port | 8081 |
| Set DNS hostname | example.net |
If you also configured a destination port in rule #2, that value would override the 8081 destination port defined in rule #1.
Origin Rules settings
import { Example } from "~/components";
The following sections describe the available settings in Origin Rules.
Host header
Allows you to rewrite the HTTP Host header of incoming requests. The Host header tells the destination server which website or application the request is intended for.
A common use case for this functionality is when your content is hosted on a third-party server that only accepts Host headers with their own server names. In this situation, you must update the Host HTTP header in incoming requests from Host: example.com to Host: thirdpartyserver.example.net.
:::note[Notes]
-
In most situations, when you rewrite the HTTP
Hostheader you also need to configure a DNS record override. TheHostheader override only updates the header value; the DNS record override will handle the rerouting of the request. -
An origin rule performing a
Hostheader override will also update the Server Name Indication (SNI) value of the original request to the same value. To set an SNI value different from theHostheader value, add an SNI override in the same origin rule or create a separate origin rule for this purpose. -
If you have configured load balancing through Cloudflare and you wish to override the HTTP
Hostheader per origin or for a given monitor, refer to Override HTTP Host headers in the Load Balancing documentation for more information. -
When an origin rule is configured to override the
Hostheader for a request handled by a Worker, this override is not taken into account when evaluating Workers routes.
:::
Server Name Indication (SNI)
Allows you to override the Server Name Indication (SNI) [^1] value of a request. For more information, refer to What is SNI (Server Name Indication)? in the Learning Center.
:::note[Notes]
- The new SNI value must be a valid hostname on the same Cloudflare account (possibly on a different zone).
- Currently, you can only use a static value when overriding SNI.
- An SNI override will take precedence over SNI rewrites of custom origins when using Cloudflare for SaaS.
:::
[^1]: SNI allows a server to host multiple TLS Certificates for multiple websites using a single IP address. SNI adds the website hostname in the TLS handshake to inform the server which website to present when using shared IPs.
DNS record
Allows you to override the resolved hostname of incoming requests, which controls which origin server Cloudflare sends the request to. This functionality is also known as resolve override.
A common use case is when you are serving an application from a specific path (for example, mydomain.com/app). In this case, the app may be hosted on a different server or by a third party. A DNS record override allows you to redirect requests to this endpoint to the server for that third-party application.
:::note
-
You must specify a valid hostname in a DNS record override that is a hostname on the same Cloudflare account (possibly on a different zone). You can configure a DNS record (a
CNAME,A, orAAAArecord) with a hostname pointing to a third-party hostname/IP address, either proxied by Cloudflare or not. -
In most situations, when you configure a DNS record override you also need to configure a
Hostheader override. The DNS record override handles the rerouting of the request; theHostheader override updates theHostHTTP header value in the request. Defining aHostheader override will also update the Server Name Indication (SNI) value of the original request to the same value. To set an SNI value different from theHostheader value, add an SNI override in the same origin rule or create a separate origin rule for this purpose.
:::
The following example DNS records configure a resolve.example.com hostname pointing to an external hostname and IP address using a CNAME record and an A record, respectively:
Example CNAME record
- Type: CNAME
- Name:
resolve.example.com - Target:
domain.s3.amazonaws.com - TTL:
Auto - Proxy status: Proxied (orange cloud icon)
Example A record
- Type: A
- Name:
resolve.example.com - IPv4 address:
203.0.113.1 - TTL:
Auto - Proxy status: Proxied (orange cloud icon)
Destination port
Allows you to override the destination port of a request.
When you configure a destination port override, you can redirect incoming requests to a different port. For example, you could override the destination port for requests received for mydomain.com so that they are served by the application running on port 9000 (mydomain.com:9000).
The destination port must be between 1 and 65,535.
Origin Rules
import { Render } from "~/components";
Origin Rules allow you to change the origin server that Cloudflare sends a request to, or modify how the request reaches that server. This is useful when you need to route specific requests to a different backend, such as a third-party service or a server on a non-standard port. You can perform the following overrides:
- Host header: Overrides the
Hostheader of incoming requests. - Server Name Indication (SNI): Overrides the Server Name Indication (SNI) value of incoming requests.
- DNS record: Overrides the resolved hostname of incoming requests, sending the request to a different origin server.
- Destination port: Overrides the resolved destination port of incoming requests.
Each origin rule includes a filter expression that defines which requests the overrides apply to (for example, matching on hostname, path, or other request properties).
<Render
file="snippets-alternative"
product="rules"
params={{ suffix: "and customized modifications" }}
/>
<Render file="rules-requirements" product="rules" params={{ sentenceIntro: "Origin Rules require" }} />
Rules templates
Availability
| Free | Pro | Business | Enterprise | |
|---|---|---|---|---|
| Availability | Yes | Yes | Yes | Yes |
| Number of rules | 10 | 25 | 50 | 300 |
| Override Host header | No | No | No | Yes |
| Override SNI | No | No | No | Yes |
| Override DNS records | No | No | No | Yes |
| Override destination port | Yes | Yes | Yes | Yes |
Execution order
Important remarks
If you override the hostname with an origin rule (via Host header override or DNS record override) and add a header override to your load balancer configuration, the origin rule will take precedence over the load balancer configuration.
Like Page Rules, an origin rule performing a Host header override will update the SNI value of the original request to the same value of the Host header. To set an SNI value different from the Host header override, add an SNI override in the same origin rule or create a separate origin rule for this purpose.
<Render file="troubleshoot-rules-with-trace" product="rules" params={{ rulesFeatureName: "origin rules" }} />
Origin Rules API parameter reference
Create different overrides by including different action parameters in the action_parameters field:
| Override type | What to include |
|---|---|
| Host header override | host_header parameter |
| SNI override | sni object |
| DNS record override / Destination port override | origin object |
:::note The same origin rule can have different types of overrides. Refer to Configuring several overrides in the same rule for a syntax example. :::
Host header override parameters
The full syntax of the action_parameters field for overriding the HTTP Host header is the following:
"action_parameters": {
"host_header": "<HOST_HEADER_VALUE>"
}
SNI override parameters
The full syntax of the action_parameters field for overriding the SNI value of incoming requests is the following:
"action_parameters": {
"sni": {
"value": "<SNI_VALUE>"
}
}
DNS record override and destination port override parameters
The full syntax of the action_parameters field for overriding both the hostname and the destination port of incoming requests is the following:
"action_parameters": {
"origin": {
"host": "<HOSTNAME>",
"port": <PORT>
}
}
If you are only overriding the hostname or the port, omit the port or host parameter, respectively.
Configuring several overrides in the same rule
The same origin rule can have different types of overrides. For example, a single origin rule can perform an HTTP Host header override and a destination port override. The syntax of such a rule would be the following:
"action_parameters": {
"host_header": "<HOST_HEADER_VALUE>",
"origin": {
"port": <PORT>
}
}
Change the HTTP Host header and DNS record
import { Render, Tabs, TabItem, Example } from "~/components";
The following origin rule overrides the HTTP Host header to hr-server.example.com for all requests with a URI path starting with /hr-app/. It also overrides the DNS record to the same hostname.
The Host header override only updates the header value; the DNS record override will handle the rerouting of incoming requests. For more information on these overrides, refer to Origin Rules settings.
Expression when using the Expression Builder:
| Field | Operator | Value |
|---|---|---|
| URI Path | starts with | /hr-app/ |
Expression when using the Expression Editor:
(starts_with(http.request.uri.path, "/hr-app/"))
Value after Host Header > Rewrite to:
hr-server.example.com
Value after DNS Record > Override to:
hr-server.example.com
Change the destination port
import { Render, Tabs, TabItem, Example } from "~/components";
The following origin rule overrides the destination port to 8081 for all requests where the URI path starts with /team/calendar/.
Text in Expression Editor:
starts_with(http.request.uri.path, "/team/calendar/")
Value after Destination Port > Rewrite to:
8081
Define a single origin rule using Terraform
import { Render } from "~/components";
The following example defines a single origin rule for a zone using Terraform. The rule overrides the Host header, the resolved hostname, and the destination port of API requests.
# Change origin for API requests
resource "cloudflare_ruleset" "http_origin_example" {
zone_id = "<ZONE_ID>"
name = "Change origin"
description = ""
kind = "zone"
phase = "http_request_origin"
rules {
ref = "change_api_origin"
description = "Change origin of API requests"
expression = "(http.request.uri.path matches \"^/api/\")"
action = "route"
action_parameters {
host_header = "example.net"
origin {
host = "example.net"
port = 8000
}
}
}
}
Origin Rules examples
import { ResourcesBySelector } from "~/components";
<ResourcesBySelector directory="rules/origin-rules/examples/" types={["example"]} />
Change URI path and Host header
import { Example, Steps, DashButton } from "~/components";
This tutorial will instruct you how to modify both the URI path and the Host header of incoming requests using Transform Rules and Origin Rules.
Your website visitors will be routed from https://<YOUR_SOURCE_HOSTNAME>/uploads/* to https://<YOUR_TARGET_HOSTNAME>/*.
In this tutorial you will do the following:
- Create a URL rewrite to remove
/uploadsfrom the path. - Create an origin rule to modify the
Hostheader to desired hostname.
By following these steps, you can effectively manage both URI paths and Host headers to route traffic appropriately and optimize request handling.
1. Create a URL rewrite
-
In the Cloudflare dashboard, go to the Rules Overview page.
-
Select Create rule > URL Rewrite Rule.
-
Enter a descriptive name for the rule in Rule name.
-
Under If incoming requests match, select Custom filter expression, select Edit expression, and enter the following expression:
Text in Expression Editor:
raw.http.request.uri.path matches "^/uploads/.*" -
Under Set Rewrite parameters, select Path > Rewrite to, and select Dynamic.
-
Define the action for your rewrite URL rule:
Text after Path > Rewrite to > Dynamic:
regex_replace(raw.http.request.uri.path, "^/uploads/", "/")The
regex_replace()function replaces the/uploads/part of the path with/, changing/uploads/example.jpgto/example.jpg. -
Select Deploy.
2. Create an origin rule
:::note If you are routing traffic to an object storage bucket, use Cloud Connector instead of an origin rule. :::
-
In the Cloudflare dashboard, go to the Rules Overview page.
-
Select Create rule > Origin Rule.
-
Enter a descriptive name for the rule in Rule name.
-
Under When incoming requests match, define the rule expression:
Text in Expression Editor:
raw.http.request.uri.path matches "^/uploads/.*" -
Under Set origin parameters, select Host Header > Rewrite to.
-
Configure the rule to modify the
Hostheader to desired hostname:Text after Host Header > Rewrite to:
example.comThis will set the
Hostheader toexample.comfor matching requests. Make sure to replaceexample.comwith your actual hostname. -
(Optional) To route requests to a different origin (DNS target), use DNS override:
Text after DNS Record > Override to:
example.comThis will route requests to the DNS target of
example.cominstead of your default DNS record. -
Select Deploy.
Final remarks
After completing this tutorial, incoming traffic for https://<YOUR_SOURCE_HOSTNAME>/uploads/* will be routed to https://<YOUR_TARGET_HOSTNAME>/*.
Ensure the filters for the URL rewrite and the origin rule (or Cloud Connector rule) are precise to avoid unintended rule executions.
Remember that rules are evaluated in sequence, so Transform Rules (including URL rewrites) run before Origin Rules or Cloud Connector.
Origin Rules tutorials
import { ListTutorials } from "~/components";
Point to Pages with a custom domain
import { Example, DashButton, Steps } from "~/components";
This tutorial will instruct you how to configure an origin rule and a DNS record to point to a Pages deployment with a custom domain.
The procedure will use the following example values:
| URL that website visitors will access | mycustomerexample.com/blog/* |
| Zone domain | mycustomerexample.com |
| Cloudflare Pages subdomain | myblog.pages.dev |
| Cloudflare Pages custom domain | blogmirror.example.com |
When configuring your Pages custom domain, use a custom domain that you do not plan to use in production (blogmirror.example.com in this example).
1. Configure custom domain in your Pages project
To add the custom domain to your Pages deployment:
-
In the Cloudflare dashboard, go to the Workers & Pages page.
-
Select your Pages project.
-
Go to Custom domains.
-
Select Set up a custom domain.
-
Enter
blogmirror.example.comand select Continue.
When you add the custom domain to your Pages deployment, Cloudflare automatically creates a CNAME DNS record for the custom domain.
2. Create origin rule to rewrite host header and override DNS record
In your mycustomerexample.com zone, create an origin rule with the following configuration:
If incoming requests match
| Field | Operator | Value |
|---|---|---|
| URI Path | wildcard | /blog/* |
If using the Expression Editor, enter the following expression:
(http.request.uri.path wildcard "/blog/*")
Set origin parameters
- Value after Host header > Rewrite to:
blogmirror.example.com - Value after DNS record > Override to:
blogmirror.example.com
3. (Optional) Configure URL rewrite
In this example, the URL that website visitors will access starts with /blog. However, the Pages deployment does not have this initial URL segment.
Use a URL rewrite to remove the /blog segment from the URL path.
-
Go to Rules > Overview.
-
Select Create rule > URL Rewrite Rule.
-
Enter a descriptive name for the rule in Rule name.
-
In If incoming requests match, select Wildcard pattern.
-
Enter the following value in Request URL:
https://<YOUR_HOSTNAME>/blog/*In the current example, the value would be
https://mycustomerexample.com/blog/*. -
In Then rewrite the path and/or query, enter the following values under Path:
Target path Rewrite to [ /]blog/*[ /]${1} -
Select Deploy.
:::note Cloudflare provides a rule template in the dashboard called Rewrite Path for Object Storage Bucket that you can use and adapt to configure the URL rewrite rule. :::
More resources
Point to R2 bucket with a custom domain
import { Example, DashButton, Steps } from "~/components";
This tutorial will instruct you how to configure an origin rule and a DNS record to point to an R2 bucket configured with a custom domain.
The procedure will use the following example values:
| URL that website visitors will access | mycustomerexample.com/images/* |
| R2 bucket custom domain | imagesbucket.example.com |
When configuring your R2 bucket's custom domain, use a custom domain that you do not plan to use in production (imagesbucket.example.com in this example).
1. Configure custom domain in your Pages project
-
In the Cloudflare dashboard, go to the R2 object storage page.
-
Select your bucket.
-
On the bucket page, select Settings.
-
Under Public access > Custom Domains, select Connect Domain.
-
Enter the domain name you want to connect to —
imagesbucket.example.comin this example — and select Continue. -
Review the new record that will be added to the DNS table and select Connect Domain.
Your domain is now connected. The status takes a few minutes to change from Initializing to Active, and you may need to refresh to review the status update. If the status has not changed, select the ... next to your bucket and select Retry connection.
To view the added DNS record, select ... next to the connected domain and select Manage DNS.
:::note The domain used must belong to the same account as the R2 bucket. :::
2. Create origin rule to rewrite host header and override DNS record
In your mycustomerexample.com zone, create an origin rule with the following configuration:
If incoming requests match
| Field | Operator | Value |
|---|---|---|
| URI Path | wildcard | /images/* |
If using the Expression Editor, enter the following expression:
(http.request.uri.path wildcard "/images/*")
Set origin parameters
- Value after Host header > Rewrite to:
imagesbucket.example.com - Value after DNS record > Override to:
imagesbucket.example.com
3. (Optional) Configure URL rewrite
In our example, the URL that website visitors will access starts with /images. However, images stored in the example R2 bucket do not have this initial URL segment.
Use a URL rewrite to remove the /images segment from the URL path. Cloudflare provides a rule template in the dashboard called Rewrite Path for Object Storage Bucket that you can use to configure the required rewrite.
-
In the Cloudflare dashboard, go to the Rules Overview page.
-
Select Create rule > URL Rewrite Rule.
-
Enter a descriptive name for the rule in Rule name.
-
In If incoming requests match, select Wildcard pattern.
-
Enter the following value in Request URL:
https://<YOUR_HOSTNAME>/images/*In the current example, the value would be
https://mycustomerexample.com/images/*. -
In Then rewrite the path and/or query, enter the following values under Path:
Target path Rewrite to [ /]images/*[ /]${1} -
Select Deploy.
:::note Cloudflare provides a rule template in the dashboard called Rewrite Path for Object Storage Bucket that you can use and adapt to configure the URL rewrite rule. :::
More resources
Page Rules
Page Rules trigger one or more actions whenever a certain URL pattern is matched. Page Rules are available in Rules > Page Rules.
Availability
The default number of allowed page rules depends on the domain plan as shown below.
| Free | Pro | Business | Enterprise | |
|---|---|---|---|---|
| Availability | Yes | Yes | Yes | Yes |
| Number of rules | 3 | 20 | 50 | 125 |
Before getting started
It is important to understand a few Page Rules behaviors.
Page Rules require proxied DNS records
Page Rules require a proxied DNS record for your page rule to work. Page Rules will not apply to hostnames that do not exist in DNS or are not being directed to Cloudflare.
If you are creating a Page Rule for a hostname that does not have a real origin server, you still need a proxied DNS record. You can use a reserved IP address or domain as a placeholder. The record only needs to exist so that Cloudflare proxies traffic for that hostname. Create one of the following:
www.example.com A 192.0.2.1
www.example.com AAAA 2001:DB8::1
www.example.com CNAME domain.example
Cloudflare recommends using only reserved IP addresses or domain names for placeholder records to avoid accidentally routing traffic to infrastructure you do not own.
For more information on reserved IP addresses or top level domains, please refer to these RFCs:
Priority order matters
Only the highest priority matching page rule takes effect on a request.
Page Rules are prioritized in descending order in the Cloudflare dashboard, with the highest priority rule at the top. For this reason, Cloudflare recommends ordering your rules from most specific to least specific.
A page rule matches a URL pattern based on the following format (comprised of five segments):
<SCHEME>://<HOSTNAME>:<PORT>/<PATH>?<QUERY_STRING>
An example URL with all the segments looks like the following:
https://www.example.com:443/image.png?parameter1=value1
The <SCHEME> and <PORT> segments are optional. If omitted, <SCHEME> matches both http:// and https:// protocols. If no <PORT> is specified, the rule will match all ports.
Disabled page rules
When a page rule is disabled, actions will not trigger, but the rule will:
- Still appear in the Cloudflare dashboard.
- Be editable.
- Count against the number of rules allowed for your domain.
Manage Page Rules
import { Render, TabItem, Tabs, Steps, DashButton } from "~/components";
You can manage Page Rules in the Cloudflare dashboard or via API.
Create a page rule
To create a page rule in the dashboard:
-
In the Cloudflare dashboard, go to the Page Rules page.
-
Select Create Page Rule.
-
For URL, enter the URL or URL pattern that should match the rule (more details about wildcard matching).
-
For Pick a Setting, select a Cloudflare setting to adjust. If desired, select Add a Setting to adjust multiple Cloudflare settings with the same rule.
-
In the Order dropdown, specify the desired order: First, Last or Custom.
-
To save and deploy your rule, select Save and Deploy Page Rule. If you are not ready to deploy your rule, select Save as Draft.
For ideas about what rules you can create, refer to Recommended page rules.
To create a page rule using the API, send a POST request.
You may also want to review the documentation on wildcard matching, available settings, and recommended rules.
:::note[Notes]
-
Page Rules require a proxied DNS record to work. Page Rules will not apply to subdomains that do not exist in DNS or are not being directed to Cloudflare.
-
Cloudflare does not support non-ASCII characters — such as punycode/unicode domain — in Page Rules. Instead, you could URL-encode the string using Punycode converter. :::
Edit a page rule
To edit a page rule in the dashboard:
-
In the Cloudflare dashboard, go to the Page Rules page.
-
For a specific rule:
- To enable or disable the rule, select the on/off toggle.
- To modify the URL pattern, settings, and order, select Edit (wrench icon). Then, enter the information you want to change.
To update one or more fields using the API, send a PATCH request.
To entirely replace the configuration of a page rule, send a PUT request.
Delete a page rule
To delete a page rule in the dashboard:
-
In the Cloudflare dashboard, go to the Page Rules page.
-
For a specific rule, select X. Then, select Delete.
To delete a page rule using the API, send a DELETE request.
How to
import { DirectoryListing, Render } from "~/components";
Use the following resources to accomplish specific goals in Page Rules.
URL forwarding with Page Rules
import { Render, Steps, DashButton } from "~/components";
Page Rules allow you to forward or redirect traffic to a different URL, though they are just one of the options provided by Cloudflare.
Redirect with Page Rules
To configure URL forwarding or redirects using Page Rules:
-
In the Cloudflare dashboard, go to the Page Rules page.
-
Select Create Page Rule.
-
Under If the URL matches, enter the URL or URL pattern that should match the rule.
-
In Pick a Setting, choose Forwarding URL from the drop-down menu.
-
For Select status code, choose 301 - Permanent Redirect or 302 - Temporary Redirect.
-
Enter the destination URL.
-
Select Save and Deploy Page Rule.
:::note Page Rules require a proxied DNS record to work. Page Rules will not apply to subdomains that do not exist in DNS or are not being directed to Cloudflare. :::
Forwarding examples
Imagine you want site visitors to reach your website for a variety of URL patterns. For instance, the page rule URL patterns *www.example.com/products and *example.com/products match:
http://example.com/products
http://www.example.com/products
https://www.example.com/products
https://blog.example.com/products
https://www.blog.example.com/products
but do not match:
http://www.example.com/blog/products (extra directory)
or
http://www.example.comproducts (no trailing slash)
Once you have created the pattern that matches what you want, select the Forwarding toggle. This will display a field where you can enter the address you want requests forwarded to.
https://example.com/products
If you enter the address above in the forwarding box and select Add Rule, within a few seconds any requests that match the pattern you entered will automatically be forwarded with an HTTP 302 redirect status code to the new URL.
Advanced forwarding options
If you use a basic redirect, such as forwarding the apex domain (example.com) to www.example.com, then you lose anything else in the URL.
For example, you could set up the pattern:
example.com
And have it forward to:
http://www.example.com
However, if someone entered example.com/some-particular-page.html, they would be redirected to:
www.example.com
Instead of:
www.example.com/some-particular-page.html
The solution is to use variables. Each wildcard corresponds to a variable when can be referenced in the forwarding address. The variables are represented by a $ (dollar sign) followed by a number. To refer to the first wildcard you would use $1, to refer to the second wildcard you would use $2, and so on.
To fix the forwarding from the apex to www in the above example, you could use the same pattern:
example.com/*
You would then set up the following URL for traffic to forward to:
http://www.example.com/$1
In this case, if someone went to:
example.com/some-particular-page.html
They would be redirected to:
http://www.example.com/some-particular-page.html
Additional reference for Page Rules
import { Render } from "~/components";
Bypass Cache on Cookie setting
This setting is available to Business and Enterprise customers.
The Bypass Cache on Cookie setting supports basic regular expressions (regex) as follows:
- A pipe operator (represented by
|) to match multiple cookies using OR boolean logic. For example,bypass=.*|PHPSESSID=.*would bypass the cache if either a cookie calledbypassorPHPSESSIDwere set, regardless of the cookie's value. - The wildcard operator (represented by
.*), such that a rule value oft.*st=would match both a cookie calledtestand one calledteeest.
Limitations include:
- 150 characters per cookie regex
- 12 wildcards per cookie regex
- 1 wildcard in between each
|in the cookie regex
To learn how to configure Bypass Cache on Cookie with a cache rule, refer to Bypass Cache on Cookie.
:::note
If you add both this setting and the enterprise-only Cache On Cookie setting to the same page rule, Cache On Cookie takes precedence over Bypass Cache on Cookie.
:::
Zone name occurrences must end with a slash
When saving a page rule, Cloudflare will ensure that there is a slash after each occurrence of the current zone name in the If the URL matches field. For example, if the current zone name is example.com, then:
example.comwill be saved asexample.com/example.com/path/example.comwill be saved asexample.com/path/example.com/
Note that example.com/some-path/cloudflare.com will be saved without a final slash, since the zone name is not cloudflare.com.
Network ports supported by Page Rules
If you specify a port in the If the URL matches field of a page rule, it must be one of the following:
- One of the HTTP/HTTPS ports compatible with Cloudflare’s proxy.
- A custom port of a Cloudflare Spectrum HTTPS application.
Using Page Rules with Workers
If the URL of the current request matches both a page rule and a Workers custom route, some Pages Rules settings will not be applied. For more details, refer to Page Rules.
Page Rules are case-insensitive
The pattern entered under If the URL matches will not consider upper and lower case differences — example.com/path, example.com/Path, and example.com/PATH will be triggered the same way.
If you need your rules to consider case sensitivity, you might want to use alternative Rules options instead.
Reference
import { DirectoryListing, Render } from "~/components";
Recommended page rules
import { Example, Render } from "~/components";
Use Cloudflare Page Rules to improve the user experience of your domain with hardened security and enhanced site performance, while increasing reliability and minimizing bandwidth usage for your origin server.
Keep in mind that not all rules will be right for everyone, but these are some of the most popular.
- 301/302 Forwarding URL
- Cache Level in specific paths
- Edge Cache TTL, Always Online, and Browser Cache TTL
301/302 Forwarding URL
:::note Consider using Single Redirects or Bulk Redirects to forward or redirect traffic to a different URL due to ease of use, maintenance, and cost. You should only use Page Rules when Dynamic or Bulk Redirects do not meet your use case. :::
Two common examples for using forwarding URLs are:
- Defining the root as the canonical version of your domain.
- Directing visitors to a specific page with an easy to remember URL.
This example page rule configuration defines the root as the canonical version of your domain:
- If the URL matches:
*www.example.com/* - Setting: Forwarding URL | Select status code: 301 Permanent Redirect
- Enter destination URL:
https://example.com/$2
This example redirects visitors to a specific page with an easy to remember URL:
- If the URL matches:
*www.example.com/fb* - Setting: Forwarding URL | Select status code: 302 Temporary Redirect
- Enter destination URL:
https://www.facebook.com/username
Cache Level in specific paths
Certain sections of a website, like the login or admin section, have different security and performance requirements than your general public-facing pages.
The following example page rule configuration bypasses cache for requests targeting a specific path:
- If the URL matches:
example.com/user* - Setting: Cache Level | Value: Bypass
Edge Cache TTL and Browser Cache TTL
Certain resources on your domain will likely not change often. For these resources, taking advantage of aggressive caching options can significantly reduce the load on your server and bandwidth utilization.
Examples
In the following example page rule configuration, the target is a folder that holds the majority of the image assets as well as some other types of multimedia.
- If the URL matches:
example.com/sites/default/files* - Setting: Browser Cache TTL | Value: a day
- Setting: Cache Level | Value: Cache Everything
- Setting: Edge Cache TTL | Value: 7 days
The following example page rule configuration applies unique rules for critical pages that do not change very often.
- If the URL matches:
example.com/terms-of-service - Setting: Browser Cache TTL | Value: a day
- Setting: Always Online | Value: On
- Setting: Cache Level | Value: Cache Everything
- Setting: Edge Cache TTL | Value: a month
Page Rules settings
import { Render } from "~/components";
Settings control the action Cloudflare takes once a request matches the URL pattern defined in a page rule. You can use settings to enable and disable multiple Cloudflare features across the dashboard.
Note that:
- Some settings require a Pro, Business, or Enterprise domain plan.
- You can specify more than one setting to apply when the rule triggers.
The available Page Rules settings are the following:
| Setting | Description | Plans |
|---|---|---|
| Always Use HTTPS | Enable Always Use HTTPS feature. If enabled, any http:// URL is converted to https:// through a 301 redirect.If this option does not appear, you do not have an active Edge Certificate. |
All |
| Automatic HTTPS Rewrites | Turn on or off Automatic HTTPS Rewrites. | All |
| Browser Cache TTL | Control how long resources cached by client browsers remain valid. The Cloudflare dashboard and the API both prohibit setting Browser Cache TTL to 0 for non-Enterprise domains. |
All |
| Browser Integrity Check | Inspect the visitor's browser for headers commonly associated with spammers and certain bots. | All |
| Bypass Cache on Cookie | Bypass cache and fetch resources from the origin server if a regular expression matches against a cookie name present in the request. If you add both this setting and the Cache On Cookie setting to the same page rule, Cache On Cookie takes precedence over Bypass Cache on Cookie. Refer to the Additional details to learn about limited regular expression support. |
Business and Enterprise |
| Cache By Device Type | Separate cached content based on the visitor’s device type. | Enterprise |
| Cache Deception Armor | Protect from web cache deception attacks while still allowing static assets to be cached. This setting verifies that the URL's extension matches the returned Content-Type. | All |
| Cache Level | Apply custom caching based on the option selected: – Bypass: Cloudflare does not cache. – No Query String: Delivers resources from cache when there is no query string. – Ignore Query String: Delivers the same resource to everyone independent of the query string. – Standard: Caches all static content that has a query string. – Cache Everything: Treats all content as static and caches all file types beyond the Cloudflare default cached content. Respects cache headers from the origin web server unless Edge Cache TTL is also set in the Page Rule. When combined with an Edge Cache TTL > 0, Cache Everything removes cookies from the origin web server response. |
All |
| Cache on Cookie | Apply the Cache Everything option (Cache Level setting) based on a regular expression match against a cookie name. If you add both this setting and Bypass Cache on Cookie to the same page rule, Cache On Cookie takes precedence over Bypass Cache on Cookie. |
Business and above |
| Cache TTL by Status Code | Enterprise customers can set cache time-to-live (TTL) based on the response status from the origin web server. Cache TTL refers to the duration of a resource in the Cloudflare network before being marked as stale or discarded from cache. Status codes are returned by a resource's origin. Setting cache TTL based on response status overrides the default cache behavior (standard caching) for static files and overrides cache instructions sent by the origin web server. To cache non-static assets, set a Cache Level of Cache Everything using a Page Rule. Setting no-store Cache-Control or a low TTL (using max-age/s-maxage) increases requests to origin web servers and decreases performance. |
Enterprise |
| Custom Cache Key | Control specifically what variables to include when deciding which resources to cache. This allows customers to determine what to cache based on something other than just the URL. | Enterprise |
| Disable Zaraz | Turn off Zaraz. | All |
| Edge Cache TTL | Specify how long to cache a resource in the Cloudflare global network. Edge Cache TTL is not visible in response headers. | All |
| Email Obfuscation | Turn on or off Email Obfuscation. | All |
| Forwarding URL | Redirects one URL to another using an HTTP 301/302 redirect. Refer to Wildcard matching in Page Rules. |
All |
| Host Header Override | Apply a specific host header.<Render file="origin-rule-promotion" product="rules" params={{ originRuleUrl: "/rules/origin-rules/features/#host-header" }} /> | Enterprise |
| IP Geolocation Header | Cloudflare adds a CF-IPCountry HTTP header containing the country code that corresponds to the visitor. |
All |
| Opportunistic Encryption | Turn on or off the Opportunistic Encryption. | All |
| Origin Cache Control | Origin Cache Control is enabled by default for Free, Pro, and Business domains and disabled by default for Enterprise domains. | All |
| Origin Error Page Pass-thru | Turn on or off Cloudflare error pages generated from issues sent from the origin server. If enabled, this setting triggers error pages issued by the origin. | Enterprise |
| Polish | Apply options from the Polish feature of the Cloudflare Speed app. | Pro and above |
| Query String Sort | Turn on or off the reordering of query strings. When query strings have the same structure, caching improves. | Enterprise |
| Resolve Override | Change the origin address to the value specified in this setting. <Render file="origin-rule-promotion" product="rules" params={{ originRuleUrl: "/rules/origin-rules/features/#dns-record" }} /> | Enterprise |
| Respect Strong ETags | Turn on or off byte-for-byte equivalency checks between the Cloudflare cache and the origin server. | Enterprise |
| Response Buffering | Turn on or off whether Cloudflare should wait for an entire file from the origin server before forwarding it to the site visitor. By default, Cloudflare sends packets to the client as they arrive from the origin server. | Enterprise |
| Rocket Loader | Turn on or off Rocket Loader in the Cloudflare Speed app. | All |
| SSL | Control options for the SSL feature of the Edge Certificates tab in the Cloudflare SSL/TLS app. | All |
| True Client IP Header | Turn on or off the True-Client-IP Header feature of the Cloudflare Network app. | Enterprise |
Wildcard matching in Page Rules
import { Render } from "~/components";
You can use the asterisk (*) in any URL segment to match certain patterns. For example, example.com/t*st would match:
example.com/testexample.com/toastexample.com/trust
example.com/foo/* does not match example.com/foo, but example.com/foo* does match.
Helpful tips
- To match both
httpandhttps, writeexample.com. Writing*example.comis unnecessary. - To match every page on a domain, write
example.com/*. Writingexample.comwill not work. - To match every page on a domain and its subdomains, write
*example.com/*. Writingexample.comwill not work. - A wildcard (
*) in a page rule URL will match even if no characters are present and may include any part of the URL, including the query string.
Reference wildcard matches
You can reference a matched wildcard later using the $<X> syntax, where <X> indicates the index of a glob pattern. For example, $1 represents the first wildcard match and $2 represents the second wildcard match.
The $<X> syntax is especially useful with the Forwarding URL setting. For example, you could forward http://*.example.com/* to http://example.com/images/$1/$2.jpg.
This rule would match http://cloud.example.com/flare.jpg, which would be forwarded to http://example.com/images/cloud/flare.jpg.
To add a $ character in the forwarding URL, escape it by adding a backslash \ in front like \$.
:::caution Avoid creating a redirect where the domain points to itself as the destination. A domain that points to itself can cause an infinite redirect error, which makes your site inaccessible to visitors. :::
Troubleshoot Page Rules - Billing and subscription
import { Render, Steps, DashButton } from "~/components";
If I run out of Page Rules, can I buy more?
No, you cannot buy additional Page Rules. You will need to migrate to modern Rules features to benefit from increased quotas.
How do I cancel my Page Rules purchase?
To cancel a purchase:
-
In the Cloudflare dashboard, go to the Billing page.
-
Go to Subscriptions.
-
Find the associated website (listed in alphabetical order) and select Edit.
-
For Additional page rules, change the amount to your previous value. If you are over your current limit, you may have to delete existing page rules (paused or active).
-
Finish the updates to your subscription.
When I change plans will I keep my add-on subscriptions?
When you change plans, you will keep your Page Rules add-on subscription.
What happens if I cancel Page Rules halfway through my billing cycle?
You will be billed for and have access to your additional Page Rules until the end of your current billing cycle.
Your plan will adjust at the start of your next billing cycle. For more details, refer to our billing policy.
Troubleshoot Page Rules - General
import { Render } from "~/components";
Why is a page rule not working?
The most common reason that a page rule is not working — such as URL forwarding — is that the page rule you created is on a record that is not proxied by Cloudflare in your DNS settings.
Consider an example where you have a page rule that redirects a subdomain (subdomain.yoursitename.com) back to your apex domain (yoursitename.com). If you do not have that record proxied in your DNS settings for the subdomain record, Cloudflare's proxy is not running over the record and a page rule will not work because it is going direct to your server.
Error 500 (Internal server error)
Root cause
This may be due to a configuration issue on a page rule. When creating a page rule that uses two wildcards, like a Forwarding URL rule, it is possible to create a rule that mentions the second wildcard with the $2 placeholder. Refer to the example below:

When updating the same rule, you can remove one of the wildcard in the If the URL matches field and save it. Refer to the example below:

If you do so, the $2 placeholder reference a wildcard that does not exist anymore, and as such, an Error 500 (Internal server error) is thrown when a URL triggers the page rule.
Resolution
Update the page rule and remove the reference $2 to the second wildcard. If there is only one wildcard, then you can only use $1.
Troubleshooting
import { DirectoryListing, Render } from "~/components";
Use the following resources to help you troubleshoot issues with Page Rules.
Reference
import { DirectoryListing } from "~/components";
Refer to the following pages for more information about Rules:
Page Rules migration guide
import { Render, TabItem, Tabs, Example, Steps } from "~/components";
Cloudflare is continuously improving its platform to deliver more powerful and scalable tools for managing your configurations. To help you take full advantage of these improvements, we recommend using modern Rules features for new implementations. These products address the limitations of Page Rules while providing greater flexibility, scalability, and ease of use.
For a quick start, explore the one-click templates available in the Cloudflare dashboard in Rules > Overview. These templates simplify common configurations like redirects, rewrites and header modifications, making setup faster and easier.
Page Rules migration
To make the transition seamless, Cloudflare will handle the migration of your existing Page Rules automatically. This process is planned for late 2025 or beyond, with no action required on your part. You will receive advance notification before any changes are made.
If you wish to explore the benefits of modern Rules features sooner, you can begin adopting them today. Doing so allows you to:
- Take advantage of modern features and capabilities sooner.
- Customize and refine your rules to match your evolving needs.
To assist with this process, we provide you with a comprehensive mapping between Page Rules settings and modern Rules products in this guide.
Why transition?
Cloudflare Page Rules has several fundamental limitations, such as triggering solely based on URL patterns and being limited to 125 rules per zone for performance reasons. These rules are also complex to debug when multiple page rules apply to the same incoming request.
In 2022, we announced in our blog post The future of Page Rules that Page Rules would be replaced with a suite of dedicated products, each built to be best-of-breed and put more power into the hands of our users. The new Rules products — Configuration Rules, Compression Rules, Origin Rules, Redirects, and Transform Rules — are now generally available (GA) and have already been adopted by tens of thousands of Cloudflare customers.
Improvements in modern Rules features include:
- New engine: New Rules features are powered by the Ruleset Engine, which offers versatile configuration with a robust language that supports many HTTP request and response fields.
- Improved scalability: Thanks to the improved scalability, Cloudflare plans now have increased quotas.
- Easier troubleshooting: Rule execution is more predictable, since each rule operates independently, simplifying troubleshooting. Additionally, Rules simulator helps understand rule interactions.
- Improved consistency: New Rules features also ensure consistency, with common fields and capabilities shared across products, offering a seamless experience and predictable Terraform configurations.
Key differences
The evaluation and execution order of Rules features is different from Page Rules:
- Rule matching logic: Page Rules apply the first matching rule (first match wins). In contrast, modern Rules are stackable, meaning multiple matching rules can combine and apply to the same request (last match wins). For example, if multiple cache rules match the same URL, the features in those rules will all apply in order.
- Action separation: A Page Rule may include multiple actions for different products that are applied in a sequence selected by the customer within the Page Rule itself. Modern Rules features are evaluated in a fixed sequence, with customers defining the rule order within a product phase.
- Precedence: Modern Rules features take precedence over Page Rules. For instance, if both define caching settings for the same path, Cache Rules will override Page Rules.
- Caching behavior: In Cache Rules, selecting Eligible for cache automatically enables Cache Everything by default. To maintain the exact behavior of Page Rules, you may need to adjust your configuration.
- Interactions with Workers: Requests handled by Workers will suppress Page Rules actions, but they will not suppress actions from modern Rules features.
Convert Page Rules URLs to filter expressions
Modern Rules use filter expressions instead of URL patterns. These expressions, built with the Rules language, allow greater precision by leveraging fields, functions, and operators.
The following example demonstrates the use of the http.request.full_uri field and the wildcard operator for wildcard matching:
example.com/*/downloads/*.txt
becomes a filter expression such as:
http.request.full_uri wildcard "http*://example.com/*/downloads/*.txt*"
Single Redirects and URL Rewrite Rules also offer a simplified view called Wildcard pattern, allowing you to specify URL patterns (http*://example.com/*/downloads/*.txt*) without specifying the full filter expression (http.request.full_uri wildcard "http*://example.com/*/downloads/*.txt*").
Important considerations
- Protocol scheme: Page Rules URL matching does not include the URI scheme (for example,
http://orhttps://) unless explicitly included in the rule. Filter expressions usinghttp.request.full_urifield, however, require matching the full URI, including the protocol scheme. To make your filter expression scheme-agnostic, usehttp*://as a wildcard for bothhttp://andhttps://. - Query strings: Page Rules ignore query strings unless they are part of the rule URL. Filter expressions include the query string automatically, as part of the
http.request.full_urifield. To ensure query strings do not affect your matching, append a*wildcard at the end of your filter expression, such as.txt*.
Feature correspondence table
To help you map existing Page Rules to modern Rules products, this table outlines how Page Rules settings translate to modern Rules and provides examples for common configurations.
Also, to streamline common configurations, the Cloudflare dashboard now includes dozens of one-click templates, available in Rules > Overview. These templates enable you to deploy commonly used features — such as redirects, rewrites, and header modifications — instantly, with pre-filled filter expressions and actions. Explore these templates in the dashboard for a faster setup.
| Page Rules setting | New implementation uses... | Migration/Replacement instructions |
|---|---|---|
| Always Use HTTPS | Redirect Rules (Single Redirects) | Migrate Always Use HTTPS |
| Browser Cache TTL | Cache Rules | Migrate Browser Cache TTL |
| Browser Integrity Check | Configuration Rules | Migrate Browser Integrity Check |
| Bypass Cache on Cookie | Cache Rules | Migrate Bypass Cache on Cookie |
| Cache By Device Type | Cache Rules | Migrate Cache By Device Type |
| Cache Deception Armor | Cache Rules | Migrate Cache Deception Armor |
| Cache Level | Cache Rules | Migrate Cache Level |
| Cache on Cookie | Cache Rules | Migrate Cache on Cookie |
| Cache TTL by status code | Cache Rules | Migrate Cache TTL by status code |
| Custom Cache Key | Cache Rules | Migrate Custom Cache Key |
| Disable Apps | Configuration Rules | Migrate Disable Apps |
| Disable Performance | N/A (deprecated) | Replace Disable Performance |
| Disable Railgun | N/A (deprecated) | N/A |
| Disable Security | N/A (deprecated) | Replace Disable Security |
| Disable Zaraz | Configuration Rules | Migrate Disable Zaraz |
| Edge Cache TTL | Cache Rules | Migrate Edge Cache TTL |
| Email Obfuscation | Configuration Rules | Migrate Email Obfuscation |
| Forwarding URL | Redirect Rules (Single Redirects) | Migrate Forwarding URL |
| Host Header Override | Origin Rules | Migrate Host Header Override |
| IP Geolocation Header | Transform Rules (Managed Transforms) | Migrate IP Geolocation Header |
| Opportunistic Encryption | Configuration Rules | Migrate Opportunistic Encryption |
| Origin Cache Control | Cache Rules | Migrate Origin Cache Control |
| Origin Error Page Pass-thru | Cache Rules | Migrate Origin Error Page Pass-thru |
| Polish | Configuration Rules | Migrate Polish |
| Query String Sort | Cache Rules | Migrate Query String Sort |
| Resolve Override | Origin Rules | Migrate Resolve Override |
| Respect Strong ETags | Cache Rules | Migrate Respect Strong ETags |
| Response Buffering | N/A (deprecated) | N/A |
| Rocket Loader | Configuration Rules | Migrate Rocket Loader |
| Security Level | Configuration Rules | Migrate Security Level |
| True Client IP Header | Transform Rules (Managed Transforms) | Migrate True Client IP Header |
| SSL | Configuration Rules | Migrate SSL |
| Web Application Firewall | N/A (deprecated) | N/A |
Migrate Always Use HTTPS
Context:
You configured a Page Rule to perform an automatic redirect from HTTP to HTTPS for all subdomains of example.com and the example.com domain itself:
- URL
*example.com/* - Setting: Always Use HTTPS
How to migrate:
-
Create a single redirect to always redirect HTTP requests to HTTPS. You can select the Redirect from HTTP to HTTPS rule template or enter the following rule configuration:
- If incoming requests match: Wildcard pattern
- Request URL:
http://*
- Request URL:
- Then:
- Target URL:
https://${1} - Status code: 301
- Preserve query string: Enabled
- Target URL:
- If incoming requests match: Wildcard pattern
-
Turn off your existing Page Rule and validate the behavior of the redirect you created.
-
If your tests succeed, delete the existing Page Rule.
| Page Rules configuration | Migrate to a single redirect |
|---|---|
![]() |
![]() |
Migrate Automatic HTTPS Rewrites
Context:
You configured a Page Rule turning on Automatic HTTPS Rewrites for all subdomains of example.com and the example.com domain itself:
- URL:
*example.com/* - Setting: Automatic HTTPS Rewrites
- Value: On
How to migrate:
-
Create a configuration rule to always rewrite HTTP links to HTTPS for any hostname containing
example.com:- When incoming requests match: Custom filter expression
- Using the Expression Builder:
Hostname contains "example.com" - Using the Expression Editor:
(http.host contains "example.com")
- Using the Expression Builder:
- Then the settings are:
- Setting: Automatic HTTPS Rewrites
- Value: On
- When incoming requests match: Custom filter expression
-
Turn off your existing Page Rule and validate the behavior of the configuration rule you created.
-
If your tests succeed, delete the existing Page Rule.
| Page Rules configuration | Migrate to a configuration rule |
|---|---|
![]() |
![]() |
Migrate Browser Cache TTL
Context:
You configured a Page Rule adjusting browser cache TTL to one day for all subdomains of example.com and the example.com domain itself:
- URL:
*example.com/* - Setting: Browser Cache TTL
- Enter Browser Cache TTL: a day
How to migrate:
-
Create a cache rule to adjust browser cache TTL for caching resources in the browser to one day for any hostname containing
example.com:- When incoming requests match: Custom filter expression
- Using the Expression Builder:
Hostname contains "example.com" - Using the Expression Editor:
(http.host contains "example.com")
- Using the Expression Builder:
- Then:
- Cache eligibility: Eligible for cache
- Browser TTL: Override origin and use this TTL
- Input time-to-live (TTL): 1 day
- When incoming requests match: Custom filter expression
-
Turn off your existing Page Rule and validate the behavior of the cache rule you created.
-
If your tests succeed, delete the existing Page Rule.
| Page Rules configuration | Migrate to a cache rule |
|---|---|
![]() |
![]() |
Migrate Browser Integrity Check
Context:
You configured a Page Rule turning on Browser Integrity Check for all subdomains of example.com and the example.com domain itself:
- URL:
*example.com/* - Setting: Browser Integrity Check
- Value: On
How to migrate:
-
Create a configuration rule to turn on Browser Integrity Check for protecting against bots and threats for any hostname containing
example.com:- When incoming requests match: Custom filter expression
- Using the Expression Builder:
Hostname contains "example.com" - Using the Expression Editor:
(http.host contains "example.com")
- Using the Expression Builder:
- Then the settings are:
- Setting: Browser Integrity Check
- Value: On
- When incoming requests match: Custom filter expression
-
Turn off your existing Page Rule and validate the behavior of the configuration rule you created.
-
If your tests succeed, delete the existing Page Rule.
| Page Rules configuration | Migrate to a configuration rule |
|---|---|
![]() |
![]() |
Migrate Bypass Cache on Cookie
Context:
You configured a Page Rule turning on Bypass Cache on Cookie for all subdomains of example.com and the example.com domain itself:
- URL:
*example.com/* - Setting: Bypass Cache on Cookie
- Enter value:
test_cookie
How to migrate:
-
Create a cache rule to bypass cache for requests containing cookie
test_cookiefor any hostname containingexample.com:- When incoming requests match: Custom filter expression
- Using the Expression Builder:
Hostname contains "example.com" AND Cookie contains "test-cookie" - Using the Expression Editor:
(http.host contains "example.com" and http.cookie contains "test-cookie")
- Using the Expression Builder:
- Then:
- Cache eligibility: Bypass cache
- When incoming requests match: Custom filter expression
-
Turn off your existing Page Rule and validate the behavior of the cache rule you created.
-
If your tests succeed, delete the existing Page Rule.
| Page Rules configuration | Migrate to a cache rule |
|---|---|
![]() |
![]() |
Migrate Cache By Device Type
Context:
You configured a Page Rule turning on Cache By Device Type for all subdomains of example.com and the example.com domain itself:
- URL:
*example.com/* - Setting: Cache By Device Type
- Value: On
How to migrate:
-
Create a cache rule to cache content based on user agent or device type for any hostname containing
example.com:- When incoming requests match: Custom filter expression
- Using the Expression Builder:
Hostname contains "example.com" - Using the Expression Editor:
(http.host contains "example.com")
- Using the Expression Builder:
- Then:
- Cache eligibility: Eligible for cache
- Setting: Cache key
- Cache by device type: On
- When incoming requests match: Custom filter expression
-
Turn off your existing Page Rule and validate the behavior of the cache rule you created.
-
If your tests succeed, delete the existing Page Rule.
| Page Rules configuration | Migrate to a cache rule |
|---|---|
![]() |
![]() |
Migrate Cache Deception Armor
Context:
You configured a Page Rule turning on Cache Deception Armor for all subdomains of example.com and the example.com domain itself:
- URL:
*example.com/* - Setting: Cache Deception Armor
How to migrate:
-
Create a cache rule to protect against cache deception attacks for any hostname containing
example.com:- When incoming requests match: Custom filter expression
- Using the Expression Builder:
Hostname contains "example.com" - Using the Expression Editor:
(http.host contains "example.com")
- Using the Expression Builder:
- Then:
- Cache eligibility: Eligible for cache
- Setting: Cache key
- Cache deception armor: On
- When incoming requests match: Custom filter expression
-
Turn off your existing Page Rule and validate the behavior of the cache rule you created.
-
If your tests succeed, delete the existing Page Rule.
| Page Rules configuration | Migrate to a cache rule |
|---|---|
![]() |
![]() |
Migrate Cache Level (Cache Everything)
Context:
You configured a Page Rule turning on caching of all assets for all subdomains of example.com and the example.com domain itself:
- URL:
*example.com/* - Setting: Cache Level
- Select Cache Level: Cache Everything
How to migrate:
-
Create a cache rule to adjust cache level for any hostname containing
example.com:- When incoming requests match: Custom filter expression
- Using the Expression Builder:
Hostname contains "example.com" - Using the Expression Editor:
(http.host contains "example.com")
- Using the Expression Builder:
- Then:
- Cache eligibility: Eligible for cache
- When incoming requests match: Custom filter expression
-
Turn off your existing Page Rule and validate the behavior of the cache rule you created.
-
If your tests succeed, delete the existing Page Rule.
| Page Rules configuration | Migrate to a cache rule |
|---|---|
![]() |
![]() |
Migrate Cache on Cookie
Context:
You configured a Page Rule turning on caching for responses that contained cookie test-cookie for all subdomains of example.com and the example.com domain itself:
- URL:
*example.com/* - Setting: Cache on Cookie
- Enter value:
test-cookie
How to migrate:
-
Create a cache rule to cache responses containing cookie
test_cookiefor any hostname containingexample.com:- When incoming requests match: Custom filter expression
- Using the Expression Builder:
Hostname contains "example.com" AND Cookie contains "test-cookie" - Using the Expression Editor:
(http.host contains "example.com" and http.cookie contains "test-cookie")
- Using the Expression Builder:
- Then:
- Cache eligibility: Eligible for cache
- When incoming requests match: Custom filter expression
-
Turn off your existing Page Rule and validate the behavior of the cache rule you created.
-
If your tests succeed, delete the existing Page Rule.
| Page Rules configuration | Migrate to a cache rule |
|---|---|
![]() |
![]() |
Migrate Cache TTL by status code
Context:
You configured a Page Rule turning on caching of every response with status code between 200 and 599 for one day, for all subdomains of example.com and the example.com domain itself:
- URL:
*example.com/* - Setting: Cache TTL by status code
- Status code or enter range:
200-599 - Select option: a day
How to migrate:
-
Create a cache rule to cache responses with status code between
200and599for one day for any hostname containingexample.com:- When incoming requests match: Custom filter expression
- Using the Expression Builder:
Hostname contains "example.com" - Using the Expression Editor:
(http.host contains "example.com") - Then:
- Cache eligibility: Eligible for cache
- Setting: Edge TTL
- Use cache-control header if present, use default Cloudflare caching behavior if not
- Status code TTL:
- Scope: Range
- From: 200
- To: 599
- Duration: 1 day
- Using the Expression Builder:
- When incoming requests match: Custom filter expression
-
Turn off your existing Page Rule and validate the behavior of the cache rule you created.
-
If your tests succeed, delete the existing Page Rule.
| Page Rules configuration | Migrate to a cache rule |
|---|---|
![]() |
![]() |
Migrate Custom Cache Key
Context:
You configured a Page Rule setting a custom cache key for all query string parameters, for all subdomains of example.com and the example.com domain itself:
- URL:
*example.com/* - Setting: Custom Cache Key
- Query String: All query string parameters
How to migrate:
-
Create a cache rule to set a custom cache key for all query string parameters, for any hostname containing
example.com:- When incoming requests match: Custom filter expression
- Using the Expression Builder:
Hostname contains "example.com" - Using the Expression Editor:
(http.host contains "example.com")
- Using the Expression Builder:
- Then:
- Cache eligibility: Eligible for cache
- Setting: Cache key
- Query string: All query string parameters
- When incoming requests match: Custom filter expression
-
Turn off your existing Page Rule and validate the behavior of the cache rule you created.
-
If your tests succeed, delete the existing Page Rule.
| Page Rules configuration | Migrate to a cache rule |
|---|---|
![]() |
![]() |
Migrate Disable Apps
Context:
You configured a Page Rule turning off Cloudflare Apps (deprecated) for all subdomains of example.com and the example.com domain itself:
- URL:
*example.com/* - Setting: Disable Apps
How to migrate:
-
Create a configuration rule to disable Cloudflare Apps (deprecated) for any hostname containing
example.com:- When incoming requests match: Custom filter expression
- Using the Expression Builder:
Hostname contains "example.com" - Using the Expression Editor:
(http.host contains "example.com")
- Using the Expression Builder:
- Then the settings are:
- Setting: Disable Apps
- When incoming requests match: Custom filter expression
-
Turn off your existing Page Rule and validate the behavior of the configuration rule you created.
-
If your tests succeed, delete the existing Page Rule.
| Page Rules configuration | Migrate to a configuration rule |
|---|---|
![]() |
![]() |
Replace Disable Performance
:::caution The Disable Performance setting is deprecated. Any Page Rules with this setting will not be migrated. :::
This Page Rules setting turned off Polish and Rocket Loader. You can still turn on or off relevant Cloudflare features one by one using Configuration Rules.
Context:
You configured a Page Rule with Disable Performance (deprecated) for all subdomains of example.com and the example.com domain itself:
- URL:
*example.com/* - Setting: Disable Performance
How to replace:
-
Create a configuration rule to disable Polish and Rocket Loader for any hostname containing
example.com:- When incoming requests match: Custom filter expression
- Using the Expression Builder:
Hostname contains "example.com" - Using the Expression Editor:
(http.host contains "example.com")
- Using the Expression Builder:
- Then the settings are:
- Polish: Off
- Rocket Loader: Off
- When incoming requests match: Custom filter expression
-
Turn off your existing Page Rule and validate the behavior of the configuration rule you created.
-
If your tests succeed, delete the existing Page Rule.
| Page Rules configuration | Migrate to a configuration rule |
|---|---|
![]() |
![]() |
Replace Disable Security
:::caution The Disable Security setting is deprecated. Any Page Rules with this setting will not be migrated. :::
This Page Rules setting turns off Email Obfuscation, Rate Limiting (previous version), Scrape Shield, URL (Zone) Lockdown, and WAF managed rules (previous version). You can still turn on or off relevant Cloudflare features one by one using Configuration Rules and WAF custom rules.
Context:
You configured a Page Rule with Disable Security (deprecated) for all subdomains of example.com and the example.com domain itself:
- URL:
*example.com/* - Setting: Disable Security
This setting turned off a subset of Cloudflare security features: Email Obfuscation, Rate Limiting (previous version), Scrape Shield, URL (Zone) Lockdown, and WAF managed rules (previous version).
How to replace:
-
Create a configuration rule to turn off one or more security features:
-
If required, create a WAF exception to skip one or more rules of WAF managed rulesets for requests coming from IP addresses in an allowlist.
-
Turn off your existing Page Rule and validate the behavior of the rules you created.
-
If your tests succeed, delete the existing Page Rule.
:::caution If you are still using WAF managed rules (previous version) or Rate Limiting (previous version), consider upgrading to the new versions of these products. It is not possible to turn off these older products using modern Rules features. :::
Migrate Disable Zaraz
Context:
You configured a Page Rule turning off Zaraz for all subdomains of example.com and the example.com domain itself:
- URL:
*example.com/* - Setting: Disable Zaraz
How to migrate:
-
Create a configuration rule to turn off Zaraz for any hostname containing
example.com:- When incoming requests match: Custom filter expression
- Using the Expression Builder:
Hostname contains "example.com" - Using the Expression Editor:
(http.host contains "example.com")
- Using the Expression Builder:
- Then the settings are:
- Setting: Disable Zaraz
- When incoming requests match: Custom filter expression
-
Turn off your existing Page Rule and validate the behavior of the configuration rule you created.
-
If your tests succeed, delete the existing Page Rule.
| Page Rules configuration | Migrate to a configuration rule |
|---|---|
![]() |
![]() |
Migrate Edge Cache TTL
Context:
You configured a Page Rule adjusting Edge Cache TTL for all subdomains of example.com and the example.com domain itself:
- URL:
*example.com/* - Setting: Edge Cache TTL
- Enter Edge Cache TTL: a day
How to migrate:
-
Create a cache rule to adjust edge cache TTL for caching resources on Cloudflare edge to one day, for any hostname containing
example.com:- When incoming requests match: Custom filter expression
- Using the Expression Builder:
Hostname contains "example.com" - Using the Expression Editor:
(http.host contains "example.com")
- Using the Expression Builder:
- Then:
- Cache eligibility: Eligible for cache
- Setting: Edge TTL
- Ignore cache-control header and use this TTL
- Input time-to-live (TTL): 1 day
- When incoming requests match: Custom filter expression
-
Turn off your existing Page Rule and validate the behavior of the cache rule you created.
-
If your tests succeed, delete the existing Page Rule.
| Page Rules configuration | Migrate to a cache rule |
|---|---|
![]() |
![]() |
Migrate Email Obfuscation
Context:
You configured a Page Rule turning off Email Obfuscation for all subdomains of example.com and the example.com domain itself:
- URL:
*example.com/* - Setting: Email Obfuscation
- Value: Off
How to migrate:
-
Create a configuration rule to turn off Email Obfuscation for any hostname containing
example.com:- When incoming requests match: Custom filter expression
- Using the Expression Builder:
Hostname contains "example.com" - Using the Expression Editor:
(http.host contains "example.com")
- Using the Expression Builder:
- Then the settings are:
- Setting: Email Obfuscation
- Value: Off
- Setting: Email Obfuscation
- When incoming requests match: Custom filter expression
-
Turn off your existing Page Rule and validate the behavior of the configuration rule you created.
-
If your tests succeed, delete the existing Page Rule.
| Page Rules configuration | Migrate to a configuration rule |
|---|---|
![]() |
![]() |
Migrate Forwarding URL
Example #1: Redirect www to root domain
Context:
You configured a Page Rule permanently redirecting www.example.com to example.com on all URI paths:
- URL:
www.example.com/* - Setting: Forwarding URL
- Select Status code: 301 - Permanent Redirect
- Destination URL:
https://example.com/$1
How to migrate:
-
Create a single redirect to permanently redirect requests from
https://www.example.comtohttps://example.com. You can select the Redirect from WWW to Root rule template or enter the following rule configuration:- If incoming requests match: Wildcard pattern
- Request URL:
https://www.example.com/*
- Request URL:
- Then:
- Target URL:
https://example.com/${1} - Status code: 301
- Preserve query string: Enabled
- Target URL:
- If incoming requests match: Wildcard pattern
-
Turn off your existing Page Rule and validate the behavior of the redirect you created.
-
If your tests succeed, delete the existing Page Rule.
<Render file="page-rules-migration-wildcard-notice" product="rules" params={{ requestUrl: "http*://www.example.com/*", targetUrl: "https://example.com/${2}", }} />
| Page Rules configuration | Migrate to a single redirect |
|---|---|
![]() |
![]() |
Example #2: Redirect all pages under old path to new path
Context:
You configured a Page Rule permanently redirecting example.com/old-path to example.com/new-path:
- URL:
example.com/old-path/* - Setting: Forwarding URL
- Select Status code: 301 - Permanent Redirect
- Destination URL:
https://example.com/new-path/$1
How to migrate:
-
Create a single redirect to permanently redirect requests for
example.com/old-pathtoexample.com/new-path:- If incoming requests match: Wildcard pattern
- Request URL:
https://example.com/old-path/*
- Request URL:
- Then:
- Target URL:
https://example.com/new-path/${1} - Status code: 301
- Preserve query string: Enabled
- Target URL:
- If incoming requests match: Wildcard pattern
-
Turn off your existing Page Rule and validate the behavior of the redirect you created.
-
If your tests succeed, delete the existing Page Rule.
<Render file="page-rules-migration-wildcard-notice" product="rules" params={{ requestUrl: "http*://example.com/old-path/*", targetUrl: "https://example.com/new-path/${2}", }} />
| Page Rules configuration | Migrate to a single redirect |
|---|---|
![]() |
![]() |
Migrate Host Header Override
Context:
You configured a Page Rule changing the Host HTTP header to example.saas-provider.com, for all requests addressed at any subdomain of example.com and the example.com domain itself:
- URL:
*example.com/* - Setting: Host Header Override
- Enter value:
example.saas-provider.com
How to migrate:
-
Create an origin rule changing the
Hostheader toexample.saas-provider.comfor any hostname containingexample.com:- When incoming requests match: Custom filter expression
- Using the Expression Builder:
Hostname contains "example.com" - Using the Expression Editor:
(http.host contains "example.com")
- Using the Expression Builder:
- Then:
- Set origin parameters:
- Host Header > Rewrite to:
example.saas-provider.com
- Host Header > Rewrite to:
- Set origin parameters:
- When incoming requests match: Custom filter expression
-
Turn off your existing Page Rule and validate the behavior of the origin rule you created.
-
If your tests succeed, delete the existing Page Rule.
| Page Rules configuration | Migrate to an origin rule |
|---|---|
![]() |
![]() |
Migrate IP Geolocation Header
Context:
You configured a Page Rule adding a CF-IPCountry HTTP header, for all requests addressed at any subdomain of example.com and the example.com domain itself:
- URL:
*example.com/* - Setting: IP Geolocation Header
- Value: On
How to migrate:
- Turn on the Add visitor location headers Managed Transform — a Transform Rules feature — to add the
CF-IPCountryand other location headers to all requests. - Turn off your existing Page Rule and validate the behavior of the Managed Transform.
- If your tests succeed, delete the existing Page Rule.
| Page Rules configuration | Migrate to a Managed Transform |
|---|---|
![]() |
![]() |
Migrate Opportunistic Encryption
Context:
You configured a Page Rule turning off Opportunistic Encryption for all subdomains of example.com and the example.com domain itself:
- URL:
*example.com/* - Setting: Opportunistic Encryption
- Value: Off
How to migrate:
-
Create a configuration rule to turn off Opportunistic Encryption for any hostname containing
example.com:- When incoming requests match: Custom filter expression
- Using the Expression Builder:
Hostname contains "example.com" - Using the Expression Editor:
(http.host contains "example.com")
- Using the Expression Builder:
- Then the settings are:
- Setting: Opportunistic Encryption
- Value: Off
- Setting: Opportunistic Encryption
- When incoming requests match: Custom filter expression
-
Turn off your existing Page Rule and validate the behavior of the configuration rule you created.
-
If your tests succeed, delete the existing Page Rule.
| Page Rules configuration | Migrate to a configuration rule |
|---|---|
![]() |
![]() |
Migrate Origin Cache Control
Context:
You configured a Page Rule turning off Origin Cache Control for all subdomains of example.com and the example.com domain itself:
- URL:
*example.com/* - Setting: Origin Cache Control
- Value: Off
How to migrate:
-
Create a cache rule to determine edge cache behavior for any hostname containing
example.com:- When incoming requests match: Custom filter expression
- Using the Expression Builder:
Hostname contains "example.com" - Using the Expression Editor:
(http.host contains "example.com")
- Using the Expression Builder:
- Then:
- Cache eligibility: Eligible for cache
- Setting: Origin Cache Control
- Enable Origin Cache Control: Off
- When incoming requests match: Custom filter expression
-
Turn off your existing Page Rule and validate the behavior of the cache rule you created.
-
If your tests succeed, delete the existing Page Rule.
| Page Rules configuration | Migrate to a cache rule |
|---|---|
![]() |
![]() |
Migrate Origin Error Page Pass-thru
Context:
You configured a Page Rule turning on Origin Error Page Pass-thru for all subdomains of example.com and the example.com domain itself:
- URL:
*example.com/* - Setting: Origin Error Page Pass-thru
- Value: On
How to migrate:
-
Create a cache rule to determine edge cache behavior for any hostname containing
example.com:- When incoming requests match: Custom filter expression
- Using the Expression Builder:
Hostname contains "example.com" - Using the Expression Editor:
(http.host contains "example.com")
- Using the Expression Builder:
- Then:
- Cache eligibility: Eligible for cache
- Setting: Origin error page pass-thru
- Use Origin error page pass-thru: On
- When incoming requests match: Custom filter expression
-
Turn off your existing Page Rule and validate the behavior of the cache rule you created.
-
If your tests succeed, delete the existing Page Rule.
| Page Rules configuration | Migrate to a cache rule |
|---|---|
![]() |
![]() |
Migrate Polish
Context:
You configured a Page Rule turning off Polish for all subdomains of example.com and the example.com domain itself:
- URL:
*example.com/* - Setting: Polish
- Value: Off
How to migrate:
-
Create a configuration rule to turn off Polish for any hostname containing
example.com:- When incoming requests match: Custom filter expression
- Using the Expression Builder:
Hostname contains "example.com" - Using the Expression Editor:
(http.host contains "example.com")
- Using the Expression Builder:
- Then the settings are:
- Setting: Polish
- Select value: Off
- Setting: Polish
- When incoming requests match: Custom filter expression
-
Turn off your existing Page Rule and validate the behavior of the configuration rule you created.
-
If your tests succeed, delete the existing Page Rule.
| Page Rules configuration | Migrate to a configuration rule |
|---|---|
![]() |
![]() |
Migrate Query String Sort
Context:
You configured a Page Rule turning on Query String Sort for all subdomains of example.com and the example.com domain itself:
- URL:
*example.com/* - Setting: Query String Sort
- Value: On
How to migrate:
-
Create a cache rule to sort query string parameters for caching purposes, for any hostname containing
example.com:- When incoming requests match: Custom filter expression
- Using the Expression Builder:
Hostname contains "example.com" - Using the Expression Editor:
(http.host contains "example.com")
- Using the Expression Builder:
- Then:
- Cache eligibility: Eligible for cache
- Setting: Cache key
- Sort query string: On
- When incoming requests match: Custom filter expression
-
Turn off your existing Page Rule and validate the behavior of the cache rule you created.
-
If your tests succeed, delete the existing Page Rule.
| Page Rules configuration | Migrate to a cache rule |
|---|---|
![]() |
![]() |
Migrate Resolve Override
Context:
You configured a Page Rule changing the origin to example.saas-provider.com, for all requests addressed at any subdomain of example.com and the example.com domain itself:
- URL:
*example.com/* - Setting: Resolve Override
- Enter value:
example.saas-provider.com
How to migrate:
-
Create an origin rule overriding the origin to
example.saas-provider.comfor any hostname containingexample.com:- When incoming requests match: Custom filter expression
- Using the Expression Builder:
Hostname contains "example.com" - Using the Expression Editor:
(http.host contains "example.com")
- Using the Expression Builder:
- Then:
- DNS Record > Override to:
example.saas-provider.com
- DNS Record > Override to:
- When incoming requests match: Custom filter expression
-
Turn off your existing Page Rule and validate the behavior of the origin rule you created.
-
If your tests succeed, delete the existing Page Rule.
| Page Rules configuration | Migrate to an origin rule |
|---|---|
![]() |
![]() |
Migrate Respect Strong ETags
Context:
You configured a Page Rule turning on byte-for-byte equivalency checks for all subdomains of example.com and the example.com domain itself:
- URL:
*example.com/* - Setting: Respect Strong ETags
- Value: On
How to migrate:
-
Create a cache rule to respect strong ETags for any hostname containing
example.com:- When incoming requests match: Custom filter expression
- Using the Expression Builder:
Hostname contains "example.com" - Using the Expression Editor:
(http.host contains "example.com")
- Using the Expression Builder:
- Then:
- Cache eligibility: Eligible for cache
- Setting: Respect strong ETags
- Use strong ETag headers: On
- When incoming requests match: Custom filter expression
-
Turn off your existing Page Rule and validate the behavior of the cache rule you created.
-
If your tests succeed, delete the existing Page Rule.
| Page Rules configuration | Migrate to a cache rule |
|---|---|
![]() |
![]() |
Migrate Rocket Loader
Context:
You configured a Page Rule turning off Rocket Loader for all subdomains of example.com and the example.com domain itself:
- URL:
*example.com/* - Setting: Rocket Loader
- Value: Off
How to migrate:
-
Create a configuration rule to turn off Rocket Loader for any hostname containing
example.com:- When incoming requests match: Custom filter expression
- Using the Expression Builder:
Hostname contains "example.com" - Using the Expression Editor:
(http.host contains "example.com")
- Using the Expression Builder:
- Then the settings are:
- Setting: Rocket Loader
- Value: Off
- Setting: Rocket Loader
- When incoming requests match: Custom filter expression
-
Turn off your existing Page Rule and validate the behavior of the configuration rule you created.
-
If your tests succeed, delete the existing Page Rule.
| Page Rules configuration | Migrate to a configuration rule |
|---|---|
![]() |
![]() |
Migrate Security Level
Context:
You configured a Page Rule setting Security Level to I'm Under Attack for all subdomains of example.com and the example.com domain itself:
- URL:
*example.com/* - Setting: Security Level
- Select Security Level: I'm Under Attack
How to migrate:
-
Create a configuration rule to set Security Level to I'm Under Attack, for any hostname containing
example.com:- When incoming requests match: Custom filter expression
- Using the Expression Builder:
Hostname contains "example.com" - Using the Expression Editor:
(http.host contains "example.com")
- Using the Expression Builder:
- Then the settings are:
- Setting: I'm Under Attack
- Value: On
- When incoming requests match: Custom filter expression
-
Turn off your existing Page Rule and validate the behavior of the configuration rule you created.
-
If your tests succeed, delete the existing Page Rule.
| Page Rules configuration | Migrate to a configuration rule |
|---|---|
![]() |
![]() |
Migrate True Client IP Header
Context:
You configured a Page Rule adding a True-Client-IP HTTP header for all requests addressed at any subdomain of example.com and the example.com domain itself:
- URL:
*example.com/* - Setting: True Client IP Header
- Value: On
How to migrate:
- Turn on the Add "True-Client-IP" header Managed Transform — a Transform Rules feature — to add the
True-Client-IPheader to all requests. - Turn off your existing Page Rule and validate the behavior of the Managed Transform.
- If your tests succeed, delete the existing Page Rule.
| Page Rules configuration | Migrate to a Managed Transform |
|---|---|
![]() |
![]() |
Migrate SSL
Context:
You configured a Page Rule setting SSL to Strict for all subdomains of example.com and the example.com domain itself:
- URL:
*example.com/* - Setting: SSL
- Select SSL/TLS encryption mode: Strict
How to migrate:
-
Create a configuration rule to set SSL to Strict, for any hostname containing
example.com:- When incoming requests match: Custom filter expression
- Using the Expression Builder:
Hostname contains "example.com" - Using the Expression Editor:
(http.host contains "example.com")
- Using the Expression Builder:
- Then the settings are:
- Setting: SSL
- Select SSL/TLS encryption mode: Strict
- Setting: SSL
- When incoming requests match: Custom filter expression
-
Turn off your existing Page Rule and validate the behavior of the configuration rule you created.
-
If your tests succeed, delete the existing Page Rule.
| Page Rules configuration | Migrate to a configuration rule |
|---|---|
![]() |
![]() |
Settings that will not be migrated
The following Page Rules settings will not be migrated to other types of rules:
- Disable Performance (this setting is deprecated)
- Disable Railgun (this setting is deprecated, since Railgun is no longer available)
- Disable Security (this setting is deprecated)
- Response Buffering (this setting is deprecated)
- Web Application Firewall (this setting is deprecated, since the previous version of WAF managed rules is deprecated)
All other Page Rules settings will be migrated during 2025.
More resources
If you have feedback to share, refer to our Community thread.
Troubleshoot Rules
import { Example, Render } from "~/components";
Interaction between redirects and other Cloudflare products
Your redirects may interfere with Cloudflare products and features such as challenges. Consider excluding the /cdn-cgi/* URI path in your rule expression to avoid issues. Alternatively, you may exclude only a sub-path such as /cdn-cgi/challenge-platform/* to avoid issues with specific features (in this example, Cloudflare challenges).
You may also want to exclude the /.well-known/* URL path used by several validation services. Refer to Interaction between redirects and verification procedures like HTTP DCV for more information.
Interaction between Cloudflare challenges and Rules features
If you are issuing a challenge for a given URI path that has one or more Rules features enabled, you should exclude URI paths starting with /cdn-cgi/challenge-platform/ in your rule expressions to avoid challenge loops.
For example, define a compound expression for your rule using the and operator and the starts_with() function:
<OTHER_RULE_CONDITIONS> and not starts_with(http.request.uri, "/cdn-cgi/challenge-platform/")
Interaction between redirects and verification procedures like HTTP DCV
Paths used in validation procedures such as custom hostname verification (Cloudflare for SaaS), Pages domain validation, or HTTP domain control validation (DCV) may be affected by redirects.
Consider excluding the /.well-known/* URI path from your rule to avoid issues.
Content-Length header removed from response
Cloudflare may remove the Content-Length header from responses delivered to website visitors. If the visitor must receive the Content-Length header, configure the origin server to include a cache-control: no-transform HTTP header in the response.
This rule may not apply to your traffic
If your rule expression is matching a hostname for which you have neither created a DNS record nor enabled proxying traffic through Cloudflare, you will get a pop-up window with a couple of options:
- If no DNS record exists for the hostname: Whether to proceed with the rule creation or to create a new proxied DNS record for that hostname.
- If there is a DNS record for the hostname, but traffic is not being proxied: Whether to proceed with the rule creation or to enable proxying for the existing DNS record.
If you choose to create a new DNS record, the new record will have a rules tag and the following associated comment:
Created during Cloudflare Rules deployment process for <RULE_NAME>
URL rewrites affect other Rules features executed later
If you rewrite a URI path using a URL rewrite, this may affect other Rules features executed later — such as Origin Rules — if they include the URI path in their filter expression.
Consider the following origin rule configuration:
- Rule expression:
http.host == "example.com" and starts_with(http.request.uri.path, "/downloads/") - Host header > Rewrite to:
assets.example.com
If you configure a new URL rewrite with the following configuration:
- Rule expression:
http.host == "example.com" and starts_with(http.request.uri.path, "/downloads/") - Path > Rewrite to > Dynamic:
regex_replace(http.request.uri.path, "^/downloads/", "/")
The origin rule will no longer match /downloads/* paths, since URL rewrites run before Origin Rules and the URI path will be rewritten from "/downloads/" to "/".
Solution
To prevent this situation, use raw fields in your rule expression. Raw fields are immutable during the entire request evaluation workflow, and they are not affected by the actions of previously matched rules.
In the current example, you could use the raw.http.request.uri.path field in both rules:
URL rewrite
- Rule expression:
http.host == "example.com" and starts_with(raw.http.request.uri.path, "/downloads/") - Path > Rewrite to > Dynamic:
regex_replace(raw.http.request.uri.path, "^/downloads/", "/")
Origin rule
- Rule expression:
http.host == "example.com" and starts_with(raw.http.request.uri.path, "/downloads/") - Host header > Rewrite to:
assets.example.com
This way, the two rules will work as intended. Additionally, this allows you to use the same expression in the two rules, even when the first rule is updating the URI path value.
For a list of raw fields, refer to the Fields reference.
Rules simulator changelog
import { ProductReleaseNotes } from "~/components";
{/* */}
Use Rules simulator
import { GlossaryTooltip, DashButton, Steps } from "~/components";
Use Rules simulator in the dashboard
1. Configure one or more Cloudflare products
- Log in to the Cloudflare dashboard, and select your account.
- Set configuration settings at the account level, or select a domain and configure settings for one or more Cloudflare products.
2. Build a simulation
-
In the Cloudflare dashboard, go to the Simulator page.
-
Enter a URL to simulate. The URL must include a hostname that belongs to your account.
-
Select an HTTP method. If you select POST, PUT, or PATCH, you should enter a value in Request Body.
-
(Optional) Define any custom request properties to simulate the conditions of a specific HTTP/S request. You can customize the following request properties:
-
Select Run simulation.
3. Assess results
The Simulation results page shows all evaluated and executed configurations from different Cloudflare products, in evaluation order. Any inactive rules are not evaluated.
-
Analyze the different steps with evaluated and executed configurations for the current simulation. Simulation results include matches for all active rules and configurations, whether configured at the account level or for a specific domain or subdomain.
To show all configurations, including the ones that did not match the request, select All configurations in the Results shown dropdown.
-
(Optional) Update your Cloudflare configuration (at the account or at the domain/subdomain level) and run a new simulation to check the impact of your changes.
4. (Optional) Save the simulation configuration
To run a simulation later with the same configuration:
- Copy the JSON shown in the dashboard with the current simulation configuration.
- When creating a new simulation, paste it in the JSON box to define all the settings of the new simulation.
Use Rules simulator via API
Use the Request Trace operation to perform a simulation using the Cloudflare API.
Steps in simulation results
- Execution of one or more rules of Cloudflare products built on the Ruleset Engine. Refer to the Ruleset Engine's Phases list for a list of such products.
- Page Rules: Execution of one or more rules.
- Workers: Execution of one or more scripts.
Rules simulator
import { DirectoryListing, Plan, ProductAvailabilityText, Render, } from "~/components";
Rules simulator simulates an HTTP/S request through Cloudflare's network to your origin server. Use this tool to understand how your Cloudflare configurations (such as rules, caching, and security settings) would affect a specific request. If the hostname you are testing is not proxied by Cloudflare, Rules simulator will still return all the configurations that Cloudflare would have applied to the request.
You can define specific request properties to simulate different conditions for an HTTP/S request. Rules that are turned off in Cloudflare products will not be evaluated.
Rules simulator is available to users with an Administrator or Super Administrator role.
When to use Rules simulator
Use Log Explorer when you need to investigate what actually happened with real production traffic:
- Analyzing historical data and trends
- Investigating security incidents after they occur
- Searching for patterns across thousands of requests
- Monitoring application performance over time
- Providing forensic evidence to support teams
The key difference is that Rules simulator simulates "what-if" scenarios, while Log Explorer shows actual historical traffic.
Resources
Rules simulator limitations
Rules simulator does not display rules that are automatically bypassed for operational reasons.
For example, when SSL/TLS certificates are in pending_validation status, security rules are automatically disabled for domain control validation (DCV) paths like /.well-known/pki-validation/ and /.well-known/acme-challenge/. These bypasses will not appear in simulation results.
For more information, refer to Why are some rules bypassed? in the WAF documentation.
Unsupported features
Rules simulator currently does not support:
- Hostnames using Data Localization Suite
- Spectrum applications
Additionally, the following products will not appear in simulation results:
- Firewall rules (deprecated)
- Load Balancing and Load Balancer Custom Rules
- IP Access rules
- Rate limiting rules (previous version)
- WAF managed rules (previous version)
- Content security rules
Configure Snippets via API
import { APIRequest } from "~/components";
You can create Snippets using the Cloudflare API.
Required permissions
The API token used in API requests to manage Snippets must have at least the following permission:
- Zone > Snippets > Edit
:::note
A token with this permission is only valid for the Snippets endpoints described in this page. You cannot use it to interact with the http_request_snippets phase via Rulesets API.
:::
Endpoints
To obtain the complete endpoint, append the Snippets endpoints listed below to the Cloudflare API base URL:
https://api.cloudflare.com/client/v4
The {zone_id} argument is the zone ID (a hexadecimal string). You can find this value in the Cloudflare dashboard.
The following table summarizes the available operations.
| Operation | Verb + Endpoint |
|---|---|
| List all code snippets | GET /zones/{zone_id}/snippets |
| Create/update code snippet | PUT /zones/{zone_id}/snippets/{snippet_name} |
| Get code snippet details | GET /zones/{zone_id}/snippets/{snippet_name} |
| Get code snippet content | GET /zones/{zone_id}/snippets/{snippet_name}/content |
| Delete code snippet | DELETE /zones/{zone_id}/snippets/{snippet_name} |
| List snippet rules | GET /zones/{zone_id}/snippets/snippet_rules |
| Create/update/delete snippet rules | PUT /zones/{zone_id}/snippets/snippet_rules |
| Delete all snippet rules | DELETE /zones/{zone_id}/snippets/snippet_rules |
Example API calls
Create/update code snippet
To create or update a Snippet, use the following PUT request. The snippet is named $SNIPPET_NAME and the body contains the JavaScript code.
<APIRequest path="/zones/{zone_id}/snippets/{snippet_name}" method="PUT" form={{ files: "@example.js", metadata: '{"main_module": "example.js"}', }} />
The name of a snippet can only contain the characters a-z, 0-9, and _ (underscore). The name must be unique in the context of the zone. You cannot change the snippet name after creating the snippet.
The required body parameters are:
files: One or more JavaScript files. Every file is treated as a JavaScript ES module. The main module mustexport defaultan object with afetchhandler. Additional files can be imported from the main module by relative path (for example,import { helper } from "./helper.js"). Unlike Workers, Snippets do not support non-ESM files such as JSON, text, binary, or WebAssembly files, and reject them when you upload them.metadata: Object containingmain_module, which must match the filename of the uploaded file that contains the default export.
To make this example work, save your JavaScript code in a file named example.js, and then execute curl command with a PUT request from the folder where example.js is located.
{
"errors": [],
"messages": [],
"success": true,
"result": {
"created_on": "2023-07-24-00:00:00",
"modified_on": "2023-07-24-00:00:00",
"snippet_name": "snippet_name_01"
}
}
To deploy a new snippet you must create a snippet rule. The expression of the snippet rule defines when the snippet code will run.
Create/update/delete snippet rules
:::caution When using this endpoint to create a new rule and keep existing rules, you must include all rules in the request body. Omitting an existing rule will delete the corresponding rule. :::
Once you have created a code snippet, you can link it to rules. This is done via the following PUT request to the snippet_rules endpoint.
<APIRequest path="/zones/{zone_id}/snippets/snippet_rules" method="PUT" json={{ rules: [ { description: "Trigger snippet on specific cookie", enabled: true, expression: 'http.cookie eq "a=b"', snippet_name: "snippet_name_01", }, ], }} />
Create a snippet in the dashboard
import { Render, Steps, DashButton } from "~/components";
-
In the Cloudflare dashboard, go to the Snippets page.
-
(Optional) If you have not created any snippets yet, select one of the snippet templates that addresses a common use case. Then, review and adjust the proposed snippet code.
To start from scratch, select Create Snippet.
-
In Snippet name, enter a descriptive name for the snippet. You cannot change the name after creating the snippet.
-
Enter the snippet's JavaScript code in the code editor. You can test how your snippet will handle incoming requests using the HTTP and Preview tabs.
-
Select Snippet rule to configure when the snippet will run.
-
Under Run this Snippet if incoming requests match, select if you wish to run the snippet only for requests that match a custom filter expression or for all incoming requests.
-
(Optional) To define a custom expression, use the Expression Builder (specifying one or more values for Field, Operator, and Value) or manually enter an expression using the Expression Editor. For more information, refer to Edit expressions in the dashboard.
-
Select Done.
-
To deploy your snippet, select Deploy. If you are not ready to deploy your snippet, open the dropdown next to Deploy and select Save as Draft.
Configure Snippets using Terraform
import { Render } from "~/components";
You can create Snippets using the Terraform Cloudflare provider.
To get started with Terraform for Cloudflare configuration, refer to Get started.
Example configuration
The following example Terraform configuration creates a snippet and an associated snippet rule that defines when the snippet code will run. The snippet code is loaded from the file1.js file in your machine.
resource "cloudflare_snippet" "my_snippet" {
zone_id = "<ZONE_ID>"
name = "my_test_snippet_1"
main_module = "file1.js"
files {
name = "file1.js"
content = file("file1.js")
}
}
resource "cloudflare_snippet_rules" "cookie_snippet_rule" {
zone_id = "<ZONE_ID>"
rules {
enabled = true
expression = "http.cookie eq \"a=b\""
description = "Trigger snippet on specific cookie"
snippet_name = "my_test_snippet_1"
}
depends_on = [cloudflare_snippet.my_snippet]
}
The name of a snippet can only contain the characters a-z, 0-9, and _ (underscore). The name must be unique in the context of the zone. You cannot change the snippet name after creating the snippet.
All snippet_name values in the cloudflare_snippet_rules resource must match the names of existing snippets.
More resources
Refer to the Terraform Cloudflare provider documentation for more information on the cloudflare_snippet and cloudflare_snippet_rules resources.
Troubleshoot Snippets
import { GlossaryTooltip } from "~/components";
Error 1201: Snippet tried to continue to origin multiple times
This error occurs when a Snippet attempts to call fetch(request) more than once.
Resolution
Ensure that your Snippet code only calls fetch(request) once. This method is used to send the modified request to the origin server, and it should be called only once per Snippet to avoid conflicts.
Error 1202: Snippets exceeded subrequests limit
This error occurs when the number of subrequests exceeds the limit for your Cloudflare plan.
Resolution
Review your Snippet to ensure your code is within the subrequest limits for your plan. Each subrequest counts against your limit, including any redirects within a subrequest chain.
Error 1203: Snippets exceeded CPU time limit
This error occurs when a Snippet exceeds the defined CPU time limit for Snippets.
Resolution
Review your Snippet to ensure your code is within the CPU time limit. If you need a higher CPU time limit, consider using Cloudflare Workers. Refer to When to use Snippets vs Workers for details.
Error 1204: Snippets exceeded memory limit
This error occurs when a Snippet exceeds the defined memory limit for Snippets.
Resolution
Review your Snippet to ensure your code is within the memory limit. If you need a higher memory limit, consider using Cloudflare Workers. Refer to When to use Snippets vs Workers for details.
Error 1205: Deployment in progress
A new Snippet was just deployed and is currently propagating across Cloudflare's global network. During this short window, requests may not be processed as expected.
Resolution
This is a temporary issue. Retry your request after a few seconds — the Snippet will be active once propagation completes.
Error 1206: Snippet threw exception
The Snippet encountered an unhandled JavaScript exception during execution. This may be caused by one of the following:
- Runtime JavaScript errors in Snippet code
- Unhandled promise rejections
- Type errors or reference errors
Resolution
- Review the Snippet code to identify where the exception might have occurred and fix any detected bugs.
- Add proper error handling (
try...catchblocks).
Snippets cannot be renamed
The name you define when creating a Snippet will be used as the Snippet ID and cannot be edited afterwards.
Resolution
To change the name of your Snippet, create a new Snippet and delete the old one.
How Snippets work
Cloudflare Snippets are executed based on rules defined within your zone. Here is how the process works:

1. Evaluate snippet rules
For each incoming request, Cloudflare evaluates the expression of every snippet rule defined in the zone. The evaluation checks for a match based on various request properties (such as bot score, WAF attack score, country of origin, and cookies).
2. Build Snippets table
For every snippet rule in a zone that matches an incoming request, Cloudflare adds the corresponding unique snippet ID to a Snippets table.
3. Execute snippets code
Once all the rules have been evaluated and the full table has been compiled, Cloudflare starts processing all the snippet IDs in the table.
The snippets are executed sequentially. Each snippet receives the modified request from the previous snippet and applies new modifications to it.
4. Continue with the request execution workflow
After executing the final snippet IDs, the resulting modified request is passed back to the request execution workflow. Refer to Execution order for more information on the Rules features evaluated before and after Cloudflare Snippets.
Subrequests made by a snippet
When a snippet calls fetch() to a proxied hostname in the same zone, the request that the snippet makes is a subrequest. The incoming request that triggered the snippet is evaluated as normal, including by the security features configured for the hostname the visitor requested.
WAF custom rules and WAF managed rules on the target hostname are not evaluated for a same-zone subrequest made by a snippet. This applies to both fetch(url) and fetch(request).
:::caution Do not rely on custom rules on the target hostname to restrict access to subrequests made by a snippet. To protect the target, apply protections to the incoming request that triggers the snippet, or validate the request at the target itself (for example, in your origin server or in a Worker). :::
Cloudflare Snippets
import { GlossaryTooltip, Render, Card } from "~/components";
Cloudflare Snippets allow you to run short pieces of JavaScript code on Cloudflare's network to customize how requests and responses are handled for your website or application. With Snippets, you can modify HTTP response headers, implement JWT validation, perform complex redirects, and more.
For code samples addressing common use cases, refer to the Examples section.
<Render file="rules-requirements" product="rules" params={{ sentenceIntro: "Snippets require" }} />
Snippets elements
To create and deploy a snippet, you need to define the following elements:
- Snippet rule: A filter expression that determines which requests the Snippet will be applied to. Each snippet can only be associated with one snippet rule.
- Code snippet: JavaScript code that runs on the Cloudflare network when a request matches the associated snippet rule.
For more information, refer to How Snippets work and Create a snippet in the dashboard.
:::note If you have used the Cloudflare API to create a code snippet that is not associated with a snippet rule, the Cloudflare dashboard will show that code snippet in a separate tab called Unused Snippets. You can either edit the snippet code and associate it with a snippet rule, or delete the unused code snippet. :::
Templates
Availability
| Free | Pro | Business | Enterprise | |
|---|---|---|---|---|
| Availability | No | Yes | Yes | Yes |
| Number of snippets | 0 | 25 | 50 | 300 |
| Number of snippet subrequests | 0 | 2 | 3 | 5 |
Each subrequest in a redirect chain counts against the subrequest limit. This means that if a subrequest was redirected it would count as two subrequests. To avoid issues, ensure that you make a subrequest to the end location of the redirect chain.
Currently, Version Management does not support Snippets.
Limits
Cloudflare Snippets are designed for fast, lightweight logic that runs on the Cloudflare network. The following limits apply:
| Description | All plans |
|---|---|
| Maximum execution time | 5 ms |
| Maximum memory | 2 MB |
| Maximum total package size | 32 KB |
Explore our detailed guide for best practices, real-world use cases, and example implementations.
Execution order
When to use Snippets vs Workers
This guide helps you determine when to use Snippets or Workers on Cloudflare's global network. It provides best practices, comparisons, and real-world use cases to help you choose the right product for your workload.
What are Snippets?
Cloudflare Snippets provide a fast, declarative way to modify HTTP requests and responses at the edge — without requiring a full-stack compute platform. Snippets extend Cloudflare Rules by allowing you to write JavaScript-based logic that modifies requests before they reach an origin and responses after they return from upstream.
Snippets enable you to:
- Modify headers, validate JWTs, and implement complex rewrites or redirects.
- Retry failed requests to different origins and apply custom caching strategies.
- Execute multiple Snippets sequentially, with each Snippet modifying the request or response before handing it off to the next.
Snippets are included at no additional cost in all paid plans, making them the preferred solution for lightweight edge logic.
What are Workers?
By contrast, Cloudflare Workers provide a full-stack compute platform designed for applications requiring state, compute, and integrations with Cloudflare’s Developer Platform. Workers operate on a usage-based pricing model and include a free tier.
Choosing the right product
Snippets are ideal for fast, cost-free request and response modifications at the edge. They extend Cloudflare Rules without requiring additional infrastructure or external solutions.
When to use Snippets
- Ultra-fast traffic modifications applied directly on Cloudflare's network.
- Extend Cloudflare Rules beyond built-in actions for greater control.
- Simplify CDN migrations by replacing VCL, EdgeWorkers, or on-premise logic.
- Modify headers, cache responses, and perform redirects.
- Integrate edge logic into development workflows using JavaScript.
What Snippets are not designed for
- Persistent state management (for example, session storage or databases).
- Compute-intensive tasks (for example, image transformations or AI inference).
- Deep integrations with Developer Platform services like Durable Objects or D1.
- Use cases requiring advanced runtime features, such as:
Key features
- Ultra-fast, edge-optimized execution, powered by Ruleset Engine and Workers runtime.
- Included at no additional cost on all paid plans.
- Granular request matching using dozens of request attributes, such as URI, user-agent, and cookies.
- Sequential execution – multiple Snippets can run on the same request, applying modifications step by step.
- Native integration with Cloudflare Rules – Snippets inherit request modifications from other products running in earlier request phases.
- JavaScript and Web APIs support, including:
- Essential Workers runtime features, such as:
- Automated deployment and versioning via Terraform.
Snippets vs Workers: Feature comparison
| Feature | Snippets | Workers |
|---|---|---|
| Execute scripts based on request attributes (for example, headers, geolocation, and cookies) | ✅ | ❌ |
| Execute code on a specific URL route | ✅ | ✅ |
| Modify HTTP requests/responses or serve a different response | ✅ | ✅ |
| Add, remove, or rewrite headers dynamically | ✅ | ✅ |
| Cache assets at the edge | ✅ | ✅ |
| Route traffic dynamically between origin servers | ✅ | ✅ |
| Authenticate requests, pre-sign URLs, run A/B testing | ✅ | ✅ |
| Define logic using JavaScript and Web APIs | ✅ | ✅ |
| Perform compute-heavy tasks (for example, AI, image transformations) | ❌ | ✅ |
| Store persistent data (for example, KV, Durable Objects, and D1) | ❌ | ✅ |
| Build APIs and full-stack applications | ❌ | ✅ |
| Use TypeScript, Python, Rust, or other programming languages | ❌ | ✅ |
| Support non-HTTP protocols | ❌ | ✅ |
| Analyze execution logs and track performance metrics | ❌ | ✅ |
| Deploy via command-line interface (CLI) | ❌ | ✅ |
| Roll out gradually, roll back to previous versions | ❌ | ✅ |
| Optimize execution with Smart Placement | ❌ | ✅ |
Code examples: Common Snippets templates
Below are practical use cases demonstrating Snippets in action. You can find more templates to get started in the Examples section.
Modify HTTP headers
Modifies request and response headers dynamically.
export default {
async fetch(request) {
// Get the current timestamp
const timestamp = Date.now();
// Convert the timestamp to hexadecimal format
const hexTimestamp = timestamp.toString(16);
// Clone the request and add the custom header with HEX timestamp
const modifiedRequest = new Request(request, {
headers: new Headers(request.headers),
});
modifiedRequest.headers.set("X-Hex-Timestamp", hexTimestamp);
// Pass the modified request to the origin
const response = await fetch(modifiedRequest);
// Clone the response so that it's no longer immutable
const newResponse = new Response(response.body, response);
// Add a custom header with a value to the response
newResponse.headers.append(
"x-snippets-hello",
"Hello from Cloudflare Snippets",
);
// Delete headers from the response
newResponse.headers.delete("x-header-to-delete");
newResponse.headers.delete("x-header2-to-delete");
// Adjust the value for an existing header in the response
newResponse.headers.set("x-header-to-change", "NewValue");
// Serve modified response to the visitor
return newResponse;
},
};
Serve a custom maintenance page
Routes traffic to a maintenance page when your origin is undergoing a planned maintenance.
export default {
async fetch(request) {
return new Response(
`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>We'll Be Right Back!</title>
<style> body { font-family: Arial, sans-serif; text-align: center; padding: 20px; } </style>
</head>
<body>
<h1>We'll Be Right Back!</h1>
<p>Our site is undergoing maintenance. Check back soon!</p>
</body>
</html>
`,
{ status: 503, headers: { "Content-Type": "text/html" } },
);
},
};
Custom cache
Performs programmatic caching at the edge to reduce origin load.
const CACHE_DURATION = 30 * 24 * 60 * 60; // 30 days
export default {
async fetch(request) {
const cache = caches.default;
const cacheKey = new Request(request.url, { method: "GET" });
let response = await cache.match(cacheKey);
if (!response) {
response = await fetch(request);
response = new Response(response.body, response);
response.headers.set("Cache-Control", `s-maxage=${CACHE_DURATION}`);
await cache.put(cacheKey, response.clone());
}
return response;
},
};
Redirect based on country code
Redirects visitors based on their geographic location.
export default {
async fetch(request) {
const country = request.cf.country;
const redirectMap = {
US: "https://example.com/us",
EU: "https://example.com/eu",
};
if (redirectMap[country])
return Response.redirect(redirectMap[country], 301);
return fetch(request);
},
};
Redirect 403 Forbidden to a different page
If the origin responded with 403 Forbidden error code, redirects visitor to a different page.
export default {
async fetch(request) {
// Send original request to the origin
const response = await fetch(request);
// Check if origin responded with 403 status code
if (response.status == 403) {
// If so, redirect to this URL
const destinationURL = "https://example.com";
// With this status code
const statusCode = 301;
// Serve redirect
return Response.redirect(destinationURL, statusCode);
}
// Otherwise, serve origin's response
else {
return response;
}
},
};
Retry to another origin
If the response to the original request is not 200 OK or a redirect, sends to another origin.
export default {
async fetch(request) {
// Send original request to the origin
const response = await fetch(request);
// If response is not 200 OK or a redirect, send to another origin
if (!response.ok && !response.redirected) {
// First, clone the original request to construct a new request
const newRequest = new Request(request);
// Add a header to identify a re-routed request at the new origin
newRequest.headers.set("X-Rerouted", "1");
// Clone the original URL
const url = new URL(request.url);
// Send request to a different origin / hostname
url.hostname = "example.com";
// Serve response to the new request from the origin
return await fetch(url, newRequest);
}
// If response is 200 OK or a redirect, serve it
return response;
},
};
Remove fields from API response
If the origin responds with JSON, deletes sensitive fields before returning a response to the visitor.
export default {
async fetch(request) {
// Send original request to the origin
const response = await fetch(request);
// Check if origin responded with JSON
try {
// Parse API response as JSON
var api_response = response.json();
// Specify the fields you want to delete. For example, to delete "botManagement" array from parsed JSON:
delete api_response.botManagement;
// Serve modified API response
return Response.json(api_response);
} catch (err) {
// On failure, serve unmodified origin's response
return response;
}
},
};
Set CORS headers
Adjusts Cross-Origin Resource Sharing (CORS) headers and handles preflight requests.
// Define CORS headers
const corsHeaders = {
"Access-Control-Allow-Origin": "*", // Replace * with your allowed origin(s)
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS", // Adjust allowed methods as needed
"Access-Control-Allow-Headers": "Content-Type, Authorization", // Adjust allowed headers as needed
"Access-Control-Max-Age": "86400", // Adjust max age (in seconds) as needed
};
export default {
async fetch(request) {
// Make a copy of the request to modify its headers
const modifiedRequest = new Request(request);
// Handle preflight requests (OPTIONS)
if (request.method === "OPTIONS") {
return new Response(null, {
headers: {
...corsHeaders,
},
status: 200, // Respond with OK status for preflight requests
});
}
// Pass the modified request through to the origin
const response = await fetch(modifiedRequest);
// Make a copy of the response to modify its headers
const modifiedResponse = new Response(response.body, response);
// Set CORS headers on the response
Object.keys(corsHeaders).forEach((header) => {
modifiedResponse.headers.set(header, corsHeaders[header]);
});
return modifiedResponse;
},
};
Rewrite links on HTML pages
Replaces outdated links without having to make changes on your origin.
export default {
async fetch(request) {
// Define the old hostname here.
const OLD_URL = "oldsite.com";
// Then add your new hostname that should replace the old one.
const NEW_URL = "newsite.com";
class AttributeRewriter {
constructor(attributeName) {
this.attributeName = attributeName;
}
element(element) {
const attribute = element.getAttribute(this.attributeName);
if (attribute) {
element.setAttribute(
this.attributeName,
attribute.replace(OLD_URL, NEW_URL),
);
}
}
}
const rewriter = new HTMLRewriter()
.on("a", new AttributeRewriter("href"))
.on("img", new AttributeRewriter("src"));
const res = await fetch(request);
const contentType = res.headers.get("Content-Type");
// If the response is HTML, it can be transformed with
// HTMLRewriter -- otherwise, it should pass through
if (contentType.startsWith("text/html")) {
return rewriter.transform(res);
} else {
return res;
}
},
};
Slow down requests
Defines a delay to be used when incoming requests match your rule. Useful for suspicious requests.
export default {
async fetch(request) {
// Define delay
const delay_in_seconds = 5;
// Introduce a delay
await new Promise((resolve) =>
setTimeout(resolve, delay_in_seconds * 1000),
); // Set delay in milliseconds
// Pass the request to the origin
const response = await fetch(request);
return response;
},
};
Using Snippets and Workers together
While Snippets and Workers have distinct capabilities, they can work together to handle complex traffic workflows.
To avoid conflicts, Snippets and Workers should operate on separate request paths rather than running on the same URL. Have them fetch their respective URLs as a subrequest within their logic, ensuring smooth execution and caching behavior.
Example 1: Passing data between Snippets and Workers
Snippets can modify incoming requests before they reach a Worker, and Workers can read these modifications, perform additional transformations, and pass them downstream.
Snippet: Add a custom header
export default {
async fetch(request) {
// Get the current timestamp
const timestamp = Date.now();
const hexTimestamp = timestamp.toString(16);
// Clone request and add a custom header
const modifiedRequest = new Request(request, {
headers: new Headers(request.headers),
});
modifiedRequest.headers.set("X-Hex-Timestamp", hexTimestamp);
console.log(`X-Hex-Timestamp: ${hexTimestamp}`);
// Pass modified request to origin
return fetch(modifiedRequest);
},
};
Worker: Read a header and add it to the response
export default {
async fetch(request) {
const response = await fetch("https://{snippets_url}", request); // Ensure {snippets_url} points to the endpoint modified by Snippets
const newResponse = new Response(response.body, response);
let hexTimestamp = request.headers.get("X-Hex-Timestamp") || "null";
console.log(hexTimestamp);
newResponse.headers.set("X-Hex-Timestamp", hexTimestamp);
return newResponse;
},
};
Result: The Snippet sets X-Hex-Timestamp, which the Worker reads and forwards to the origin.
Example 2: Caching Worker responses using Snippets
A Worker performs compute-heavy processing (for example, image transformation), while a Snippet serves cached results to avoid unnecessary Worker execution. This can be helpful in situations when running Workers before cache is not desirable.
Worker: Transform and cache responses
export default {
async fetch(request) {
const url = new URL(request.url);
url.hostname = "origin.example.com"; // Ensure this hostname points to the origin where the resource is hosted
const newRequest = new Request(url, request);
const customKey = `https://${url.hostname}${url.pathname}`; // This custom cache key should be the same in both Worker and Snippet configuration for cache to work
// Fetch and modify response
const response = await fetch(newRequest);
const newResponse = new Response(response.body, response);
// Cache the transformed response
const cache = caches.default;
const cachedResponse = newResponse.clone();
cachedResponse.headers.set("X-Cached-In-Workers", "true");
await cache.put(customKey, cachedResponse);
newResponse.headers.set("X-Retrieved-From-Workers", "true");
return newResponse;
},
};
Snippet: Serve cached responses or forward to Worker
export default {
async fetch(request) {
const url = new URL(request.url);
url.hostname = "origin.example.com"; // Ensure this hostname points to the origin where the resource is hosted
const cacheKey = `https://${url.hostname}${url.pathname}`; // This custom cache key should be the same in both Worker and Snippet configuration for cache to work
// Access cache
const cache = caches.default;
let response = await cache.match(cacheKey);
if (!response) {
console.log(`Cache miss for: ${cacheKey}. Fetching from Worker...`);
url.hostname = "worker.example.com"; // Ensure this hostname points to the Workers route
response = await fetch(new Request(url, request));
// Cache the response for future use
response = new Response(response.body, response);
response.headers.set("Cache-Control", `s-maxage=3600`);
response.headers.set("x-snippets-cache", "stored");
} else {
console.log(`Cache hit for: ${cacheKey}`);
response = new Response(response.body, response);
response.headers.set("x-snippets-cache", "hit");
}
return response;
},
};
Result: The transformed response (X-Cached-In-Workers: true) is served from cache, avoiding redundant Worker execution (X-Retrieved-From-Workers is not present). When cache expires, the Snippet fetches a fresh version.
Migration between Snippets and Workers
Snippets and Workers share the same Workers runtime, meaning JavaScript code that does not rely on bindings, persistent storage, or advanced execution features can be migrated seamlessly between them.
When to migrate workloads to Snippets
You should consider migrating a Worker to Snippets if it:
- Only modifies headers, redirects, caching rules, or origin routing.
- Does not require bindings, persistent storage, or external integrations.
- Is a lightweight JavaScript function with simple logic.
- Needs to run an unlimited number of times for free on a Pro, Business, or Enterprise plan.
Migrating to Snippets allows you to:
- Leverage advanced request matching via the Ruleset Engine.
- Eliminate usage-based billing — Snippets are included at no cost on all paid plans.
- Simplify management by integrating traffic modifications directly into Cloudflare Rules.
When to migrate workloads to Workers
You should migrate from Snippets to Workers if your logic:
- Exceeds execution time, memory, or other limits.
- Requires persistent state management, such as:
- Performs compute-intensive operations, including:
- Interacts with Cloudflare's Developer Platform.
- Requires unit testing.
- Needs deployment automation via CLI (Wrangler).
If your Snippet reaches the limits of execution time, memory, or functionality, transitioning to Workers ensures your logic can scale without restrictions.
Conclusion
Cloudflare Snippets provide a production-ready solution for fast, declarative edge traffic logic, bridging the gap between Cloudflare Rules and Developer Platform.
Snippets and Workers solve different problems:
- Use Snippets for fast, lightweight traffic modifications at the edge, including header rewrites, caching, redirects, origin routing, custom responses, A/B testing and authentication.
- Workers are built for advanced compute, persistent state, and full-stack applications.
A/B testing with same-URL direct access
This version passes through requests for /test/* and /control/* URI paths to the origin server, bypassing random assignment.
const NAME = "myExampleABTest";
export default {
async fetch(request) {
// Clone the original URL
const url = new URL(request.url);
// Enable Passthrough to allow direct access to control and test routes.
if (url.pathname.startsWith("/control") || url.pathname.startsWith("/test"))
return fetch(request);
// Determine which group this requester is in.
const cookie = request.headers.get("cookie");
if (cookie && cookie.includes(`${NAME}=control`)) {
url.pathname = "/control" + url.pathname;
} else if (cookie && cookie.includes(`${NAME}=test`)) {
url.pathname = "/test" + url.pathname;
} else {
// If there is no cookie, this is a new client. Choose a group and set the cookie.
const group = Math.random() < 0.5 ? "test" : "control"; // 50/50 split
if (group === "control") {
url.pathname = "/control" + url.pathname;
} else {
url.pathname = "/test" + url.pathname;
}
// Reconstruct response to avoid immutability
let response = await fetch(url);
response = new Response(response.body, response);
// Set cookie to enable persistent A/B sessions.
response.headers.append("Set-Cookie", `${NAME}=${group}; path=/`);
return response;
}
return fetch(url);
},
};
Append dates to cookies to use with A/B testing
export default {
async fetch(request) {
const response = await fetch(request);
// Clone the response so that it is no longer immutable
const newResponse = new Response(response.body, response);
// Define the dynamic expiry time. 24 h * 60 m * 60 s * 1000 ms = 86,400,000 ms
const expiry = new Date(Date.now() + 7 * 86400000).toUTCString();
// Define the group variable. "A" if the request header "userGroup" is "premium", "B" if otherwise.
const group = request.headers.get("userGroup") == "premium" ? "A" : "B";
// Append the custom header with the values
newResponse.headers.append(
"Set-Cookie",
`testGroup=${group}; Expires=${expiry}; Path=/`,
);
return newResponse;
},
};
Auth with headers
:::caution[Caution when using in production]
The example code contains a generic header key and value of X-Custom-PSK and mypresharedkey. To best protect your resources, change the header key and value in the Snippets editor before saving your code.
:::
export default {
async fetch(request) {
/**
* @param {string} PRESHARED_AUTH_HEADER_KEY Custom header to check for key
* @param {string} PRESHARED_AUTH_HEADER_VALUE Hard-coded key value
*/
const PRESHARED_AUTH_HEADER_KEY = "X-Custom-PSK";
const PRESHARED_AUTH_HEADER_VALUE = "mypresharedkey";
const psk = request.headers.get(PRESHARED_AUTH_HEADER_KEY);
if (psk === PRESHARED_AUTH_HEADER_VALUE) {
// Correct preshared header key supplied. Fetch request from origin.
return fetch(request);
}
// Incorrect key supplied. Reject the request.
return new Response("Sorry, you have supplied an invalid key.", {
status: 403,
});
},
};
Send Bot Management information to origin
export default {
async fetch(request) {
// Clone the original request to construct a new request
const newRequest = new Request(request);
// Set Bot Management headers on a new request to the origin: https://developers.cloudflare.com/bots/reference/bot-management-variables/#workers-variables
newRequest.headers.set("bot-score", request.cf.botManagement.score); // bot score (integer)
newRequest.headers.set(
"verified-bot",
request.cf.botManagement.verifiedBot,
); // verified bot (boolean)
newRequest.headers.set("ja4", request.cf.botManagement.ja4); // JA4 fingerprint hash (string)
// Serve response to the new request from the origin
return await fetch(newRequest);
},
};
Send suspect bots to a honeypot
export default {
async fetch(request) {
const response = await fetch(request);
// Clone the response so that it is no longer immutable
const newResponse = new Response(response.body, response);
if (request.cf.botManagement.score < 30) {
const honeypot = "https://example.com/";
return await fetch(honeypot, request);
} else {
return newResponse;
}
},
};
Bulk redirect based on a map object
export default {
async fetch(request) {
// Define a variable with the hostname that needs to be redirected.
const externalHostname = "example.com";
// Define the map object. Replace the sources (/pathX) and targets (/redirectX) with ones that apply to your case.
const redirectMap = new Map([
["/path1", "https://" + externalHostname + "/redirect1"],
["/path2", "https://" + externalHostname + "/redirect2"],
["/path3", "https://" + externalHostname + "/redirect3"],
["/path4", "https://cloudflare.com"],
]);
// Clone the original URL.
const requestURL = new URL(request.url);
// Check the request path against the map and redirect accordingly.
const path = requestURL.pathname;
const location = redirectMap.get(path);
if (location) {
return Response.redirect(location, 301);
}
// If request path not in map, return the original request.
return fetch(request);
},
};
Country code redirect
export default {
async fetch(request) {
/**
* A map of the URLs to redirect to
* @param {Object} countryMap
*/
const countryMap = {
// Replace the country codes and target URLs with ones that apply to your case.
US: "https://example.com/us",
EU: "https://example.com/eu",
};
// Use the cf object to obtain the country of the request
// more on the cf object: https://developers.cloudflare.com/workers/runtime-apis/request#incomingrequestcfproperties
const country = request.cf.country;
// If country is not null and is defined in the country map above, redirect.
if (country != null && country in countryMap) {
const url = countryMap[country];
// Remove this logging statement from your final output.
console.log(
`Based on ${country}-based request, your user would go to ${url}.`,
);
return Response.redirect(url);
// If request country not in map, return another page.
} else {
return fetch("https://example.com", request);
}
},
};
Custom cache
// Define configurable cache duration in seconds (default: 30 days)
const CACHE_DURATION_SECONDS = 30 * 24 * 60 * 60;
// Define which parts of the request to include in the cache key
const USE_PATH = true; // Include path in the cache key
const USE_QUERY_STRING = true; // Include query string in the cache key
const INCLUDE_HEADERS = ["User-Agent"]; // Headers to include in the cache key
export default {
async fetch(request, env, ctx) {
// Generate a custom cache key based on user preferences
const cacheKey = createCacheKey(request);
console.log(`Retrieving cache for: ${cacheKey.url}.`);
// Access the default Cache API
const cache = caches.default;
// Attempt to retrieve the cached response
let response = await cache.match(cacheKey);
if (!response) {
// Cache miss: Fetch the asset from the origin
console.log(`Cache miss for: ${cacheKey.url}. Fetching from origin...`);
response = await fetch(request);
// Wrap the origin response for caching
response = new Response(response.body, response);
// Set Cache-Control headers to define the TTL
response.headers.set(
"Cache-Control",
`s-maxage=${CACHE_DURATION_SECONDS}`,
);
response.headers.set("x-snippets-cache", "stored");
// Store the response in the cache
await cache.put(cacheKey, response.clone());
} else {
// Cache hit: Return the cached response
console.log(`Cache hit for: ${cacheKey.url}.`);
response = new Response(response.body, response);
response.headers.set("x-snippets-cache", "hit");
// Optionally check if the cache should expire based on age
const ageHeader = response.headers.get("Age");
if (ageHeader && parseInt(ageHeader, 10) > CACHE_DURATION_SECONDS) {
console.log(
`Cache expired for: ${cacheKey.url}. Deleting cached response...`,
);
await cache.delete(cacheKey);
response.headers.set("x-snippets-cache", "deleted");
}
}
// Return the response to the client
return response;
},
};
/**
* Function to create a custom cache key based on request properties
* @param {Request} request - The incoming request object
* @returns {Request} - A valid cache key based on the URL
*/
function createCacheKey(request) {
const url = new URL(request.url); // Use the request's base URL
const cacheKey = new URL(url.origin); // Start with the origin (scheme + hostname)
// Optionally include the path
if (USE_PATH) {
cacheKey.pathname = url.pathname;
}
// Optionally include the query string
if (USE_QUERY_STRING) {
cacheKey.search = url.search;
}
// Optionally include specific headers
if (INCLUDE_HEADERS.length > 0) {
const headerParts = INCLUDE_HEADERS.map(
(header) => `${header}=${request.headers.get(header) || ""}`,
).join("&");
cacheKey.searchParams.append("headers", headerParts);
}
// Return the constructed URL as the cache key
return new Request(cacheKey.toString(), {
method: "GET",
});
}
Debugging logs
// Replace with your actual logging service endpoint
const loggingEndpoint = "https://your-logging-endpoint";
export default {
async fetch(request) {
try {
// Attempt to fetch the request from the origin server
const response = await fetch(request.clone());
// Check if the response status indicates an error (for example, 4xx or 5xx)
if (!response.ok) {
// Prepare error details and context for logging
const errorDetails = {
status: response.status,
statusText: response.statusText,
url: request.url,
method: request.method,
headers: Object.fromEntries(request.headers),
};
// Log error details to your logging service
await logError(errorDetails);
// Return the original response with status and statusText intact
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: response.headers,
});
}
// Return the successful response from the origin server
return response;
} catch (error) {
// Handle any exceptions that occur during fetch
const errorDetails = {
message: error.message,
stack: error.stack,
url: request.url,
method: request.method,
headers: Object.fromEntries(request.headers),
};
// Log error details to your logging service
await logError(errorDetails);
// Return a generic error response
return new Response("Internal Server Error", {
status: 500,
});
}
},
};
// Function to log error details to your logging service
async function logError(details) {
try {
const response = await fetch(loggingEndpoint, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(details),
});
if (!response.ok) {
console.error("Failed to log error:", response.statusText);
}
} catch (error) {
console.error("Error logging error:", error.message);
}
}
Define CORS headers
// Define CORS headers
const corsHeaders = {
"Access-Control-Allow-Origin": "*", // Replace * with your allowed origin(s)
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS", // Adjust allowed methods as needed
"Access-Control-Allow-Headers": "Content-Type, Authorization", // Adjust allowed headers as needed
"Access-Control-Max-Age": "86400", // Adjust max age (in seconds) as needed
};
export default {
async fetch(request) {
// Make a copy of the request to modify its headers
const modifiedRequest = new Request(request);
// Handle preflight requests (OPTIONS)
if (request.method === "OPTIONS") {
return new Response(null, {
headers: {
...corsHeaders,
},
status: 200, // Respond with OK status for preflight requests
});
}
// Pass the modified request through to the origin
const response = await fetch(modifiedRequest);
// Make a copy of the response to modify its headers
const modifiedResponse = new Response(response.body, response);
// Set CORS headers on the response
Object.keys(corsHeaders).forEach((header) => {
modifiedResponse.headers.set(header, corsHeaders[header]);
});
return modifiedResponse;
},
};
Follow redirects from the origin
export default {
async fetch(request) {
// Define fetch options to follow redirects
const fetchOptions = {
redirect: "follow", // Ensure fetch follows redirects automatically. Each subrequest in a redirect chain counts against the subrequest limit.
};
// Make the fetch request to the origin
const response = await fetch(request, fetchOptions);
// Log the final URL after redirects (optional, for debugging)
console.log(`Final URL after redirects: ${response.url}`);
// Return the final response to the client
return response;
},
};
This template is ready for use and should fit most redirect-following scenarios.
It ensures the Snippet transparently follows redirects issued by the origin server. The redirect: "follow" option of the Fetch API ensures automatic handling of 3xx redirects, returning the final response. If the origin response is not a redirect, the original content is returned.
:::note Snippets have a maximum number of subrequests per invocation which depends on your plan. If the origin server issues multiple redirects, each redirect subrequest will count towards this limit. When the number of subrequests exceeds the limit for your Cloudflare plan, you will receive a 1202 error. Ensure your origin configuration minimizes unnecessary redirects. :::
Add HEX timestamp to a request header
export default {
async fetch(request) {
// Get the current timestamp
const timestamp = Date.now();
// Convert the timestamp to hexadecimal format
const hexTimestamp = timestamp.toString(16);
// Clone the request and add the custom header
const modifiedRequest = new Request(request, {
headers: new Headers(request.headers),
});
modifiedRequest.headers.set("X-Hex-Timestamp", hexTimestamp);
// Log the custom header for debugging
console.log(`X-Hex-Timestamp: ${hexTimestamp}`);
// Pass the modified request to the origin
const response = await fetch(modifiedRequest);
return response;
},
};
Snippets examples
import { ResourcesBySelector } from "~/components";
Refer to the following examples to get started creating your snippet code.
Refer to How Snippets work and Create a snippet in the dashboard for overall guidance.
<ResourcesBySelector directory="rules/snippets/examples" types={["example"]} />
Validate JSON web tokens (JWT)
:::caution[Caution when using in production]
The example code contains a placeholder HMAC secret of "mysecretsymmetrickey". To best protect your resources, change the secret to a strong, unique value in the Snippets editor before saving your code.
:::
export default {
async fetch(request) {
const HMAC_SECRET = "mysecretsymmetrickey"; // Change this to your secret key
const CLOCK_TOLERANCE = 30; // Tolerance for clock skew in seconds
// Extract JWT token from "Authorization: Bearer" header
function getJWTToken(request) {
const authorizationHeader = request.headers.get("Authorization");
if (authorizationHeader && authorizationHeader.startsWith("Bearer ")) {
return authorizationHeader.substring(7, authorizationHeader.length);
}
return null;
}
// Convert a base64url string to a Uint8Array
function base64urlToUint8Array(base64url) {
const base64 = base64url
.replace(/-/g, "+")
.replace(/_/g, "/")
.padEnd(base64url.length + ((4 - (base64url.length % 4)) % 4), "=");
const binary = atob(base64);
return Uint8Array.from(binary, (c) => c.charCodeAt(0));
}
// Decode a base64url-encoded JWT segment to a JSON object
function decodeSegment(s) {
const bytes = base64urlToUint8Array(s);
return JSON.parse(new TextDecoder().decode(bytes));
}
// Validate JWT token structure, cryptographic signature, and claims.
async function validateJWT(token) {
const parts = token.split(".");
if (parts.length !== 3) {
throw new Error("Invalid JWT format");
}
const [headerB64, payloadB64, signatureB64] = parts;
const decodedHeader = decodeSegment(headerB64);
const decodedPayload = decodeSegment(payloadB64);
// Verify the algorithm.
// This prevents algorithm-confusion attacks where an attacker changes the "alg" header to bypass signature verification.
// Never branch on the token's alg header to decide how to verify.
// Always enforce the algorithm you expect.
if (decodedHeader.alg !== "HS256") {
throw new Error("Unsupported algorithm");
}
// Verify the cryptographic signature.
// Import the shared secret as an HMAC-SHA256 key, then verify that the signature matches "header.payload".
// If this fails, the token was tampered with or signed with a different key.
const signature = base64urlToUint8Array(signatureB64);
const data = new TextEncoder().encode(`${headerB64}.${payloadB64}`);
const key = await crypto.subtle.importKey(
"raw",
new TextEncoder().encode(HMAC_SECRET),
{ name: "HMAC", hash: "SHA-256" },
false,
["verify"],
);
const valid = await crypto.subtle.verify(
{ name: "HMAC" },
key,
signature,
data,
);
if (!valid) {
throw new Error("Invalid signature");
}
// Validate standard claims.
// The claims a JWT must contain depends on your use case.
const now = Math.floor(Date.now() / 1000);
// exp (expiration): Reject tokens that have expired.
// A small clock tolerance accounts for clock skew between servers.
if (typeof decodedPayload.exp !== "number") {
throw new Error("Missing exp claim");
}
if (decodedPayload.exp < now - CLOCK_TOLERANCE) {
throw new Error("JWT has expired");
}
// iat (issued at): Ensure the token declares when it was created.
if (typeof decodedPayload.iat !== "number") {
throw new Error("Missing iat claim");
}
// Additional claims per RFC 7519 (https://www.rfc-editor.org/rfc/rfc7519.html):
// - nbf (not before): Reject tokens used before their valid start time.
// if (decodedPayload.nbf > now + CLOCK_TOLERANCE) throw new Error("JWT is not yet valid");
// - iss (issuer): Ensure the token was issued by a trusted authority.
// if (decodedPayload.iss !== "https://auth.example.com") throw new Error("Invalid issuer");
// - aud (audience): Ensure the token is intended for your service.
// if (decodedPayload.aud !== "my-api") throw new Error("Invalid audience");
// - sub (subject): Identify the principal the token refers to.
// - jti (JWT ID): Unique token identifier, useful for preventing replay attacks.
return true;
}
// Execute the function to extract JWT token
const jwtToken = getJWTToken(request);
// If the token is not provided, serve 401 Forbidden
if (!jwtToken) {
return new Response("Missing JWT token", { status: 401 });
}
try {
// If the token is valid, the validateJWT function will not error and serve the actual response
// An example of a valid token that will expire in 2033 is "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNjI0OTkyMDAwLCJleHAiOjIwMDExMjAwMDB9._qgQ_TMrGfYgOoA8HtTZwEGoj8zAPWxsz8CT1jEAGzo"
await validateJWT(jwtToken);
return fetch(request);
} catch {
return new Response("Invalid JWT token", { status: 401 });
}
},
};
Maintenance page
// Define your customizable inputs
const statusCode = 503;
const title = "We'll Be Right Back!";
const message =
"Our site is currently undergoing scheduled maintenance. We’re working hard to bring you a better experience. Thank you for your patience and understanding.";
const estimatedTime = "1 hour";
const contactEmail = "support@example.com";
const contactPhone = "+1 234 567 89";
export default {
async fetch(request) {
// Serve the maintenance page as a response
return new Response(generateMaintenancePage(), {
status: statusCode,
headers: {
"Content-Type": "text/html",
"Retry-After": "3600", // Suggest retry after 1 hour
},
});
},
};
function generateMaintenancePage() {
return `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>${title}</title>
<style>
body {
margin: 0;
font-family: Arial, sans-serif;
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
background-color: #f4f4f4;
color: #333;
text-align: center;
}
.container {
max-width: 600px;
padding: 20px;
}
h1 {
font-size: 2rem;
color: #0056b3;
margin-bottom: 10px;
}
p {
font-size: 1rem;
margin-bottom: 20px;
line-height: 1.5;
}
.contact {
margin-top: 20px;
font-size: 0.9rem;
color: #666;
}
.contact a {
color: #0056b3;
text-decoration: none;
}
.contact a:hover {
text-decoration: underline;
}
.logo {
margin: 20px 0;
max-width: 150px;
}
.timer {
font-weight: bold;
color: #e63946;
}
</style>
</head>
<body>
<div class="container">
<h1>${title}</h1>
<p>${message}</p>
<p>If all goes to plan, we'll be back online in <span class="timer">${estimatedTime}</span>. 🚀</p>
<p class="contact">
Need help? Reach out to us at <a href="mailto:${contactEmail}">${contactEmail}</a>
or call us at <a href="tel:${contactPhone}">${contactPhone}</a>.
</p>
</div>
</body>
</html>
`;
}
Snippet rule
Configure a custom filter expression:
| Field | Operator | Value |
|---|---|---|
| IP Source Address | is not in list | admin_ips |
If you are using the Expression Editor, enter the following expression:
(not ip.src in $admin_ips)
The IP list admin_ips was previously created and contains the list of IP addresses of the site administrators, which will be able to access the site during the maintenance period.
Override a Set-Cookie header with a certain value
export default {
async fetch(request) {
// Receive response from the origin
const response = await fetch(request);
// Create a new Headers object to modify response headers
const newHeaders = new Headers(response.headers);
// Get all Set-Cookie headers
const cookieArray = response.headers.getSetCookie();
if (cookieArray.length > 0) {
const updatedCookies = cookieArray.map((cookie) => {
// For example, replace the currency value with GBP
if (cookie.trim().startsWith("currency=")) {
return cookie.replace(/currency=[^;]+/, "currency=GBP");
}
return cookie;
});
// Delete the existing Set-Cookie headers
newHeaders.delete("Set-Cookie");
// Add the updated Set-Cookie headers individually
updatedCookies.forEach((cookie) => {
newHeaders.append("Set-Cookie", cookie.trim());
});
}
// Return the modified response with updated headers
return new Response(response.body, {
status: response.status,
headers: newHeaders,
});
},
};
Redirect 403 Forbidden to a different page
export default {
async fetch(request) {
// Send original request to the origin
const response = await fetch(request);
// Check if origin responded with 403 status code
if (response.status == 403) {
// If so, redirect to this URL
const destinationURL = "https://example.com";
// With this status code
const statusCode = 301;
// Serve redirect
return Response.redirect(destinationURL, statusCode);
}
// Otherwise, serve origin's response
else {
return response;
}
},
};
Redirect from one domain to another
export default {
async fetch(request) {
// Define variables to use in the response redirect.
const base = "https://example.com";
const statusCode = 301;
// Clone the original URL.
const url = new URL(request.url);
// Define a "pathname" and "search" variables, extracting their values from the cloned URL.
const { pathname, search } = url;
// Define the destination URL using the variables you declared previously.
const destinationURL = `${base}${pathname}${search}`;
console.log(destinationURL);
// Respond with the redirect.
return Response.redirect(destinationURL, statusCode);
},
};
Remove fields from API response
export default {
async fetch(request) {
// Send original request to the origin
const response = await fetch(request);
// Check if origin responded with JSON
try {
// Parse API response as JSON
var api_response = response.json();
// Specify the fields you want to delete. For example, to delete "botManagement" array from parsed JSON:
delete api_response.botManagement;
// Serve modified API response
return Response.json(api_response);
} catch (err) {
// On failure, serve unmodified origin's response
return response;
}
},
};
Remove query strings before sending request to origin
export default {
async fetch(request) {
// Define the query strings you want to remove
const queryStringsToRemove = ["utm_source", "utm_medium", "utm_campaign"];
// Get the URL from the request
const url = new URL(request.url);
// Remove the specified query strings
queryStringsToRemove.forEach((query) => {
url.searchParams.delete(query);
});
// Create a new request with the modified URL
const modifiedRequest = new Request(url, request);
// Pass the modified request to the origin
const response = await fetch(modifiedRequest);
return response;
},
};
Remove response headers
export default {
async fetch(request) {
// Define the prefix of the headers you want to remove
const headerPrefix = "x-header-";
// Receive response from the origin
const response = await fetch(request);
// Create a new Headers object to modify response headers
const newHeaders = new Headers(response.headers);
// Remove headers that start with the specified prefix
for (const [key] of newHeaders.entries()) {
if (key.startsWith(headerPrefix)) {
newHeaders.delete(key);
}
}
// Return the modified response with updated headers
return new Response(response.body, {
status: response.status,
headers: newHeaders,
});
},
};
Return information about the incoming request
export default {
async fetch(request) {
// For any request, respond with JSON object containing all incoming request properties provided by Cloudflare network
return Response.json(request.cf, {
// Add new header to identify request was served by Snippets
headers: {
"x-snippets-hello": "Hello from Cloudflare Snippets",
},
});
},
};
Rewrite links on HTML pages
export default {
async fetch(request) {
// Define the old hostname here.
const OLD_URL = "oldsite.com";
// Then add your new hostname that should replace the old one.
const NEW_URL = "newsite.com";
class AttributeRewriter {
constructor(attributeName) {
this.attributeName = attributeName;
}
element(element) {
const attribute = element.getAttribute(this.attributeName);
if (attribute) {
element.setAttribute(
this.attributeName,
attribute.replace(OLD_URL, NEW_URL),
);
}
}
}
const rewriter = new HTMLRewriter()
.on("a", new AttributeRewriter("href"))
.on("img", new AttributeRewriter("src"));
const res = await fetch(request);
if (!res.headers.has("Content-Type")) {
return res;
}
const contentType = res.headers.get("Content-Type");
if (typeof contentType !== "string") {
return res;
}
// If the response is HTML, it can be transformed with
// HTMLRewriter -- otherwise, it should pass through
if (contentType.startsWith("text/html")) {
return rewriter.transform(res);
} else {
return res;
}
},
};
Change origin and modify paths
This example demonstrates how to use Cloudflare Snippets to:
- Reroute incoming requests to a different origin.
- Prepend a directory to the URL path.
- Remove specific segments from the URL path.
export default {
async fetch(request) {
// Clone the original request to create a new request object
const newRequest = new Request(request);
// Add a header to identify a rerouted request at the new origin
newRequest.headers.set("X-Rerouted", "1");
// Clone and parse the original URL
const url = new URL(request.url);
// Step 1: Reroute to a different origin
url.hostname = "example.com"; // Change the hostname to the new origin
// Step 2: Append a directory to the path
url.pathname = `/new-path${url.pathname}`; // Prepend "/new-path" to the current path
// Step 3: Remove a specific segment from the path
url.pathname = url.pathname.replace("/remove-me", ""); // Rewrite `/remove-me/something` to `/something`
// Fetch the modified request from the updated URL
return await fetch(url, newRequest);
},
};
This configuration will perform the following rewrites:
| Request URL | URL after rewrite |
|---|---|
https://subdomain.example.com/foo |
https://example.com/new-path/foo |
https://example.com/remove-me/bar |
https://example.com/new-path/bar |
https://example.net/remove-me |
https://example.com/new-path |
Set security headers
export default {
async fetch(request) {
// Define an object with the security headers you want to set.
// Refer to https://developers.cloudflare.com/rules/snippets/examples/security-headers/#other-common-security-headers for more options.
const DEFAULT_SECURITY_HEADERS = {
"X-Content-Type-Options": "nosniff",
"Referrer-Policy": "strict-origin-when-cross-origin",
"Cross-Origin-Embedder-Policy": 'require-corp; report-to="default";',
"Cross-Origin-Opener-Policy": 'same-site; report-to="default";',
"Cross-Origin-Resource-Policy": "same-site",
};
// You can also define headers to be deleted.
const BLOCKED_HEADERS = [
"Public-Key-Pins",
"X-Powered-By",
"X-AspNet-Version",
];
// Receive response from the origin.
let response = await fetch(request);
// Create a new Headers object to modify response headers
let newHeaders = new Headers(response.headers);
// This sets the headers for HTML responses:
if (
newHeaders.has("Content-Type") &&
!newHeaders.get("Content-Type").includes("text/html")
) {
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: newHeaders,
});
}
// Use DEFAULT_SECURITY_HEADERS object defined above to set the new security headers.
Object.keys(DEFAULT_SECURITY_HEADERS).map((name) => {
newHeaders.set(name, DEFAULT_SECURITY_HEADERS[name]);
});
// Use the BLOCKED_HEADERS object defined above to delete headers you wish to block.
BLOCKED_HEADERS.forEach((name) => {
newHeaders.delete(name);
});
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: newHeaders,
});
},
};
Other common security headers
- Content-Security-Policy headers: Enabling these headers will permit content from a trusted domain and all its subdomains. Refer to Content-Security-Policy for details.
"Content-Security-Policy": "default-src 'self' example.com *.example.com",
- Strict-Transport-Security headers: These are not automatically set because your website might get added to Chrome's HSTS preload list.
"Strict-Transport-Security" : "max-age=63072000; includeSubDomains; preload",
- Permissions-Policy header: Allow or deny the use of browser features, such as opting out of FLoC.
"Permissions-Policy": "interest-cohort=()",
- X-XSS-Protection header: Prevents a page from loading if an XSS attack is detected. Refer to X-XSS-Protection for details.
"X-XSS-Protection": "0",
- X-Frame-Options header: Prevents click-jacking attacks. Refer to X-Frame-Options.
"X-Frame-Options": "DENY",
Send timestamp to origin as a custom header
export default {
async fetch(request) {
// Get the current timestamp
const timestamp = Date.now();
// Convert the timestamp to hexadecimal format
const hexTimestamp = timestamp.toString(16);
// Clone the request and add the custom header
const modifiedRequest = new Request(request, {
headers: new Headers(request.headers),
});
modifiedRequest.headers.set("X-Hex-Timestamp", hexTimestamp);
// Log the custom header for debugging
console.log(`X-Hex-Timestamp: ${hexTimestamp}`);
// Pass the modified request to the origin
const response = await fetch(modifiedRequest);
return response;
},
};
Route to a different origin based on origin response
export default {
async fetch(request) {
// Send original request to the origin
const response = await fetch(request);
// If response is not 200 OK or a redirect, send to another origin
if (!response.ok && !response.redirected) {
// First, clone the original request to construct a new request
const newRequest = new Request(request);
// Add a header to identify a re-routed request at the new origin
newRequest.headers.set("X-Rerouted", "1");
// Clone the original URL
const url = new URL(request.url);
// Send request to a different origin / hostname
url.hostname = "example.com";
// Serve response to the new request from the origin
return await fetch(url, newRequest);
}
// If response is 200 OK or a redirect, serve it
return response;
},
};
Sign requests
The following Snippet will:
-
For request URLs beginning with
/generate/, replace/generate/with/, sign the resulting path with its timestamp, and return the full, signed URL in the response body. -
For all other request URLs, verify the signed URL and allow the request through.
export default {
async fetch(request) {
const secretKey = "your_secret_key"; // Replace with your actual secret key
const expiration = 60; // Expiration time in seconds (how long an HMAC token should be valid for)
const encoder = new TextEncoder();
// Import the secret key for HMAC-SHA256 signing
const key = await crypto.subtle.importKey(
"raw",
encoder.encode(secretKey),
{ name: "HMAC", hash: "SHA-256" },
false,
["sign", "verify"],
);
const url = new URL(request.url);
// Check if the request URL starts with /generate/
if (url.pathname.startsWith("/generate/")) {
// Replace /generate/ with /
url.pathname = url.pathname.replace("/generate/", "/");
const currentTimestamp = Math.floor(Date.now() / 1000); // Current timestamp in seconds
// Data to authenticate: combine pathname and timestamp
const dataToAuthenticate = `${url.pathname}${currentTimestamp}`;
// Sign the data with HMAC-SHA256
const signature = await crypto.subtle.sign(
"HMAC",
key,
encoder.encode(dataToAuthenticate),
);
// Encode the timestamp and HMAC in a secure manner
const signatureBase64 = btoa(
String.fromCharCode(...new Uint8Array(signature)),
);
const signedData = `${currentTimestamp}-${signatureBase64}`;
const encodedSignedData = encodeURIComponent(signedData);
// Create the signed URL
const signedURL = `${url}?verify=${encodedSignedData}`;
// Return the signed URL in the response body
return new Response(signedURL, { status: 200 });
}
// For all other request URLs, verify the signed URL
const params = new URLSearchParams(url.search);
const verifyParam = params.get("verify");
if (!verifyParam) {
return new Response("Verification parameter is missing", { status: 403 });
}
// Decode and split the verify parameter into timestamp and HMAC
const decodedVerifyParam = decodeURIComponent(verifyParam);
const [timestampStr, receivedMac] = decodedVerifyParam.split("-");
// Parse timestamp and ensure it's a valid number
const timestamp = parseInt(timestampStr, 10);
if (isNaN(timestamp)) {
return new Response("Invalid timestamp", { status: 403 });
}
// Check if the request has expired
const currentTimestamp = Math.floor(Date.now() / 1000);
if (currentTimestamp > timestamp + expiration) {
return new Response("Signed URL has expired", { status: 403 });
}
// Remove the verify parameter to verify the URL
params.delete("verify");
url.search = params.toString();
// Construct the data to authenticate for verification
const dataToVerify = `${url.pathname}${timestamp}`;
// Verify the signature with HMAC-SHA256
const isValid = await crypto.subtle.verify(
"HMAC",
key,
new Uint8Array([...atob(receivedMac)].map((char) => char.charCodeAt(0))),
encoder.encode(dataToVerify),
);
if (!isValid) {
return new Response("Invalid signature", { status: 403 });
}
// Continue processing the request if the signature is valid
return fetch(request);
},
};
Slow down suspicious requests
export default {
async fetch(request) {
// Define delay
const delay_in_seconds = 5;
// Introduce a delay
await new Promise((resolve) =>
setTimeout(resolve, delay_in_seconds * 1000),
); // Set delay in milliseconds
// Pass the request to the origin
const response = await fetch(request);
return response;
},
};
Snippet rule
Configure a custom filter expression:
| Field | Operator | Value |
|---|---|---|
| Bot Score | less than | 10 |
If you are using the Expression Editor, enter the following expression:
(cf.bot_management.score lt 10)
Transform Rules
import { Render } from "~/components";
Transform Rules allow you to adjust the URI path, query string, and HTTP headers of requests and responses on the Cloudflare global network.
There are several types of Transform Rules:
- URL Rewrite Rules: Rewrite the URL path and query string of an HTTP request.
- Request Header Transform Rules: Set the value of an HTTP request header or remove a request header.
- Response Header Transform Rules: Set the value of an HTTP response header or remove a response header.
- Managed Transforms: Perform common adjustments to HTTP request and response headers with pre-built, one-step configurations.
<Render
file="snippets-alternative"
product="rules"
params={{ suffix: "header modifications and rewrite logic" }}
/>
<Render file="rules-requirements" product="rules" params={{ sentenceIntro: "Transform Rules require" }} />
Get started
Alternatively, create a transform rule from scratch in the dashboard or via Cloudflare API. Refer to the following sections for detailed instructions:
For Terraform examples, refer to Transform Rules configuration using Terraform.
Refer to Rules language for more information on building expressions for Transform Rules.
Availability
Cloudflare Transform Rules are available to all customers. Support for regular expressions depends on your Cloudflare plan.
This table outlines the Transform Rules features available with each customer plan:
| Free | Pro | Business | Enterprise | |
|---|---|---|---|---|
| Availability | Yes | Yes | Yes | Yes |
| Active Transform Rules | 10 | 25 | 50 | 300 |
| Regex support | No | No | Yes | Yes |
A Cloudflare user must have the Firewall role or one of the Administrator roles to access Transform Rules.
Transform Rules evaluation
Managed Transforms run before other types of Transform Rules that modify HTTP headers:
- Managed Transforms that adjust HTTP request headers run before Request Header Transform Rules.
- Managed Transforms that adjust HTTP response headers run before Response Header Transform Rules.
Transform Rules run in order. Rules that appear later in the list of Transform Rules can overwrite changes done by previous rules. You can define the rule order in the dashboard or via API.
Request and response fields are immutable within each phase while evaluating Transform Rules for a request/response. This means that later rules in the same phase cannot match on changes made by earlier rules (they always use the original field values). For more information, refer to Field values during rule evaluation.
<Render file="challenge-issues" product="rules" params={{ featureName: "Rules features such as Transform Rules" }} />
<Render file="troubleshoot-rules-with-trace" product="rules" params={{ rulesFeatureName: "Transform Rules" }} />
Troubleshoot Transform Rules
When troubleshooting a rule configuration, review the Transform Rules evaluation section to understand how and when your Transform Rule is evaluated for each request.
For more information on runtime errors related to Transform Rules configuration, refer to Cloudflare 1xxx errors.
Why do I not see my request header modifications?
Transform Rules performing request header modifications affect the HTTP headers sent by Cloudflare's network to your origin server. You will not find these headers in your browser request or response data, which can make it difficult to tell if the rule is working as intended.
To check if a request header transform rule is taking effect, you can check the logs on your origin server or use Rules simulator to check that the rule is matching traffic correctly. Since Cloudflare Logpush only logs original HTTP request/response headers, Logpush logs will not include any header transformations done via Transform Rules.
To add HTTP headers that website visitors will receive in their browsers, you must modify the response headers instead.
Add a wildcard CORS response header
import { Example } from "~/components";
The following response header transform rule adds a header named Access-Control-Allow-Origin with a static wildcard value (*) to the HTTP response:
Text in Expression Editor:
(http.host eq "<YOUR_HOSTNAME>")
Selected operation under Modify response header: Set static
Header name: Access-Control-Allow-Origin
Value: *
You can also use an expression similar to the following to apply the CORS header to several specific hostnames:
(http.host in {"<YOUR_HOSTNAME_1>" "<YOUR_HOSTNAME_2>"})
Add a request header with the current bot score
import { Example } from "~/components";
The following request header transform rule adds a header named X-Bot-Score with the current bot score to the HTTP request:
Text in Expression Editor:
starts_with(http.request.uri.path, "/en/")
Selected operation under Modify request header: Set dynamic
Header name: X-Bot-Score
Value: to_string(cf.bot_management.score)
Add request header with a static value
import { Example } from "~/components";
The following request header transform rule adds a header named X-Source with a static value (Cloudflare) to the HTTP request:
Text in Expression Editor:
starts_with(http.request.uri.path, "/en/")
Selected operation under Modify request header: Set static
Header name: X-Source
Value: Cloudflare
Add a request header for subrequests from other zones
import { Example } from "~/components";
The following request header transform rule adds an HTTP header to Workers subrequests coming from a different zone:
Text in Expression Editor (replace myappexample.com with your domain):
(cf.worker.upstream_zone != "" and cf.worker.upstream_zone != "myappexample.com")
Selected operation under Modify request header: Set static
Header name: X-External-Workers-Subrequest
Value: 1
The cf.worker.upstream_zone field used in the rule expression is set to empty if the current request is not a Workers subrequest.
Add a response header with a static value
import { Example } from "~/components";
The following response header transform rule adds a header named set-cookie with a static value (cookiename=value) to the HTTP response:
Text in Expression Editor:
starts_with(http.request.uri.path, "/en/")
Selected operation under Modify response header: Add
Header name: set-cookie
Value: cookiename=value
This rule would keep any existing set-cookie headers already present in the HTTP response.
Transform Rules examples
import { ResourcesBySelector } from "~/components";
<ResourcesBySelector directory="rules/transform/examples/" types={["example"]} />
Normalize encoded slashes in URL path
import { Example } from "~/components";
Different web servers and applications handle encoded forward slashes (%2F) in URLs differently. Cloudflare follows RFC 3986, which specifies that %2F should not be automatically normalized to / because / is a reserved character in URLs, and decoding it might change the intended meaning of the path.
However, many origin servers do automatically decode %2F into / when processing requests. If your origin server behaves this way, you may want to apply the same normalization at Cloudflare’s edge to ensure consistency in request handling, rule evaluation, and logging.
How to normalize %2F
To normalize encoded forward slashes (%2F) to standard slashes (/) in the request path before subsequent rule evaluation, create a new URL rewrite rule and define a dynamic URL path rewrite using url_decode() function:
Text in Expression Editor:
(lower(raw.http.request.full_uri) wildcard "*%2f*")
Text after Path > Rewrite to > Dynamic:
url_decode(http.request.uri.path)
This transformation ensures that %2F is always treated as / in the request path. This is particularly useful when setting up rules that depend on URL path matching, as it prevents discrepancies caused by differing normalization behaviors.
Remove a request header
import { Example } from "~/components";
The following request header transform rule removes the cf-connecting-ip header from the HTTP request:
Text in Expression Editor:
starts_with(http.request.uri.path, "/private/")
Selected operation under Modify request header: Remove
Header name: cf-connecting-ip
Remove a response header
import { Example } from "~/components";
The following response header transform rule removes the cf-connecting-ip header from the HTTP response:
Text in Expression Editor:
starts_with(http.request.uri.path, "/private/")
Selected operation under Modify response header: Remove
Header name: cf-connecting-ip
Rewrite blog archive URLs
import { Example } from "~/components";
To rewrite the URLs of a blog archive that follow the URL format /posts/<YYYY>-<MM>-<DD>-<TITLE> to the new format /posts/<YYYY>/<MM>/<DD>/<TITLE>, create the following URL rewrite rule:
Text in Expression Editor:
http.request.uri.path ~ "^/posts/[0-9]+-[0-9]+-[0-9]+-.*"
Text after Path > Rewrite to > Dynamic:
regex_replace(http.request.uri.path, "^/posts/([0-9]+)-([0-9]+)-([0-9]+)-(.*)$", "/posts/${1}/${2}/${3}/${4}")
The function regex_replace() also allows you to extract parts of the URL using regular expressions' capture groups. Create capture groups by putting part of the regular expression in parentheses. Then, reference a capture group using ${<NUMBER>} in the replacement string, where <NUMBER> is the number of the capture group.
Rewrite path of moved section of a website
import { Example } from "~/components";
To rewrite everything under /blog/<PATH> to /marketing/<PATH>, create a new URL rewrite rule and define a dynamic URL path rewrite using wildcard pattern parameters:
When incoming requests match
- Wildcard pattern
- Request URL:
https://<YOUR_HOSTNAME>/blog/*
- Request URL:
Then rewrite the path and/or query
- Target path: [
/]blog/* - Rewrite to: [
/]marketing/${1}
Make sure to replace <YOUR_HOSTNAME> with your actual hostname and adjust the example paths according to your setup.
Rewrite path of archived blog posts
import { Example } from "~/components";
To rewrite all requests to /news/2012/... to /archive/news/2012/... you must add a reference to the content of the original URL. Create a new URL rewrite rule and define a dynamic URL path rewrite using wildcard pattern parameters:
When incoming requests match
- Wildcard pattern
- Request URL:
https://<YOUR_HOSTNAME>/news/2012/*
- Request URL:
Then rewrite the path and/or query
- Target path: [
/]news/2012/* - Rewrite to: [
/]archive/news/2012/${1}
Make sure to replace <YOUR_HOSTNAME> with your actual hostname and adjust the example paths according to your setup.
Rewrite path for object storage bucket
import { Example } from "~/components";
To remove /files/ from URI paths before routing request to your object storage bucket, create a new URL rewrite rule and define a dynamic URL path rewrite using wildcard pattern parameters:
When incoming requests match
- Wildcard pattern
- Request URL:
https://<YOUR_HOSTNAME>/files/*
- Request URL:
Then rewrite the path and/or query
- Target path: [
/]files/* - Rewrite to: [
/]${1}
Make sure to replace <YOUR_HOSTNAME> with your actual hostname and adjust the example paths according to your setup.
Then, use Cloud Connector to route traffic to an object storage bucket.
Rewrite image paths with several URL segments
import { Example } from "~/components";
To rewrite paths like /images/<FOLDER1>/<FOLDER2>/<FILENAME> — where <FOLDER1>, <FOLDER2>, and <FILENAME> can vary — to /img/<FILENAME>, create a URL rewrite rule with a dynamic rewrite of the path component:
Text in Expression Editor:
http.request.uri.path ~ "^/images/[^/]+/[^/]+/[^/]+$"
Text after Path > Rewrite to > Dynamic:
regex_replace(http.request.uri.path, "^/images/[^/]+/[^/]+/(.+)$", "/img/${1}")
For example, this rule would rewrite the /images/nature/animals/tiger.png path to /img/tiger.png.
Rewrite URL query string
import { Example } from "~/components";
To rewrite a request to the /blog path to /blog?sort-by=date, create a URL rewrite rule with the following settings:
Text in Expression Editor:
http.request.uri.path == "/blog"
Text after Query > Rewrite to > Static:
sort-by=date
Additionally, set the path rewrite action of the same rule to Preserve so that the URL path does not change.
Rewrite page path for visitors in specific countries
import { Example } from "~/components";
To have a welcome page in two languages, create two URL rewrite rules with a static rewrite of the path component:
URL rewrite rule #1
Text in Expression Editor:
http.request.uri.path == "/welcome.html" && ip.src.country == "GB"
Text after Path > Rewrite to > Static:
/welcome-gb.html
URL rewrite rule #2
Text in Expression Editor:
http.request.uri.path == "/welcome.html" && ip.src.country == "PT"
Text after Path > Rewrite to > Static:
/welcome-pt.html
Set a response header with the current bot score
import { Example } from "~/components";
The following response header transform rule sets a header named X-Bot-Score to the current bot score in the HTTP response:
Text in Expression Editor:
starts_with(http.request.uri.path, "/en/")
Selected operation under Modify response header: Set dynamic
Header name: X-Bot-Score
Value: to_string(cf.bot_management.score)
This rule would overwrite any existing X-Bot-Score headers already present in the HTTP response.
Set response header with a static value
import { Example } from "~/components";
The following response header transform rule sets a header named X-Source to a static value (Cloudflare) in the HTTP response:
Text in Expression Editor:
starts_with(http.request.uri.path, "/en/")
Selected operation under Modify response header: Set static
Header name: X-Source
Value: Cloudflare
This rule would overwrite any existing X-Source headers already present in the HTTP response.
Configure Managed Transforms
import { Details, TabItem, Tabs, Render, Steps, APIRequest, DashButton, } from "~/components";
-
In the Cloudflare dashboard, go to the Rules Settings page.
-
In the Managed Transforms tab, enable or disable the desired Managed Transforms by selecting the toggle next to each entry. Some Managed Transforms may not be available in your Cloudflare plan or product subscriptions.
1. Get list of available Managed Transforms
Check the Managed Transform's current status and availability using the List Managed Transforms operation.
The following example request obtains a list of available Managed Transforms, organized by request or response, with information about their current status (enabled field) and if you can update them, based on conflicts with other enabled Managed Transforms (has_conflict field).
Each Managed Transform item will optionally contain a conflicts_with array informing you about any Managed Transforms that will conflict with the current Managed Transform when enabled.
The response will only include available Managed Transforms according to your Cloudflare plan and product subscriptions.
{
"result": {
"managed_request_headers": [
{
"enabled": false,
"has_conflict": false,
"id": "add_bot_protection_headers"
},
{
"enabled": false,
"has_conflict": false,
"id": "add_client_certificate_headers"
},
{
"enabled": false,
"has_conflict": false,
"id": "add_visitor_location_headers"
},
{
"conflicts_with": ["remove_visitor_ip_headers"],
"enabled": false,
"has_conflict": false,
"id": "add_true_client_ip_headers"
},
{
"conflicts_with": ["add_true_client_ip_headers"],
"enabled": false,
"has_conflict": false,
"id": "remove_visitor_ip_headers"
},
{
"enabled": false,
"has_conflict": false,
"id": "add_waf_credential_check_status_header"
},
{
"enabled": false,
"has_conflict": false,
"id": "add_waf_content_scan_status_header"
}
],
"managed_response_headers": [
{
"enabled": false,
"has_conflict": false,
"id": "remove_x-powered-by_header"
},
{
"enabled": false,
"has_conflict": false,
"id": "add_security_headers"
}
]
},
"success": true,
"errors": [],
"messages": []
}
2. Change the status of Managed Transforms
Change the status of the desired Managed Transforms using the Update status of Managed Transforms operation.
Add the Managed Transforms you wish to change to the request body, and update their status in the enabled field. You cannot enable a Managed Transform that has a conflict with a currently enabled Managed Transform (that is, an item where has_conflict is true).
Make sure you include the Managed Transforms you are updating in the correct JSON object (managed_request_headers or managed_response_headers).
The response will include all the available Managed Transforms and their new status after the update.
<APIRequest path="/zones/{zone_id}/managed_headers" method="PATCH" json={{ managed_request_headers: [ { id: "add_visitor_location_headers", enabled: true, }, ], managed_response_headers: [ { id: "remove_x-powered-by_header", enabled: true, }, ], }} />
{
"result": {
"managed_request_headers": [
{
"enabled": false,
"has_conflict": false,
"id": "add_bot_protection_headers"
},
{
"enabled": false,
"has_conflict": false,
"id": "add_client_certificate_headers"
},
{
"enabled": true,
"has_conflict": false,
"id": "add_visitor_location_headers"
},
{
"conflicts_with": ["remove_visitor_ip_headers"],
"enabled": false,
"has_conflict": false,
"id": "add_true_client_ip_headers"
},
{
"conflicts_with": ["add_true_client_ip_headers"],
"enabled": false,
"has_conflict": false,
"id": "remove_visitor_ip_headers"
},
{
"enabled": false,
"has_conflict": false,
"id": "add_waf_credential_check_status_header"
},
{
"enabled": false,
"has_conflict": false,
"id": "add_waf_content_scan_status_header"
}
],
"managed_response_headers": [
{
"enabled": true,
"has_conflict": false,
"id": "remove_x-powered-by_header"
},
{
"enabled": false,
"has_conflict": false,
"id": "add_security_headers"
}
]
},
"success": true,
"errors": [],
"messages": []
}
Managed Transforms
Managed Transforms allow you to perform common adjustments to HTTP request and response headers with pre-built, one-step configurations. The available adjustments include:
- Add bot protection request headers.
- Remove or add headers related to the visitor's IP address.
- Add request header when Cloudflare detects leaked credentials.
- Add security-related response headers.
- Remove
X-Powered-Byresponse headers.
For a complete list, refer to Available Managed Transforms.
When you enable a Managed Transform, Cloudflare internally deploys one or more Transform Rules to handle the common configuration you selected. These generated rules will not count against the maximum number of Transform Rules available in your Cloudflare plan.
Enabled Managed Transforms will apply to all inbound requests for the zone (domain or subdomain added to Cloudflare).
:::note The generated internal Transform Rules will not appear in the Transform Rules list in the Cloudflare dashboard. :::
Next steps
For dashboard, API, and Terraform instructions, refer to Configure Managed Transforms.
Available Managed Transforms
import { GlossaryTooltip, Render } from "~/components";
This page lists the available Managed Transforms. They can modify HTTP request headers or response headers.
<Render file="snippets-alternative" product="rules" params={{ suffix: "and customized header modifications" }} />
Important remarks
-
Enabling a Managed Transform may cause issues in your website. You should test any changes in a staging environment. If you detect any undesired or unexpected behavior, consider disabling the Managed Transform and creating a partial implementation using your own transform rule.
-
The names of HTTP headers are case-insensitive. Cloudflare may use a capitalization different from the one presented in this page. Make sure that your origin server can handle HTTP request headers regardless of the exact capitalization of their names.
HTTP request headers
Add bot protection headers
:::note Requires an Enterprise plan with Bot Management enabled. :::
Adds HTTP headers with bot-related values to the request sent to the origin server:
cf-bot-score: Contains the bot score (for example,30).cf-verified-bot: Containstrueif the request comes from a verified bot, orfalseotherwise.cf-ja3-hash: Contains the JA3 fingerprint.cf-ja4: Contains the JA4 fingerprint.
Add TLS client auth headers
Adds HTTP headers with Mutual TLS (mTLS) client authentication values to the request sent to the origin server:
cf-cert-revoked: Value from thecf.tls_client_auth.cert_revokedfield.cf-cert-verified: Value from thecf.tls_client_auth.cert_verifiedfield.cf-cert-presented: Value from thecf.tls_client_auth.cert_presentedfield.cf-cert-issuer-dn: Value from thecf.tls_client_auth.cert_issuer_dnfield.cf-cert-subject-dn: Value from thecf.tls_client_auth.cert_subject_dnfield.cf-cert-issuer-dn-rfc2253: Value from thecf.tls_client_auth.cert_issuer_dn_rfc2253field.cf-cert-subject-dn-rfc2253: Value from thecf.tls_client_auth.cert_subject_dn_rfc2253field.cf-cert-issuer-dn-legacy: Value from thecf.tls_client_auth.cert_issuer_dn_legacyfield.cf-cert-subject-dn-legacy: Value from thecf.tls_client_auth.cert_subject_dn_legacyfield.cf-cert-serial: Value from thecf.tls_client_auth.cert_serialfield.cf-cert-issuer-serial: Value from thecf.tls_client_auth.cert_issuer_serialfield.cf-cert-sha256: Value from thecf.tls_client_auth.cert_fingerprint_sha256field.cf-cert-sha1: Value from thecf.tls_client_auth.cert_fingerprint_sha1field.cf-cert-not-before: Value from thecf.tls_client_auth.cert_not_beforefield.cf-cert-not-after: Value from thecf.tls_client_auth.cert_not_afterfield.cf-cert-ski: Value from thecf.tls_client_auth.cert_skifield.cf-cert-issuer-ski: Value from thecf.tls_client_auth.cert_issuer_skifield.
Add visitor location headers
Adds HTTP headers with location information for the visitor's IP address to the request sent to the origin server:
cf-ipcity: The visitor's city (value from theip.src.cityfield).cf-ipcountry: The visitor's country (value from theip.src.countryfield).cf-ipcontinent: The visitor's continent (value from theip.src.continentfield).cf-iplongitude: The visitor's longitude (value from theip.src.lonfield).cf-iplatitude: The visitor's latitude (value from theip.src.latfield).cf-region: The visitor's region (value from theip.src.regionfield).cf-region-code: The visitor's region code (value from theip.src.region_codefield).cf-metro-code: The visitor's metro code (value from theip.src.metro_codefield).cf-postal-code: The visitor's postal code (value from theip.src.postal_codefield).cf-timezone: The name of the visitor's timezone (value from theip.src.timezone.namefield).
:::note
Turning on IP geolocation will send a cf-ipcountry HTTP header to your origin server even when Add visitor location headers is turned off.
:::
Encoding of non-ASCII header values
Cloudflare always converts non-ASCII characters to UTF-8 in HTTP request and response header values. This applies to location headers added by the Add visitor location headers managed transform.
Add "True-Client-IP" header
:::note Only available on Enterprise plans. :::
Adds a true-client-ip request header with the visitor's IP address.
This Managed Transform is unavailable when Remove visitor IP headers is enabled.
Remove visitor IP headers
Removes HTTP headers that may contain the visitor's IP address from the request sent to the origin server. Handles the following HTTP request headers:
cf-connecting-ipx-forwarded-for(refer to the notes below)true-client-ip
This Managed Transform is unavailable when Add "True-Client-IP" header is enabled.
Visitor IP address in the x-forwarded-for HTTP header
For the x-forwarded-for HTTP request header, enabling Remove visitor IP headers will only remove the visitor IP from the header value when Cloudflare receives a request proxied by at least another CDN (content delivery network). In this case, Cloudflare will only keep the IP address of the last proxy.
For example, consider an incoming request proxied by two CDNs (CDN_1 and CDN_2) before reaching the Cloudflare network. The x-forwarded-for header would be similar to the following:
x-forwarded-for: <VISITOR_IP>, <THIRD_PARTY_CDN_1_IP>, <THIRD_PARTY_CDN_2_IP>
With Remove visitor IP headers enabled, the x-forwarded-for header sent to the origin server will be:
x-forwarded-for: <THIRD_PARTY_CDN_2_IP>
Add leaked credentials checks header
Adds an Exposed-Credential-Check request header whenever the WAF detects leaked credentials in the incoming request.
The header can have these values:
| Header + Value | Description | Availability |
|---|---|---|
Exposed-Credential-Check: 1 |
Previously leaked username and password detected | Pro plan and above |
Exposed-Credential-Check: 2 |
Previously leaked username detected | Enterprise plan |
Exposed-Credential-Check: 3 |
Similar combination of previously leaked username and password detected | Enterprise plan |
Exposed-Credential-Check: 4 |
Previously leaked password detected | All plans |
You will only receive this managed header at your origin server if:
- The leaked credentials detection in the WAF is turned on.
- The Add Leaked Credentials Checks Header managed transform is turned on.
- Your Cloudflare plan supports the type of credentials detection. For example, Free plans can only know if a password was previously leaked. In this situation, Cloudflare will add an
Exposed-Credential-Check: 4header to the request.
Add malicious uploads detection header
Adds a Malicious-Uploads-Detection request header indicating the outcome of scanning uploaded content for malicious signatures.
The header can have one of the following values:
| Header + Value | Description |
|---|---|
Malicious-Uploads-Detection: 1 |
The request contains at least one malicious content object (cf.waf.content_scan.has_malicious_obj is true). |
Malicious-Uploads-Detection: 2 |
The file scanner was unable to scan all the content objects detected in the request (cf.waf.content_scan.has_failed is true). |
Malicious-Uploads-Detection: 3 |
The request contains at least one content object (cf.waf.content_scan.has_obj is true). |
For more information, refer to Malicious uploads detection.
HTTP response headers
Remove "X-Powered-By" headers
Removes the X-Powered-By HTTP response header that provides information about the application at the origin server that handled the request.
Add security headers
:::note Adding the following security headers may have an impact on your website, such as blocking resources or triggering certificate errors. If you find any issues, try disabling the Managed Transform to isolate the possible cause. :::
Adds several security-related HTTP response headers. The added response headers and values are the following:
x-content-type-options: nosniffx-xss-protection: 1; mode=blockx-frame-options: SAMEORIGINreferrer-policy: same-originexpect-ct: max-age=86400, enforce
To increase protection, enable HTTP Strict Transport Security (HSTS) for your website.
Create a request header transform rule via API
import { Details, Render, APIRequest } from "~/components";
Use the Rulesets API to create Request Header Transform Rules via API. Refer to the Rules examples gallery for common use cases.
If you are using Terraform, refer to Transform Rules configuration using Terraform.
Basic rule settings
When creating a request header transform rule via API, make sure you:
- Set the rule action to
rewrite. - Define the header modification parameters in the
action_parametersfield according to the operation to perform (set or remove header). - Deploy the rule to the
http_request_late_transformphase at the zone level.
Procedure
<Render file="rules-creation-workflow" product="rules" params={{ ruleName: "a request header transform rule", phaseName: "http_request_late_transform", }} />
Make sure your API token has the required permissions to perform the API operations.
Example requests
The following example sets the rules of an existing phase ruleset ($RULESET_ID) to a single request header transform rule — adding an HTTP request header with a static value — using the Update a zone ruleset operation. The response will contain the complete definition of the ruleset you updated.
<APIRequest path="/zones/{zone_id}/rulesets/{ruleset_id}" method="PUT" json={{ rules: [ { ref: "add_header_source", expression: '(starts_with(http.request.uri.path, "/en/"))', description: "My first request header transform rule", action: "rewrite", action_parameters: { headers: { "X-Source": { operation: "set", value: "Cloudflare", }, }, }, }, ], }} />
{
"result": {
"id": "<RULESET_ID>",
"name": "Zone-level Late Transform Ruleset",
"description": "Zone-level ruleset that will execute Late Transform Rules.",
"kind": "zone",
"version": "2",
"rules": [
{
"ref": "add_header_source",
"id": "<RULE_ID>",
"version": "1",
"action": "rewrite",
"action_parameters": {
"headers": {
"X-Source": {
"operation": "set",
"value": "Cloudflare"
}
}
},
"expression": "(starts_with(http.request.uri.path, \"/en/\"))",
"description": "My first request header transform rule",
"last_updated": "2021-04-14T14:42:04.219025Z",
"ref": "<RULE_REF>"
}
],
"last_updated": "2021-04-14T14:42:04.219025Z",
"phase": "http_request_late_transform"
},
"success": true,
"errors": [],
"messages": []
}
The following example sets the rules of an existing phase ruleset ($RULESET_ID) to a single request header transform rule — adding an HTTP request header with a dynamic value — using the Update a zone ruleset operation. The response will contain the complete definition of the ruleset you updated.
<APIRequest path="/zones/{zone_id}/rulesets/{ruleset_id}" method="PUT" json={{ rules: [ { ref: "add_header_bot_score", expression: '(starts_with(http.request.uri.path, "/en/"))', description: "My first request header transform rule", action: "rewrite", action_parameters: { headers: { "X-Bot-Score": { operation: "set", expression: "to_string(cf.bot_management.score)", }, }, }, }, ], }} />
{
"result": {
"id": "<RULESET_ID>",
"name": "Zone-level Late Transform Ruleset",
"description": "Zone-level ruleset that will execute Late Transform Rules.",
"kind": "zone",
"version": "2",
"rules": [
{
"ref": "add_header_bot_score",
"id": "<RULE_ID>",
"version": "1",
"action": "rewrite",
"action_parameters": {
"headers": {
"X-Bot-Score": {
"operation": "set",
"expression": "to_string(cf.bot_management.score)"
}
}
},
"expression": "(starts_with(http.request.uri.path, \"/en/\"))",
"description": "My first request header transform rule",
"last_updated": "2021-04-14T14:42:04.219025Z",
"ref": "<RULE_REF>"
}
],
"last_updated": "2021-04-14T14:42:04.219025Z",
"phase": "http_request_late_transform"
},
"success": true,
"errors": [],
"messages": []
}
The following example sets the rules of an existing phase ruleset ($RULESET_ID) to a single request header transform rule — removing an HTTP request header — using the Update a zone ruleset. The response will contain the complete definition of the ruleset you updated.
<APIRequest path="/zones/{zone_id}/rulesets/{ruleset_id}" method="PUT" json={{ rules: [ { ref: "remove_header_cf_connecting_ip", expression: '(starts_with(http.request.uri.path, "/en/"))', description: "My first request header transform rule", action: "rewrite", action_parameters: { headers: { "cf-connecting-ip": { operation: "remove", }, }, }, }, ], }} />
{
"result": {
"id": "<RULESET_ID>",
"name": "Zone-level Late Transform Ruleset",
"description": "Zone-level ruleset that will execute Late Transform Rules.",
"kind": "zone",
"version": "2",
"rules": [
{
"ref": "remove_header_cf_connecting_ip",
"id": "<RULE_ID>",
"version": "1",
"action": "rewrite",
"action_parameters": {
"headers": {
"cf-connecting-ip": {
"operation": "remove"
}
}
},
"expression": "(starts_with(http.request.uri.path, \"/en/\"))",
"description": "My first request header transform rule",
"last_updated": "2021-04-14T14:42:04.219025Z",
"ref": "<RULE_REF>"
}
],
"last_updated": "2021-04-14T14:42:04.219025Z",
"phase": "http_request_late_transform"
},
"success": true,
"errors": [],
"messages": []
}
Required API token permissions
The API token used in API requests to manage Request Header Transform Rules must have at least the following permissions:
- Transform Rules > Edit
- Account Rulesets > Read
Create a request header transform rule in the dashboard
import { Render, Steps, DashButton } from "~/components";
Refer to the Rules examples gallery for examples of rule definitions.
To create a rule:
-
In the Cloudflare dashboard, go to the Rules Overview page.
-
Select Create rule > Request Header Transform Rule.
-
(Optional) Select one of the rule templates that address common use cases. Then, review and adjust the proposed rule configuration.
-
Enter a descriptive name for the rule in Rule name.
-
Under When incoming requests match, select if you wish to apply the rule to all incoming requests or only to requests that match a custom filter expression.
-
(Optional) To define a custom expression, use the Expression Builder (specifying one or more values for Field, Operator, and Value) or manually enter an expression using the Expression Editor. For more information, refer to Edit expressions in the dashboard.
:::note Check the available fields and functions. :::
-
For Modify request header, select one of the following options:
- Set static — Sets the value of an HTTP request header to a static string value. Overrides the value of an existing header with the same name or adds a new header if it does not exist.
- Set dynamic — Sets the value of an HTTP request header according to the provided expression. Overrides the value of an existing header with the same name or adds a new header if it does not exist.
- Remove — Removes the HTTP request header with the provided name, if it exists.
-
Enter the name of the HTTP request header to modify in Header name and the static value or expression in Value, if you are setting the header value.
-
To modify another HTTP request header in the same rule, select Set new header. You can modify up to 30 HTTP request headers in a single rule.
The following example includes the modification of three headers:

-
To save and deploy your rule, select Deploy. If you are not ready to deploy your rule, select Save as Draft.
Request Header Transform Rules
import { Render } from "~/components";
Use Request Header Transform Rules to manipulate the headers of HTTP requests sent to your origin server (the server where your website or application is hosted).
flowchart LR
accTitle: Header modifications diagram
accDescr: Header transform rules can change the headers sent to your origin server (request header modifications) or sent your website visitors (response header modifications).
A[Visitor]
B((Cloudflare))
C[(Origin server)]
A -.-> B == "Includes request<br> header modifications" ==> C
C -.-> B -. "Includes response<br> header modifications" .-> A
style A stroke-width: 2px
style B stroke: orange,fill: orange,color: black
linkStyle 0,2,3 stroke-width: 1px
linkStyle 1 stroke-width: 3px
To modify HTTP headers in the response sent to website visitors, refer to Response Header Transform Rules.
Through Request Header Transform Rules you can:
- Set the value of an HTTP request header to a literal string value, overwriting its previous value or adding a new header to the request.
- Set the value of an HTTP request header according to an expression (a formula that computes a value based on request properties), overwriting its previous value or adding a new header to the request.
- Remove an HTTP header from the request.
You can create a request header transform rule in the dashboard, via API, or using Terraform.
<Render file="snippets-alternative" product="rules" params={{ suffix: "request header modifications" }} />
Important remarks
-
You cannot modify or remove HTTP request headers whose name starts with
x-cf-orcf-except for thecf-connecting-ipHTTP request header, which you can remove. -
Due to protocol compliance reasons, modifying or removing request headers with forbidden header names (such as
Accept-Encoding) is generally not allowed in Request Header Transform Rules. -
You cannot modify the value of any header commonly used to identify the website visitor's IP address or initial protocol, such as
x-forwarded-for,true-client-ip,x-real-ip, orx-forwarded-proto. If your origin needs a custom value in a header likex-real-ipon requests reaching your origin server, use Cloudflare Snippets or Cloudflare Workers and set the header on afetch()subrequest. The value must be a syntactically valid IP address. This workaround does not work for cross-zone subrequests, where Cloudflare unconditionally replaces the value with an internal Cloudflare address to prevent IP spoofing. -
Although you can remove the
x-forwarded-forheader in a Request Header Transform Rule, Cloudflare's backend proxy re-adds it (with the visitor's IP address) before the request reaches your origin server, because the proxy runs after all rule phases. The same applies to Managed Transforms. However, if the request is handled by Cloudflare Workers — which run before the cache — thex-forwarded-forrequest header will be absent because the proxy has not yet re-added it. -
You cannot set or modify the value of
cookieHTTP request headers, but you can remove these headers. Configuring a rule that removes thecookieHTTP request header will remove allcookieheaders in matching requests. -
If you modify the value of an existing HTTP request header using an expression that evaluates to an empty string (
"") or an undefined value, the HTTP request header is removed. -
The HTTP request header removal operation will remove all request headers with the provided name.
-
Currently, there is a limited number of HTTP request headers that you cannot modify. Cloudflare may remove restrictions for some of these HTTP request headers when presented with valid use cases. Create a post in the community for consideration.
-
To use claims inside a JSON Web Token (JWT), you must first set up a token validation configuration in API Shield.
-
Request header transform rules run in order, and later rules can overwrite changes done by previous rules.
-
The values of request and response fields are immutable within each phase, such as the
http_request_late_transformphase where request header transform rules are defined. This means that later request header transform rules will still use the original field values when evaluating their filter expressions, not the values changed by previous rules. Refer to Field values during rule evaluation for more information.
Execution order
<Render file="troubleshoot-rules-with-trace" product="rules" params={{ rulesFeatureName: "Request Header Transform Rules" }} />
Available fields and functions
The available fields when setting an HTTP request header value using an expression are the following:
cf.bot_management.*cf.client.botcf.verified_bot_categorycf.edge.server_ipcf.edge.server_portcf.edge.client_portcf.edge.client_tcpcf.edge.l4.delivery_ratecf.hostname.metadatacf.llm.prompt.custom_topic_categoriescf.llm.prompt.detectedcf.llm.prompt.injection_scorecf.llm.prompt.pii_categoriescf.llm.prompt.pii_detectedcf.llm.prompt.token_countcf.llm.prompt.unsafe_topic_categoriescf.llm.prompt.unsafe_topic_detectedcf.zone.namecf.random_seedcf.ray_idcf.timings.client_quic_rtt_mseccf.timings.client_tcp_rtt_mseccf.tls_versioncf.tls_ciphercf.tls_client_hello_lengthcf.tls_client_randomcf.tls_client_extensions_sha1cf.tls_client_extensions_sha1_lecf.tls_client_ciphers_sha1cf.tls_client_auth.*cf.worker.upstream_zonecf.fraud.email_riskhttp.cookiehttp.hosthttp.refererhttp.request.accepted_languageshttp.request.cookieshttp.request.headershttp.request.headers.*http.request.methodhttp.request.body.formhttp.request.body.form.*http.request.body.multiparthttp.request.body.multipart.*http.request.body.rawhttp.request.body.sizehttp.request.body.truncatedhttp.request.timestamp.sechttp.request.timestamp.msechttp.request.full_urihttp.request.urihttp.request.uri.*http.request.versionraw.http.request.full_uriraw.http.request.uriraw.http.request.uri.*raw.http.request.headersraw.http.request.headers.*http.user_agenthttp.x_forwarded_forip.srcip.src.latip.src.lonip.src.asnumip.src.cityip.src.countryip.src.continentip.src.metro_codeip.src.postal_codeip.src.regionip.src.region_codeip.src.is_in_european_unionip.src.subdivision_1_iso_codeip.src.subdivision_2_iso_codesslhttp.request.jwt.claimshttp.request.jwt.claims.*cf.fraud_detection.disposable_emailcf.sequence.current_opcf.sequence.msec_since_opcf.sequence.previous_opscf.waf.auth_detectedcf.waf.credential_check.*cf.waf.scorecf.waf.score.*cf.appsec.request.failed_detections
Refer to Fields for reference information on these fields.
:::caution[Important]
-
To obtain the value of an HTTP header using the
http.request.headersfield, specify the header name in lowercase. For example, to get the first value of theAccept-Encodingrequest header in an expression, use:http.request.headers["accept-encoding"][0]. -
Use the
to_string()function to get the string representation of a non-string value like an Integer value. For example,to_string(cf.bot_management.score).
:::
For information on the available functions, refer to Functions.
Format of HTTP request header names and values
import { Render } from "~/components";
The name of the HTTP request header you want to set or remove can only contain:
The value of the HTTP request header you want to set can only contain:
The maximum length of the HTTP request header value is 4 KB (~4,096 characters).
Reference
import { DirectoryListing } from "~/components";
This section contains reference information for Request Header Transform Rules.
API parameter reference
To set an HTTP request header via API, set the following parameters in the action_parameters field:
-
operation:
set -
Include one of the following parameters to define a static or dynamic value:
- value: Specifies a static value for the HTTP request header.
- expression: Specifies the expression that defines a value for the HTTP request header.
To remove an HTTP request header via API, set the following parameter in the action_parameters field:
- operation:
remove
For step-by-step instructions, refer to Create a request header transform rule via API.
Static header value parameters
The full syntax of the action_parameters field to define a static HTTP request header value is the following:
"action_parameters": {
"headers": {
"<HEADER_NAME>": {
"operation": "set",
"value": "<URI_PATH_VALUE>"
}
}
}
Dynamic header value parameters
The full syntax of the action_parameters field to define a dynamic HTTP request header value using an expression is the following:
"action_parameters": {
"headers": {
"<HEADER_NAME>": {
"operation": "set",
"expression": "<EXPRESSION>"
}
}
}
:::note Check the available fields and functions you can use in an expression. :::
Header removal parameters
The full syntax of the action_parameters field to remove an HTTP request header is the following:
"action_parameters": {
"headers": {
"<HEADER_NAME>": {
"operation": "remove"
}
}
}
Different header modifications in the same rule
The same rule can modify different HTTP request headers using different operations (set or remove a header). For example, a single rule can set the value of a header and remove a different header. The syntax of such a rule could be the following:
"action_parameters": {
"headers": {
"<HEADER_NAME_1>": {
"operation": "set",
"value": "<HEADER_VALUE_1>"
},
"<HEADER_NAME_2>": {
"operation": "remove"
}
}
}
Create a response header transform rule via API
import { Details, Render, APIRequest } from "~/components";
Use the Rulesets API to create Response Header Transform Rules via API. Refer to the Rules examples gallery for common use cases.
If you are using Terraform, refer to Transform Rules configuration using Terraform.
Basic rule settings
When creating a response header transform rule via API, make sure you:
- Set the rule action to
rewrite. - Define the header modification parameters in the
action_parametersfield according to the operation to perform (set, add, or remove header). - Deploy the rule to the
http_response_headers_transformphase at the zone level.
Procedure
<Render file="rules-creation-workflow" product="rules" params={{ ruleName: "a response header transform rule", phaseName: "http_response_headers_transform", }} />
Make sure your API token has the required permissions to perform the API operations.
Example requests
The following example configures the rules of an existing phase ruleset ($RULESET_ID) to a single response header transform rule — setting an HTTP response header to a static value — using the Update a zone ruleset operation. The response will contain the complete definition of the ruleset you updated.
<APIRequest path="/zones/{zone_id}/rulesets/{ruleset_id}" method="PUT" json={{ rules: [ { ref: "set_resp_header_source", expression: '(starts_with(http.request.uri.path, "/en/"))', description: "My first response header transform rule", action: "rewrite", action_parameters: { headers: { "X-Source": { operation: "set", value: "Cloudflare", }, }, }, }, ], }} />
{
"result": {
"id": "<RULESET_ID>",
"name": "Zone-level Response Headers Transform Ruleset",
"description": "Zone-level ruleset that will execute Response Header Transform Rules.",
"kind": "zone",
"version": "2",
"rules": [
{
"ref": "set_resp_header_source",
"id": "<RULE_ID>",
"version": "1",
"action": "rewrite",
"action_parameters": {
"headers": {
"X-Source": {
"operation": "set",
"value": "Cloudflare"
}
}
},
"expression": "(starts_with(http.request.uri.path, \"/en/\"))",
"description": "My first response header transform rule",
"last_updated": "2021-04-14T14:42:04.219025Z",
"ref": "<RULE_REF>"
}
],
"last_updated": "2021-04-14T14:42:04.219025Z",
"phase": "http_response_headers_transform"
},
"success": true,
"errors": [],
"messages": []
}
The following example configures the rules of an existing phase ruleset ($RULESET_ID) to a single response header transform rule — setting an HTTP response header to a dynamic value — using the Update a zone ruleset operation. The response will contain the complete definition of the ruleset you updated.
<APIRequest path="/zones/{zone_id}/rulesets/{ruleset_id}" method="PUT" json={{ rules: [ { ref: "set_resp_header_bot_score", expression: '(starts_with(http.request.uri.path, "/en/"))', description: "My first response header transform rule", action: "rewrite", action_parameters: { headers: { "X-Bot-Score": { operation: "set", expression: "to_string(cf.bot_management.score)", }, }, }, }, ], }} />
{
"result": {
"id": "<RULESET_ID>",
"name": "Zone-level Response Headers Transform Ruleset",
"description": "Zone-level ruleset that will execute Response Header Transform Rules.",
"kind": "zone",
"version": "2",
"rules": [
{
"ref": "set_resp_header_bot_score",
"id": "<RULE_ID>",
"version": "1",
"action": "rewrite",
"action_parameters": {
"headers": {
"X-Bot-Score": {
"operation": "set",
"expression": "to_string(cf.bot_management.score)"
}
}
},
"expression": "(starts_with(http.request.uri.path, \"/en/\"))",
"description": "My first response header transform rule",
"last_updated": "2021-04-14T14:42:04.219025Z",
"ref": "<RULE_REF>"
}
],
"last_updated": "2021-04-14T14:42:04.219025Z",
"phase": "http_response_headers_transform"
},
"success": true,
"errors": [],
"messages": []
}
The following example configures the rules of an existing phase ruleset ($RULESET_ID) to a single response header transform rule — adding a set-cookie HTTP response header with a static value — using the Update a zone ruleset operation. By configuring the rule with the add operation you will keep any existing set-cookie headers that may already exist in the response. The response will contain the complete definition of the ruleset you updated.
<APIRequest path="/zones/{zone_id}/rulesets/{ruleset_id}" method="PUT" json={{ rules: [ { ref: "add_resp_header_set_mycookie", expression: '(starts_with(http.request.uri.path, "/en/"))', description: "My first response header transform rule", action: "rewrite", action_parameters: { headers: { "set-cookie": { operation: "add", value: "mycookie=custom_value", }, }, }, }, ], }} />
{
"result": {
"id": "<RULESET_ID>",
"name": "Zone-level Response Headers Transform Ruleset",
"description": "Zone-level ruleset that will execute Response Header Transform Rules.",
"kind": "zone",
"version": "2",
"rules": [
{
"ref": "add_resp_header_set_mycookie",
"id": "<RULE_ID>",
"version": "1",
"action": "rewrite",
"action_parameters": {
"headers": {
"set-cookie": {
"operation": "add",
"value": "mycookie=custom_value"
}
}
},
"expression": "(starts_with(http.request.uri.path, \"/en/\"))",
"description": "My first response header transform rule",
"last_updated": "2021-04-14T14:42:04.219025Z",
"ref": "<RULE_REF>"
}
],
"last_updated": "2021-04-14T14:42:04.219025Z",
"phase": "http_response_headers_transform"
},
"success": true,
"errors": [],
"messages": []
}
The following example sets the rules of an existing phase ruleset ($RULESET_ID) to a single response header transform rule — removing an HTTP response header — using the Update a zone ruleset operation. The response will contain the complete definition of the ruleset you updated.
<APIRequest path="/zones/{zone_id}/rulesets/{ruleset_id}" method="PUT" json={{ rules: [ { ref: "remove_resp_header_cf_connecting_ip", expression: '(starts_with(http.request.uri.path, "/en/"))', description: "My first response header transform rule", action: "rewrite", action_parameters: { headers: { "cf-connecting-ip": { operation: "remove", }, }, }, }, ], }} />
{
"result": {
"id": "<RULESET_ID>",
"name": "Zone-level Response Headers Transform Ruleset",
"description": "Zone-level ruleset that will execute Response Header Transform Rules.",
"kind": "zone",
"version": "2",
"rules": [
{
"ref": "remove_resp_header_cf_connecting_ip",
"id": "<RULE_ID>",
"version": "1",
"action": "rewrite",
"action_parameters": {
"headers": {
"cf-connecting-ip": {
"operation": "remove"
}
}
},
"expression": "(starts_with(http.request.uri.path, \"/en/\"))",
"description": "My first response header transform rule",
"last_updated": "2021-04-14T14:42:04.219025Z",
"ref": "<RULE_REF>"
}
],
"last_updated": "2021-04-14T14:42:04.219025Z",
"phase": "http_response_headers_transform"
},
"success": true,
"errors": [],
"messages": []
}
Required API token permissions
The API token used in API requests to manage Response Header Transform Rules must have at least the following permissions:
- Transform Rules > Edit
- Account Rulesets > Read
Create a response header transform rule in the dashboard
import { Render, Steps, DashButton } from "~/components";
Refer to the Rules examples gallery for examples of rule definitions.
To create a rule:
-
In the Cloudflare dashboard, go to the Rules Overview page.
-
Select Create rule > Response Header Transform Rule.
-
(Optional) Select one of the rule templates that address common use cases. Then, review and adjust the proposed rule configuration.
-
Enter a descriptive name for the rule in Rule name.
-
Under When incoming requests match, select if you wish to apply the rule to all incoming requests or only to requests that match a custom filter expression.
-
(Optional) To define a custom expression, use the Expression Builder (specifying one or more values for Field, Operator, and Value) or manually enter an expression using the Expression Editor. For more information, refer to Edit expressions in the dashboard.
:::note Check the available fields and functions. :::
-
For Modify response header, select one of the following operations:
- Add static — Adds an HTTP response header with a static string value. This operation will not remove any existing response headers with the same name.
- Add dynamic — Adds an HTTP response header according to the provided expression. This operation will not remove any existing response headers with the same name.
- Set static — Sets the value of an HTTP response header to a static string value. Overrides the value of any existing headers with the same name or adds a new header if it does not exist.
- Set dynamic — Sets the value of an HTTP response header according to the provided expression. Overrides the value of any existing headers with the same name or adds a new header if it does not exist.
- Remove — Removes the HTTP response header with the provided name, if it exists.
-
Enter the name of the HTTP response header to modify in Header name and the static value or expression in Value, if you are setting the header value.
-
To modify another HTTP response header in the same rule, select Set new header. You can modify up to 30 HTTP response headers in a single rule.
The following example includes the modification of three response headers:

-
To save and deploy your rule, select Deploy. If you are not ready to deploy your rule, select Save as Draft.
Response Header Transform Rules
import { Render } from "~/components";
Use Response Header Transform Rules to manipulate the headers of HTTP responses sent to website visitors.
flowchart LR
accTitle: Header modifications diagram
accDescr: Header transform rules can change the headers sent to your origin server (request header modifications) or sent your website visitors (response header modifications).
A[Visitor]
B((Cloudflare))
C[(Origin server)]
A -.-> B -. "Includes request<br> header modifications" .-> C
C -.-> B == "Includes response<br> header modifications" ==> A
style A stroke-width: 2px
style B stroke: orange,fill: orange,color: black
linkStyle 0,1,2 stroke-width: 1px
linkStyle 3 stroke-width: 3px
To modify HTTP headers in the request sent to your origin server, refer to Request Header Transform Rules.
Through Response Header Transform Rules you can:
- Set the value of an HTTP response header to a literal string value, overwriting its previous value or adding a new header to the response if it does not exist.
- Set the value of an HTTP response header according to an expression, overwriting its previous value or adding a new header to the response if it does not exist.
- Add a new HTTP response header with a literal string value without removing any existing headers with the same name.
- Add a new HTTP response header according to an expression without removing any existing headers with the same name.
- Remove an HTTP header from the response.
You can create a response header transform rule in the dashboard, via API, or using Terraform.
<Render file="snippets-alternative" product="rules" params={{ suffix: "response header modifications" }} />
Important remarks
-
The response header values are calculated using the field values from the corresponding HTTP request. For example, the field
ip.src.country(used in expressions) will return the country of the website visitor, not the country of the origin server where the response was sent from. -
You cannot add, modify, or remove HTTP response headers whose name starts with
cf-orx-cf-. -
You cannot modify the value of certain headers such as
server,eh-cache-tag, oreh-cdn-cache-control. -
Currently you cannot reference IP lists in expressions of Response Header Transform Rules.
-
The HTTP response header removal operation will remove all response headers with the provided name.
-
If you change the value of an existing HTTP response header using an expression that evaluates to an empty string (
"") or an undefined value, the HTTP response header is removed. -
Currently, there is a limited number of HTTP response headers that you cannot change. Cloudflare may remove restrictions for some of these HTTP response headers when presented with valid use cases. Create a post in the community for consideration.
-
Response header transform rules will also apply to default Cloudflare error pages and Custom Errors.
-
Modifying
cache-control,CDN-Cache-Control, orCloudflare-CDN-Cache-Controlheaders using response header transform rules will not change the way Cloudflare caches an object, because Cloudflare evaluates caching behavior before applying response header modifications. To control Cloudflare cache behavior, create a cache rule. -
To add a
set-cookieheader to the response, use one of the Add static/Add dynamic operations instead of Set static/Set dynamic. Add operations append a new header without removing existing headers of the same name, while Set operations replace all existing headers of that name. Using a Set operation forset-cookiewill remove anyset-cookieheaders already in the response, including those added by other Cloudflare products such as Bot Management. -
Response header transform rules run in order, and later rules can overwrite changes done by previous rules.
-
The values of request and response fields are immutable within each phase, such as the
http_response_headers_transformphase where response header transform rules are defined. This means that later response header transform rules will still use the original field values when evaluating their filter expressions, not the values changed by previous rules. Refer to Field values during rule evaluation for more information.
<Render file="troubleshoot-rules-with-trace" product="rules" params={{ rulesFeatureName: "Response Header Transform Rules" }} />
Available fields and functions
The available fields when setting an HTTP response header value using an expression are the following:
cf.bot_management.*cf.client.botcf.verified_bot_categorycf.edge.server_ipcf.edge.server_portcf.edge.client_portcf.edge.client_tcpcf.edge.l4.delivery_ratecf.hostname.metadatacf.zone.namecf.random_seedcf.ray_idcf.timings.client_quic_rtt_mseccf.timings.client_tcp_rtt_mseccf.tls_versioncf.tls_ciphercf.tls_client_hello_lengthcf.tls_client_randomcf.tls_client_extensions_sha1cf.tls_client_extensions_sha1_lecf.tls_client_ciphers_sha1cf.tls_client_auth.*cf.worker.upstream_zonecf.fraud.email_riskhttp.cookiehttp.hosthttp.refererhttp.request.accepted_languageshttp.request.cookieshttp.request.headershttp.request.headers.*http.request.methodhttp.request.body.formhttp.request.body.form.*http.request.body.multiparthttp.request.body.multipart.*http.request.body.rawhttp.request.body.sizehttp.request.body.truncatedhttp.request.timestamp.sechttp.request.timestamp.msechttp.request.full_urihttp.request.urihttp.request.uri.*http.request.versionraw.http.request.full_uriraw.http.request.uriraw.http.request.uri.*raw.http.request.headersraw.http.request.headers.*http.user_agenthttp.x_forwarded_forip.srcip.src.latip.src.lonip.src.asnumip.src.cityip.src.countryip.src.continentip.src.metro_codeip.src.postal_codeip.src.regionip.src.region_codeip.src.is_in_european_unionip.src.subdivision_1_iso_codeip.src.subdivision_2_iso_codesslhttp.response.codehttp.response.content_type.media_typehttp.response.headershttp.response.headers.*raw.http.response.headersraw.http.response.headers.*cf.response.1xxx_codecf.response.error_typecf.timings.edge_mseccf.timings.origin_ttfb_mseccf.timings.worker_msec
Refer to Fields for reference information on these fields.
:::caution[Important]
-
To obtain the value of an HTTP header using the
http.request.headersorhttp.response.headersfield, specify the header name in lowercase. For example, to get the first value of theAccept-Encodingrequest header in an expression, use:http.request.headers["accept-encoding"][0]. -
Use the
to_string()function to get the string representation of a non-string value like an Integer value. For example,to_string(cf.bot_management.score).
:::
For information on the available functions, refer to Functions.
Format of HTTP response header names and values
import { Render } from "~/components";
The name of the HTTP response header you want to set or remove can only contain:
The value of the HTTP response header you want to set can only contain:
The length of the HTTP response header can be a maximum of 4 KB (~4,096 characters).
Reference
import { DirectoryListing } from "~/components";
This section contains reference information for Response Header Transform Rules.
API parameter reference
To set an HTTP response header, overwriting any headers with the same name, use the following parameters in the action_parameters field:
- operation:
set - Include one of the following parameters to define a static or dynamic value:
- value: Specifies a static value for the HTTP response header.
- expression: Specifies the expression that defines a value for the HTTP response header.
To add an HTTP response header, keeping any existing headers with the same name, use the following parameters in the action_parameters field:
- operation:
add - Include one of the following parameters to define a static or dynamic value:
- value: Specifies a static value for the HTTP response header.
- expression: Specifies the expression that defines a value for the HTTP response header.
To remove an HTTP response header, set the following parameter in the action_parameters field:
- operation:
remove
Static header value parameters
The full syntax of the action_parameters field to define a static HTTP response header value is the following:
"action_parameters": {
"headers": {
"<HEADER_NAME>": {
"operation": "<set|add>",
"value": "<URI_PATH_VALUE>"
}
}
}
Dynamic header value parameters
The full syntax of the action_parameters field to define a dynamic HTTP response header value using an expression is the following:
"action_parameters": {
"headers": {
"<HEADER_NAME>": {
"operation": "<set|add>",
"expression": "<EXPRESSION>"
}
}
}
:::note Check the available fields and functions you can use in an expression. :::
Header removal parameters
The full syntax of the action_parameters field to remove an HTTP response header is the following:
"action_parameters": {
"headers": {
"<HEADER_NAME>": {
"operation": "remove"
}
}
}
Different header modifications in the same rule
The same rule can modify different HTTP response headers using different operations. For example, a single rule can set the value of a header and remove a different header. The syntax of such a rule could be the following:
"action_parameters": {
"headers": {
"<HEADER_NAME_1>": {
"operation": "set",
"value": "<HEADER_VALUE_1>"
},
"<HEADER_NAME_2>": {
"operation": "remove"
}
}
}
Create a URL rewrite rule via API
import { Details, Render, APIRequest } from "~/components";
Use the Rulesets API to create URL Rewrite Rules via API. Refer to the Rules examples gallery for common use cases.
If you are using Terraform, refer to Transform Rules configuration using Terraform.
Basic rule settings
When creating a URL rewrite rule via API, make sure you:
- Set the rule action to
rewrite. - Define the URL rewrite parameters in the
action_parametersfield according to the type of URL rewrite (static or dynamic). - Deploy the rule to the
http_request_transformphase at the zone level.
Procedure
<Render file="rules-creation-workflow" product="rules" params={{ ruleName: "a URL rewrite rule", phaseName: "http_request_transform", }} />
Make sure your API token has the required permissions to perform the API operations.
Example requests
The following example sets the rules of an existing phase ruleset ($RULESET_ID) to a single URL rewrite rule — performing a static rewrite of the URI path — using the Update a zone ruleset operation. The response will contain the complete definition of the ruleset you updated.
<APIRequest path="/zones/{zone_id}/rulesets/{ruleset_id}" method="PUT" json={{ rules: [ { ref: "rewrite_eu_to_emea", expression: '(http.request.uri.query contains "eu")', description: "My first static URL rewrite rule", action: "rewrite", action_parameters: { uri: { path: { value: "/emea.html", }, }, }, }, ], }} />
{
"result": {
"ref": "rewrite_eu_to_emea",
"id": "<RULESET_ID>",
"name": "Zone-level Transform Ruleset",
"description": "Zone-level ruleset that will execute Transform Rules.",
"kind": "zone",
"version": "2",
"rules": [
{
"id": "<RULE_ID>",
"version": "1",
"action": "rewrite",
"action_parameters": {
"uri": {
"path": {
"value": "/emea.html"
}
}
},
"expression": "(http.request.uri.query contains \"eu\")",
"description": "My first static URL rewrite rule",
"last_updated": "2021-04-14T14:42:04.219025Z",
"ref": "<RULE_REF>"
}
],
"last_updated": "2021-04-14T14:42:04.219025Z",
"phase": "http_request_transform"
},
"success": true,
"errors": [],
"messages": []
}
The following example sets the rules of an existing phase ruleset ($RULESET_ID) to a single URL rewrite rule — performing a dynamic rewrite of the URI path — using the Update a zone ruleset operation. The response will contain the complete definition of the ruleset you updated.
<APIRequest path="/zones/{zone_id}/rulesets/{ruleset_id}" method="PUT" json={{ rules: [ { ref: "rewrite_2012_to_archive", expression: 'starts_with(http.request.uri.path, "/news/2012/")', description: "My first dynamic URL rewrite rule", action: "rewrite", action_parameters: { uri: { path: { expression: 'concat("/archive", http.request.uri.path)', }, }, }, }, ], }} />
{
"result": {
"id": "<RULESET_ID>",
"name": "Zone-level Transform Ruleset",
"description": "Zone-level ruleset that will execute Transform Rules.",
"kind": "zone",
"version": "2",
"rules": [
{
"ref": "rewrite_2012_to_archive",
"id": "<RULE_ID>",
"version": "1",
"action": "rewrite",
"action_parameters": {
"uri": {
"path": {
"expression": "concat(\"/archive\", http.request.uri.path)"
}
}
},
"expression": "starts_with(http.request.uri.path, \"/news/2012/\")",
"description": "My first dynamic URL rewrite rule",
"last_updated": "2021-04-14T14:42:04.219025Z",
"ref": "<RULE_REF>"
}
],
"last_updated": "2021-04-14T14:42:04.219025Z",
"phase": "http_request_transform"
},
"success": true,
"errors": [],
"messages": []
}
Required API token permissions
The API token used in API requests to manage URL Rewrite Rules must have at least the following permissions:
- Account > Transform Rules > Edit
- Account > Account Rulesets > Read
Create a URL rewrite rule in the dashboard
import { Render, Steps, DashButton } from "~/components";
Refer to the Rules examples gallery for examples of rule definitions.
To create a rule:
-
In the Cloudflare dashboard, go to the Rules Overview page.
-
Select Create rule > URL Rewrite Rule.
-
(Optional) Select one of the rule templates that address common use cases. Then, review and adjust the proposed rule configuration.
-
Enter a descriptive name for the rule in Rule name.

-
Under If incoming requests match, select one of the following options:
-
Wildcard pattern: The rule will only apply to traffic matching the wildcard pattern in Request URL. Refer to Wildcard pattern parameters for details.
-
Custom filter expression: The rule will only apply to traffic matching a custom expression. Define the rule expression to configure which requests should be rewritten. Use either the Expression Builder or the Expression Editor to define the custom expression. For more information, refer to Edit expressions in the dashboard.
:::note Check the fields and functions you can use in filter expressions of URL rewrite rules. :::
-
All incoming requests: The rule will apply to all traffic.
-
-
(Optional) Define the action for your URL rewrite rule by selecting one of the available options displayed as radio buttons, and then a value from the drop-down list, depending on the action:
-
If you select Rewrite to > Static, enter the string that will replace the original URL path (or query string). For example, enter
welcome-gb.htmlto rewrite the original URL path to/welcome-gb.html. -
If you select Rewrite to > Dynamic, enter a rewrite expression that defines the dynamic URL rewrite to perform.
-
If you do not want to change the value of a component of the original request (the URL path or the URL query string), choose Preserve for that component.
For more information, refer to URL rewrite parameters.
-
-
(Optional) Under Place at, define where to place the rule in the rules list: first rule in the list, last rule in the list, or in a custom position (after a given rule).
-
To save and deploy your rule, select Deploy. If you are not ready to deploy your rule, select Save as Draft.
Wildcard pattern parameters
The Cloudflare dashboard offers a simplified user interface for creating URL rewrites based on wildcard matching and replacement. When you select Wildcard pattern, you will have the following parameters available:
-
Request URL: Enter the wildcard pattern using the asterisk (
*) character to match multiple requests. For example,http*://*.example.com/*. -
Then rewrite the path and/or query: Define the URL rewrite settings including:
- Path > Target path: Enter the URI path to match, which can include wildcards (for example,
/oldpath/*). - Path > Rewrite to: Enter the new URI path. You can use wildcard replacement such as
${1}and${2}to define a dynamic target path (for example,/newpath/${1}). Leave this field empty to remove the URI path. - Query > Target query: Enter the query string to match, which can include wildcards (for example,
?sort=*). - Query > Rewrite to: Enter the new query string. You can use wildcard replacement such as
${1}and${2}to define a dynamic query string (for example,?order=${1}). Leave this field empty to remove the query string.
- Path > Target path: Enter the URI path to match, which can include wildcards (for example,
Refer to URL rewrite parameters for the equivalent rule configuration when using the API.
:::note[Notes]
The Request URL value is only used to match the incoming request with a rule. It will not be used for capturing URL patterns for rewrites. If you are matching the URL path or query string in Target path or Target query, respectively, make sure that the Request URL pattern also matches the incoming request, or else the rule will not trigger.
To validate URL rewrite rule matches, use Rules simulator. To validate rewritten URLs, check your origin server.
:::
URL Rewrite Rules
import { Render } from "~/components";
You can manipulate the URL of a request through different operations, namely rewrites and redirects:
-
URL rewrite: A server-side operation that converts a source URL into a target URL. It occurs before a web server has fully processed a request. A rewrite is not visible to website visitors, since the URL displayed in the browser does not change. Configure URL Rewrite Rules to perform rewrites on the Cloudflare global network without reaching your web server.
-
URL redirect: A client-side operation that converts a source URL into a target URL. It occurs after the web server has loaded the initial URL. In this case, a website visitor can notice the URL changing when the redirect occurs. Refer to Redirects to learn more about configuring redirects.
Use a URL rewrite rule to return the content of a URL while displaying a different URL in the browser. You can rewrite the URI path, the query string, or both.
:::caution
You cannot rewrite the hostname using a URL rewrite rule. To rewrite the hostname, use an origin rule.
<Render file="snippets-alternative" product="rules" params={{ suffix: "rewrite logic" }} />
:::
Static and dynamic rewrites
URL Rewrite Rules can perform static or dynamic rewrites:
- Static rewrite: Replaces a given part of a request URL (path or query string) with a static string.
- Dynamic rewrite: Supports more advanced scenarios where you use a rewrite expression (a formula based on request properties) to define the resulting path or query string.
Create URL Rewrite Rules in the dashboard, via Cloudflare API, or using Terraform.
Serve images from custom paths
When using Cloudflare Images, you can use URL Rewrite Rules to serve images from a custom path. For more information, refer to Serve images from custom domains.
<Render file="troubleshoot-rules-with-trace" product="rules" params={{ rulesFeatureName: "URL Rewrite Rules" }} />
Important remarks
-
URL rewrite rules run in order, and later rules can overwrite changes done by previous rules.
-
The values of request and response fields are immutable within each phase, such as the
http_request_transformphase where URL rewrite rules are defined. This means that later URL rewrite rules will still use the original field values when evaluating their filter expressions, not the values changed by previous rules. Refer to Field values during rule evaluation for more information.
Available fields and functions
import { Render } from "~/components";
Filter expressions
A URL rewrite rule filter expression (that is, the expression that defines which incoming requests match the rule) can include the following fields:
For information on the available functions, refer to Functions.
Rewrite expressions
A rewrite expression (that is, the expression that defines the dynamic URL rewrite to perform) can only include the following fields:
http.request.uri.*http.request.headers.*http.request.accepted_languages
Refer to the Fields reference for more information on these fields.
The concat(), regex_replace(), and wildcard_replace() functions can appear only once in a rewrite expression. Additionally, you cannot nest the regex_replace() and wildcard_replace() functions.
Reference
import { DirectoryListing } from "~/components";
This section contains reference information for URL Rewrite Rules.
URL rewrite parameters
Static and dynamic URL rewrites have different parameters:
- A static URL rewrite requires a static value for the target URL.
- A dynamic URL rewrite requires an expression that, when evaluated, will define the target URL.
A URL rewrite with wildcard patterns is a simplified interface in the Cloudflare dashboard for creating dynamic URL rewrites with wildcard matching and replacement.
The maximum length of all parameter values in a URL rewrite (combined) is 4,096 characters. For example, you could provide a static value (or an expression) for the URI path with 2,048 characters and a static value (or expression) for the query string with 2,048 characters.
API information
Static URL rewrites
The full syntax of the action_parameters field for a static URL rewrite rule that rewrites both the URI path and the query string is the following:
"action_parameters": {
"uri": {
"path": {
"value": "<URI_PATH_VALUE>"
},
"query": {
"value": "<QUERY_STRING_VALUE>"
}
}
}
If you are only rewriting the URI path or the query string, omit the query or path parameter, respectively.
Dynamic URL rewrites
The full syntax of the action_parameters field for a dynamic URL rewrite rule that rewrites both the URI path and the query string is the following:
"action_parameters": {
"uri": {
"path": {
"expression": "<URI_PATH_EXPRESSION>"
},
"query": {
"expression": "<QUERY_STRING_EXPRESSION>"
}
}
}
If you are only rewriting the URI path or the query string, omit the query or path parameter, respectively.
Wildcard matching and replacement
The syntax of a dynamic URL rewrite rule that rewrites both the URI path and the query string based on wildcard matching and replacement is the following:
{
"expression": "(http.request.full_uri wildcard r\"<REQUEST_URL>\")",
"action_parameters": {
"uri": {
"path": {
"expression": "wildcard_replace(http.request.uri.path, r\"<PATH_TARGET_PATH>\", r\"<PATH_REWRITE_TO>\")"
},
"query": {
"expression": "wildcard_replace(http.request.uri.query, r\"<QUERY_TARGET_QUERY>\", r\"<QUERY_REWRITE_TO>\")"
}
}
},
"action": "rewrite"
// ...
}
The <REQUEST_URL>, <PATH_TARGET_PATH>, <PATH_REWRITE_TO>, <QUERY_TARGET_QUERY>, and <QUERY_REWRITE_TO> value placeholders correspond to the fields available in the Cloudflare dashboard when you select the Wildcard pattern option. For more information, refer to Wildcard pattern parameters.
:::note
The http.request.uri.query field does not include the ? delimiter at the beginning, which means that your <QUERY_TARGET_QUERY> value should not try to match an initial ?.
:::
Different URL rewrite types in the same rule
The same rule can have different types of URL rewrites for the URI path and the query string. For example, a single rule can perform a dynamic URL rewrite of the URI path and a static URL rewrite of the query string. The syntax of such a rule would be the following:
"action_parameters": {
"uri": {
"path": {
"expression": "<URI_PATH_EXPRESSION>"
},
"query": {
"value": "<QUERY_STRING_VALUE>"
}
}
}
Redirects
import { Render } from "~/components";
URL forwarding, also known as URL redirection, navigates the user from a source URL to a target URL with a specific HTTP status code.
Use the following Cloudflare products to perform URL redirects, according to your use case:
-
Single Redirects: Allow you to create static or dynamic redirects at the zone level (a single domain or subdomain). A wildcard-based interface allows you to define source and target URL patterns without complex functions or regular expressions, efficiently covering thousands of URLs with a single rule.
-
Bulk Redirects: Allow you to define a large number of redirects at the account level, which can apply across domains in your account. These URL redirects are essentially static — they do not support string replacement operations or regular expressions. However, you can configure parameters that affect how source URLs are matched and how the redirect is performed.
-
Snippets: Use short pieces of JavaScript code for a more flexible way to define complex redirect functionality. Consider a few examples to get started.
<Render file="rules-requirements" product="rules" params={{ sentenceIntro: "Single Redirects and Bulk Redirects require" }} />
Redirect Rules templates
Availability
Single Redirects and Bulk Redirects are available on all Cloudflare plans. The exact quotas and features depend on your plan.
Bulk redirects
| Free | Pro | Business | Enterprise | |
|---|---|---|---|---|
| Availability | Yes | Yes | Yes | Yes |
| Bulk Redirect Rules | 15 | 15 | 15 | 50 |
| Bulk Redirect Lists | 5 | 5 | 5 | 25 |
| URL redirects across lists | 10,000 | 25,000 | 50,000 | 1,000,000 |
For URL redirects across lists, this table provides the default quota for the Enterprise plan. Bulk Redirects supports several million URL redirects — to get more redirects, contact your account team.
Bulk Redirects features and quotas are per account and they depend on the highest Cloudflare plan on your account.
Single Redirects
| Free | Pro | Business | Enterprise | |
|---|---|---|---|---|
| Availability | Yes | Yes | Yes | Yes |
| Number of rules | 10 | 25 | 50 | 300 |
| Wildcard support | Yes | Yes | Yes | Yes |
| Regex support | No | No | Yes | Yes |
Single Redirects features and quotas are per zone and depend on the zone plan.
Execution order
<Render file="troubleshoot-rules-with-trace" product="rules" params={{ rulesFeatureName: "URL redirects" }} />
Bulk Redirects concepts
import { Example, GlossaryTooltip } from "~/components";
Bulk Redirects involve the following elements:
-
URL redirect: An entry with a source URL, a target URL, a status code, and redirect parameters. URL redirects are the individual items in Bulk Redirect Lists.
-
Bulk Redirect List: A named collection containing one or more URL redirects. To activate all the URL redirects in a Bulk Redirect List, reference the list in a Bulk Redirect Rule. Different Bulk Redirect Rules can reference the same Bulk Redirect List.
-
Bulk Redirect Rule: A rule powered by the Ruleset Engine, which is the system Cloudflare uses to evaluate and execute rules. A Bulk Redirect Rule has an associated Bulk Redirect List.
A Bulk Redirect Rule enables a Bulk Redirect List, which contains one or more URL redirects.

The following example defines a Bulk Redirect List named list_b with two URL redirects:
list_b Bulk Redirect List
| Source URL | Target URL | Status code |
|---|---|---|
example.com/about |
https://example.com/news |
301 (the default) |
example.com/new_feature |
https://example.com/soon |
302 |
The following Bulk Redirect Rule, named Rule 2, enables the URL redirects in the list_b Bulk Redirect List:
Rule 2 Bulk Redirect Rule
- Rule name:
Rule 2 - Associated list:
list_b
URL redirects
A URL redirect allows you to configure a source URL, a target URL, a status code, and redirect parameters.
When specifying the source URL, use the available redirect parameters instead of wildcards, which are not supported. For example, the Include subdomains parameter allows you to configure a single URL redirect that applies both to subdomains (for example, https://b.example.com and https://a.b.example.com) and to the apex domain (the root domain with no subdomain – for example, https://example.com). Other parameters allow you to specify how the source URL’s path and query string are handled. For more information, refer to How Bulk Redirects work.
URL redirects are the list items of Bulk Redirect Lists.
Bulk Redirect Lists
Bulk Redirect Lists allow you to create distinct groups of URL redirects for different purposes. You can use a Bulk Redirect List in one or more Bulk Redirect Rules.
A Bulk Redirect List does not perform any redirects on its own — you must reference the list in a Bulk Redirect Rule to enable the redirects in the list.
A Bulk Redirect List cannot contain several URL redirects with the exact same source URL.
For details on the CSV format for importing items to a Bulk Redirect List, refer to CSV file format for Bulk Redirects.
:::note
You can only reference Bulk Redirect Lists in Bulk Redirect Rules. Other types of rules powered by the Ruleset Engine do not support Bulk Redirect Lists.
:::
Bulk Redirect Rules
Bulk Redirect Rules are rules powered by the Ruleset Engine that enable one or more URL redirects through a Bulk Redirect List.
When you configure a Bulk Redirect Rule, you associate a Bulk Redirect List to it, which enables all the URL redirects in that list. You can create a rule for each list, or have many Bulk Redirect Rules referencing the same Bulk Redirect List.
A Bulk Redirect Rule, like all rules powered by the Ruleset Engine, has an action (what happens when the rule triggers) and an expression (the conditions that must be met). Besides these two properties, it also has a name, an optional description, an associated Bulk Redirect List, and a key.
Expression
The rule expression, or filter expression, specifies the conditions that must be met for the rule to run. By default, all URL redirects of the specified list will apply.
The default expression of a Bulk Redirect Rule is the following:
http.request.full_uri in $<LIST_NAME>
This expression means that the request URL, after some basic normalization (if URL normalization is enabled), should match the source URL of a URL redirect in the list <LIST_NAME> for the redirect to be applied.
You can use an expression different from the default one to increase the specificity of URL redirect matches. For example, if you set the expression of a Bulk Redirect Rule to the following expression, there will only be a match for requests coming from the United Kingdom:
ip.src.country == "GB" and http.request.full_uri in $<LIST_NAME>
For more information on the available fields, refer to Available fields and functions.
:::note
At the left of the in operator you can only use fields directly and not values returned by a function. In most situations, you will want to use one of the following fields with the in operator:
http.request.full_uriraw.http.request.full_uri
Refer to the Fields reference for more information.
:::
Key
The rule key specifies which request field Cloudflare uses to look up a matching URL redirect in the associated Bulk Redirect List.
When there is a match for the rule expression, Cloudflare compares the value of the rule key against the source URL of each URL redirect in the associated Bulk Redirect List, searching for a match.
The key should be either http.request.full_uri or raw.http.request.full_uri. Use raw.http.request.full_uri to compare the URI received by the web server, before normalization, with the source URLs in the Bulk Redirect List.
The URI field used in the key must be the same as the URI field used in the expression. Otherwise, you may have a match for the rule expression, but no match for any of the source URLs in the list. For example, if you set the key to http.request.full_uri, the field used in the rule expression must also be http.request.full_uri. Conversely, if you set the key to raw.http.request.full_uri, the field used in the expression must be raw.http.request.full_uri.
Create Bulk Redirects via API
import { Render, Steps, APIRequest, Tabs, TabItem } from "~/components";
To create Bulk Redirects via API, you must:
- Create a Bulk Redirect List via API.
- Add items (URL redirects) to the list created in step 1.
- Create a Bulk Redirect Rule via API, which enables the list created in step 1.
<Render file="url-forwarding/requires-proxied-site" product="rules" params={{ forwardingRuleName: "Bulk Redirects" }} />
1. Create a Bulk Redirect List via API
Use the Create a list operation to create a new Bulk Redirect List. The list kind must be redirect.
<APIRequest path="/accounts/{account_id}/rules/lists" method="POST" json={{ name: "my_redirect_list", description: "My redirect list.", kind: "redirect", }} />
{
"result": {
"id": "f848b6ccb07647749411f504d6f88794",
"name": "my_redirect_list",
"description": "My redirect list.",
"kind": "redirect",
"num_items": 0,
"num_referencing_filters": 0,
"created_on": "2021-10-28T09:11:42Z",
"modified_on": "2021-10-28T09:11:42Z"
},
"success": true,
"errors": [],
"messages": []
}
Take note of the list ID — you will need it in the next step.
For more information on list operations, refer to the Lists API documentation.
2. Add items to the list
Use the Create list items operation to add URL redirect items to the list. Enter the list ID from the previous step in the endpoint URL:
<APIRequest path="/accounts/{account_id}/rules/lists/{list_id}/items" method="POST" parameters={{ list_id: "f848b6ccb07647749411f504d6f88794", }} json={[ { redirect: { source_url: "example.com/blog/", target_url: "https://example.com/blog/latest", }, }, { redirect: { source_url: "example.net/", target_url: "https://example.net/under-construction.html", status_code: 307, }, }, ]} />
{
"result": {
"operation_id": "92558f8b296d4dbe9d0419e0e53f6622"
},
"success": true,
"errors": [],
"messages": []
}
This is an asynchronous operation. The response will contain an operation_id which you will use to check if the operation completed successfully using the Get bulk operation status operation:
<APIRequest path="/accounts/{account_id}/rules/lists/bulk_operations/{operation_id}" method="GET" parameters={{ operation_id: "92558f8b296d4dbe9d0419e0e53f6622", }} />
Once the operation has completed successfully, the response will be similar to the following:
{
"result": {
"id": "92558f8b296d4dbe9d0419e0e53f6622",
"status": "completed",
"completed": "2021-10-28T09:15:42Z"
},
"success": true,
"errors": [],
"messages": []
}
3. Create a Bulk Redirect Rule via API
Since Bulk Redirect Lists are essentially containers of URL redirects, you have to enable the URL redirects in the list by creating a Bulk Redirect Rule.
Add Bulk Redirect Rules to the entry point ruleset of the http_request_redirect phase at the account level. Refer to the Rulesets API documentation for more information on creating a ruleset and supplying a list of rules for the ruleset.
A Bulk Redirect Rule must have:
actionset toredirect- An
action_parametersobject with additional configuration settings — refer to Bulk Redirects API JSON objects for details.
The following request of the Create an account ruleset operation creates a phase entry point ruleset for the http_request_redirect phase at the account level, and defines a single redirect rule. Use this operation if you have not created a phase entry point ruleset for the http_request_redirect phase yet.
<APIRequest path="/accounts/{account_id}/rulesets" method="POST" json={{ name: "My redirect ruleset", kind: "root", phase: "http_request_redirect", rules: [ { ref: "enable_my_redirect_list", expression: "http.request.full_uri in $my_redirect_list", description: "Bulk Redirect rule.", action: "redirect", action_parameters: { from_list: { name: "my_redirect_list", key: "http.request.full_uri", }, }, }, ], }} />
{
"result": {
"id": "528f4f03bf0da53a29907199625867be",
"name": "My redirect ruleset",
"kind": "root",
"version": "1",
"rules": [
{
"ref": "enable_my_redirect_list",
"id": "8da312df846b4258a05bcd454ea943be",
"version": "1",
"expression": "http.request.full_uri in $my_redirect_list",
"description": "Bulk Redirect rule.",
"action": "redirect",
"action_parameters": {
"from_list": {
"name": "my_redirect_list",
"key": "http.request.full_uri"
}
},
"last_updated": "2021-10-28T09:20:42Z"
}
],
"last_updated": "2021-10-28T09:20:42Z",
"phase": "http_request_redirect"
},
"success": true,
"errors": [],
"messages": []
}
If there is already a phase entry point ruleset for the http_request_redirect phase, use the Update an account ruleset operation instead, like in the following example:
<APIRequest path="/accounts/{account_id}/rulesets/{ruleset_id}" method="PUT" json={{ name: "My redirect ruleset", kind: "root", phase: "http_request_redirect", rules: [ { ref: "eval_redirects_list_1", expression: "http.request.full_uri in $my_redirect_list_1", description: "Bulk Redirect rule 1", action: "redirect", action_parameters: { from_list: { name: "my_redirect_list_1", key: "http.request.full_uri", }, }, }, { ref: "eval_redirects_list_2", expression: "http.request.full_uri in $my_redirect_list_2", description: "Bulk Redirect rule 2", action: "redirect", action_parameters: { from_list: { name: "my_redirect_list_2", key: "http.request.full_uri", }, }, }, ], }} />
{
"result": {
"id": "67013aa153df4e5fbda92f92bc979331",
"name": "default",
"description": "",
"kind": "root",
"version": "2",
"rules": [
{
"ref": "eval_redirects_list_1",
"id": "8be62ab2ef9a4a41af30c24ff8e73e41",
"version": "1",
"action": "redirect",
"action_parameters": {
"from_list": {
"name": "my_redirect_list_1",
"key": "http.request.full_uri"
}
},
"expression": "http.request.full_uri in $my_redirect_list_1",
"description": "Bulk Redirect rule 1",
"last_updated": "2021-12-03T15:38:51.658387Z",
"ref": "8be62ab2ef9a4a41af30c24ff8e73e41",
"enabled": true
},
{
"ref": "eval_redirects_list_2",
"id": "97e38797fb2b4b22a4919800f1318a5c",
"version": "1",
"action": "redirect",
"action_parameters": {
"from_list": {
"name": "my_redirect_list_2",
"key": "http.request.full_uri"
}
},
"expression": "http.request.full_uri in $my_redirect_list_2",
"description": "Bulk Redirect rule 2",
"last_updated": "2021-12-03T15:38:51.658387Z",
"ref": "97e38797fb2b4b22a4919800f1318a5c",
"enabled": true
}
],
"last_updated": "2021-12-03T15:38:51.658387Z",
"phase": "http_request_redirect"
},
"success": true,
"errors": [],
"messages": []
}
Required API token permissions
The API token used in API requests to manage Bulk Redirects objects (lists, list items, and rules) must have at least the following permissions:
- Account > Bulk URL Redirects > Edit
- Account > Account Filter Lists > Edit
- Mass URL Redirects Write
- Account Rule Lists Write
Create Bulk Redirects in the dashboard
import { Details, Render, DashButton, Steps } from "~/components";
To create Bulk Redirects in the Cloudflare dashboard you must:
- Create a Bulk Redirect List with one or more URL redirects.
- Create a Bulk Redirect Rule to enable the URL redirects in the list.
You can create Bulk Redirect Lists and Bulk Redirect Rules in the Cloudflare dashboard:
- At the account level, in Bulk redirects.
- At the zone level, go to Rules > Settings and select the Bulk Redirects tab.
However, the lists and rules only exist at the account level and every zone in the same account will show the same items.
<Render file="url-forwarding/requires-proxied-site" product="rules" params={{ forwardingRuleName: "Bulk Redirects" }} />
1. Create a Bulk Redirect List
1. In the Cloudflare dashboard, go to the **Bulk redirects** page. <DashButton url="/?to=/:account/bulk-redirects" />
2. Under **Bulk Redirect Lists**, select **Create Bulk Redirect List**.
3. Enter a list name and description, and select **Next**.
4. You can import a CSV file containing several URL redirects or enter URL redirects one at a time in the dashboard.
:::note
The source URL of each redirect cannot include a query string. For more information, refer to the [supported URL components](/rules/url-forwarding/bulk-redirects/reference/url-components/).
:::
<Details header="Import a CSV file">
1. Drag and drop a CSV file containing URL redirects or select **browse** and select a CSV file. For more information on the file format, refer to [CSV file format for Bulk Redirects](/rules/url-forwarding/bulk-redirects/reference/csv-file-format/).
2. The dashboard will display the URL redirects that were successfully imported from the file. You can manually adjust the displayed records or add/remove URL redirects before proceeding.
3. Select **Next**.
</Details>
<Details header="Add URL redirects manually">
1. Select **Or, manually add URL redirects**.
2. Enter the URL redirects you wish to add to the list. You must enter at least the following three fields: **Source URL**, **Target URL**, and **Status**. To set additional options, expand **Edit parameters**.
3. Add more URL redirects, if required.
4. Select **Next**.
</Details>
5. Review and edit the URL redirects you imported or created, and select **Next**.
6. Select **Continue to Redirect Rules** to go to the rule creation page, and follow the instructions in the next section. You must create a Bulk Redirect Rule to enable the URL redirects you defined.
:::note[Notes]
:::
2. Create a Bulk Redirect Rule
-
(Optional) If you are not using the Bulk Redirect List creation wizard according to the instructions in the previous section:
-
In the Cloudflare dashboard, go to the Bulk redirects page.
-
Select Create Bulk Redirect Rule.
-
-
In Rule name, enter a descriptive name for the rule.
-
Select the Bulk Redirect List you previously created.
-
(Optional) If necessary, select Or use the expression editor to edit the rule expression or the rule key.
-
To save and deploy the Bulk Redirect Rule, select Save and Deploy. If you are not ready to deploy your rule, select Save as Draft.
Bulk Redirects FAQ
import { GlossaryTooltip } from "~/components";
Below you will find answers to the most commonly asked questions regarding Bulk Redirects.
To troubleshoot errors related to Bulk Redirects:
- Refer to Troubleshooting Cloudflare 10XXX Errors for more information on runtime errors.
- Use Rules simulator to determine if a rule is triggering for a specific URL.
What happens if the same source URL appears in two different Bulk Redirect Lists?
In this situation, Cloudflare will use the URL redirect of the first rule that triggers. This will be determined by the order of the Bulk Redirect Rules enabling each Bulk Redirect List in the http_request_redirect phase entry point ruleset.
How can I solve the following error: "This account has reached the limit on the number of URL matching items on the same hostname/path"?
You may get this error when adding items to a Bulk Redirect List.
You can have any number of URL redirects with the same source hostname (with different paths) or same source path (with different hostnames). However, you can have a maximum of 16 source URLs with the same hostname and path across all lists, either enabled by a Bulk Redirect Rule or not.
If you receive this error, check if you have any unused Bulk Redirect Lists with the source hostname and path that caused the error, and remove such items from the list.
How many URL redirects can I have in a single Bulk Redirect List?
Each account has a maximum number of URL redirects across all lists which depends on your Cloudflare plan. If you wish, you can use all the URL redirects available in your plan in a single Bulk Redirect List, but you will not be able to create any other URL redirects in a different list. Refer to Availability for more information.
How can I redirect based on the non-normalized version of a URL?
Use the raw.http.request.full_uri field both in the rule expression and in the key, instead of the default field http.request.full_uri. This will take the raw version of the URL into account, that is, the URL received on the Cloudflare global network before applying normalization. Refer to Bulk Redirects concepts for more information on using a custom rule expression and a custom key.
Do Bulk Redirects take precedence over Page Rules?
Yes. Bulk Redirects take precedence over Page Rules redirects. For more information on the execution order of Rules products, refer to Execution order.
Can I purge an entire Bulk Redirect List in one API call?
If your Bulk Redirect List contains 500,001 or more items, you will not be able to purge the entire list in a single API call. Instead, you must make multiple calls to Delete List Items API end-point, deleting a maximum of 100,000 items per request.
For example, to delete a list with 1,000,000 items, you would need to issue at least 10 API requests.
How Bulk Redirects work
import { MetaInfo } from "~/components";
When a request reaches Cloudflare, Bulk Redirects are evaluated before the request is sent to your origin server. Cloudflare checks all URL redirects of each Bulk Redirect List that is enabled by a Bulk Redirect Rule.
If there is a match for a URL redirect according to the URL matching algorithm, the redirect action is performed immediately according to the URL redirect configuration parameters. Cloudflare performs no further processing once a redirect action has been executed.
Matching the source URL of redirects
The following URL redirect parameters control the matching behavior between the request URL and source URLs of the configured (and enabled) URL redirects:
-
Subpath matching
-
When
true, the URL redirect applies not only to the exact source path, but also to all paths under it. For example, consider the following source and target URLs of a URL redirect:- Source URL:
https://example.com/foo/ - Target URL:
https://example.com/qux/
- Source URL:
-
With this configuration and Subpath matching enabled, an incoming request to
example.com/foo/barwill be redirected tohttps://example.com/qux/bar.
:::note URL redirects with Subpath matching enabled cannot contain more than 16
/(slash) characters in the source URL path. ::: -
-
Include subdomains
-
When
true, the URL redirect matches not only the exact hostname in the source URL, but also any of its subdomains. For example, consider the following source and target URLs of a URL redirect:- Source URL:
https://example.com/about - Target URL:
https://example.com/newpage
- Source URL:
-
With this configuration and Includes subdomains enabled, incoming requests to
http://a.example.com/aboutandhttp://a.b.example.com/aboutwould also match, in addition to the specified domain with no subdomain (https://example.com/about).
-
For detailed information on these parameters, refer to URL redirect parameters.
Configuring the path and query string behavior
The following parameters configure how Cloudflare determines the path and query string of the final target URL:
-
Preserve query string
-
When
true, the final target URL keeps the query string of the original request. For example, consider the following source and target URLs of a URL redirect:- Source URL:
https://example.com/about - Target URL:
https://example.com/newpage
- Source URL:
-
With this configuration and Preserve query string enabled, an incoming request to
http://example.com/about?q=termwould be redirected tohttps://example.com/newpage?q=term. If Preserve query string is disabled, the same incoming request would be redirected tohttps://example.com/newpage.
-
-
Preserve path suffix
-
When
true, the final target URL includes the remaining path segments (the parts of the request path that did not match the URL redirect's source URL). -
When Subpath matching is enabled, the path that was not matched is copied over to the final target URL. For example, consider the following source and target URLs of a URL redirect:
- Source URL:
https://example.com/a/ - Target URL:
https://example.com/b/
- Source URL:
-
An incoming request to
https://example.com/a/foowill be redirected tohttps://example.com/b/foo. -
If you set Preserve path suffix to
false, the same request will still match the redirect, but it will be redirected tohttps://example.com/b/.
-
For detailed information on these parameters, refer to URL redirect parameters.
URL matching algorithm
The URL of an incoming request matches a URL redirect in a list if:
-
The scheme (
httporhttps) is the same as the source URL of the URL redirect definition. Source URLs with no scheme will match bothhttpandhttps. -
The hostname is the same as the hostname in the source URL of the URL redirect definition. If Include subdomains is enabled, the subdomains of the hostname in the redirect definition will also match.
-
The path is the same as the source URL. If Subpath matching is enabled, Cloudflare also considers the subpaths of the path in the URL redirect's source URL when determining if there is a match. For example, a URL redirect with its source URL defined as
example.com/blogwill also match requests toexample.com/blog/fooandexample.com/blog/bar.
Determining the URL redirect to apply
If multiple URL redirects can apply, then the redirect that wins is determined by the following rules:
-
Given two URL redirects with Subpath matching enabled, the URL redirect with the most specific path wins over the other URL redirect.
If there are two URL redirects with source URL paths/folderand/folder/subfolder, an incoming request for the/folder/subfolder/itemURL path will match the second redirect (/folder/subfolder) because it is more specific. -
URL redirects with the exact hostname win over URL redirects with the Include subdomains option enabled.
-
Given two URL redirects with Include subdomains enabled, the URL with the most specific domain wins over the other URL redirect.
If there are two URL redirects with source URL hostnamesbar.comandfoo.bar.com, an incoming request toqux.foo.bar.comwill match the second redirect (foo.bar.com) because it is more specific. -
URL redirects with a specific scheme (either
httporhttps) win over URL redirects that match both schemes.
Bulk Redirects
import { Render } from "~/components";
Bulk Redirects allow you to define a large number of URL redirects at the account level, which can apply across domains in your account. These redirects navigate the user from a source URL to a target URL using a given HTTP status code. URL redirection is also known as URL forwarding.
Unlike dynamic URL redirects created in Single Redirects, Bulk Redirects are essentially static. They do not support string replacement operations or regular expressions. However, you can configure URL redirect parameters that affect how source URLs are matched and how the redirect is performed.
<Render file="snippets-alternative" product="rules" params={{ suffix: "and customized redirect logic" }} />
Bulk Redirects and the WAF
Bulk Redirects run after the WAF in the request processing pipeline. This means that:
- If a WAF custom rule or rate limiting rule blocks a request, the Bulk Redirect will not execute.
- If a WAF rule logs or challenges a request that subsequently passes, the firewall event will still appear in Security Events and Logpush — even though the request is later redirected. This is expected behavior.
For the complete request processing order, refer to Rules execution order.
Related resources
- Availability: Information on the Bulk Redirects quotas and features per Cloudflare plan.
- Execution order: Execution order of the different Rules products.
- Simulate a request: Use Rules simulator to determine if a bulk redirect rule is triggering for a specific URL.
Configure Bulk Redirects using Terraform
import { Render, Tabs, TabItem } from "~/components";
This Terraform example configures account-level Bulk Redirects. It creates a Bulk Redirect List populated with URL redirects and a corresponding Bulk Redirect Rule to activate them.
# Cloudflare account ID
variable "cloudflare_account_id" {
default = "<ACCOUNT_ID>"
}
# Bulk redirect list description
variable "bulk_redirect_list_description" {
default = "my bulk redirect description"
}
# Bulk redirect list name
variable "bulk_redirect_list_name" {
default = "my_bulk_redirect_list_name"
}
# Bulk redirect list item (URL redirect)
variable "bulk_redirects" {
type = map(object({
source_url = string
target_url = string
status_code = number
}))
default = {
"redirect1" = {
source_url = "https://source.url/redirect/1"
target_url = "https://target.url/?redirect=1"
status_code = 301
}
"redirect2" = {
source_url = "https://source.url/redirect/2"
target_url = "https://target.url/?redirect=2"
status_code = 302
}
"redirect3" = {
source_url = "https://source.url/redirect/3"
target_url = "https://target.url/?redirect=3"
status_code = 307
}
}
}
# Create redirect list
resource "cloudflare_list" "bulk_redirect_to_id" {
account_id = var.cloudflare_account_id
name = var.bulk_redirect_list_name
description = var.bulk_redirect_list_description
kind = "redirect"
}
# Add redirect item into the redirect list
resource "cloudflare_list_item" "bulk_redirect_to_id_item" {
for_each = { for redirect in var.bulk_redirects : "${redirect.source_url}" => redirect }
account_id = var.cloudflare_account_id
list_id = cloudflare_list.bulk_redirect_to_id.id
redirect {
source_url = each.value.source_url
target_url = each.value.target_url
status_code = each.value.status_code
}
depends_on = [
cloudflare_list.bulk_redirect_to_id
]
}
# Create bulk redirect and attach redirect list
resource "cloudflare_ruleset" "bulk_root_redirect_to_id" {
account_id = var.cloudflare_account_id
name = var.bulk_redirect_list_name
description = var.bulk_redirect_list_description
kind = "root"
phase = "http_request_redirect"
rules {
action = "redirect"
action_parameters {
from_list {
name = var.bulk_redirect_list_name
key = "http.request.full_uri"
}
}
expression = "http.request.full_uri in ${"$"}${var.bulk_redirect_list_name}"
description = var.bulk_redirect_list_description
enabled = true
}
depends_on = [
cloudflare_list_item.bulk_redirect_to_id_item
]
}
Required token permissions
Your API token must have at least the following permissions:
- Account Filter Lists > Edit
- Bulk URL Redirects > Edit
- Account Rule Lists Write
- Bulk URL Redirects Write
CSV file format for Bulk Redirects
You can use a CSV file to import URL redirects into a Bulk Redirect List using the Cloudflare dashboard. Each line in the CSV file must follow this format:
<SOURCE_URL>,<TARGET_URL>[,<STATUS_CODE>,<PRESERVE_QUERY_STRING>,<INCLUDE_SUBDOMAINS>,<SUBPATH_MATCHING>,<PRESERVE_PATH_SUFFIX>]
Only the <SOURCE_URL> and <TARGET_URL> values are mandatory. The default value of <STATUS_CODE> is 301 and the default value for all the boolean parameters is FALSE.
To enable one of the URL redirect parameters, use one of the following values: TRUE or true. To keep an option disabled, use one of FALSE or false, or enter a comma (delimiter) without entering any value.
Example CSV file
All the lines in this example are valid lines that you can import in the dashboard:
example.com/contacts,https://example.net/contact-us,301,,,,
example.com/about,https://example.net/about-us,,FALSE,TRUE,,
example.com/docs,https://example.com/draft-docs,302,,TRUE
Important remarks
- The source URL cannot include a query string. For details on which URL components are supported in source URLs, refer to Supported URL components in Bulk Redirects.
- The CSV file must not include a header row with column names.
- A source/target URL must be enclosed in quotes (
") when it includes a comma (,). You can always enclose URL values in quotes, but it is not required. - You can skip an optional value by immediately entering a comma (the delimiter) without entering any value.
- You do not need to include trailing commas.
Available fields and functions
The available fields when defining a Bulk Redirect Rule filter expression are the following:
cf.edge.server_ipcf.edge.server_portcf.edge.client_portcf.edge.client_tcpcf.edge.l4.delivery_ratecf.hostname.metadatacf.zone.namecf.random_seedcf.ray_idcf.timings.client_quic_rtt_mseccf.timings.client_tcp_rtt_mseccf.tls_versioncf.tls_ciphercf.tls_client_hello_lengthcf.tls_client_randomcf.tls_client_extensions_sha1cf.tls_client_extensions_sha1_lecf.tls_client_ciphers_sha1cf.tls_client_auth.*cf.worker.upstream_zonehttp.cookiehttp.hosthttp.refererhttp.request.accepted_languageshttp.request.cookieshttp.request.headershttp.request.headers.*http.request.methodhttp.request.timestamp.sechttp.request.timestamp.msechttp.request.full_urihttp.request.urihttp.request.uri.*http.request.versionraw.http.request.full_uriraw.http.request.headersraw.http.request.headers.*raw.http.request.uriraw.http.request.uri.*http.user_agenthttp.x_forwarded_forip.srcip.src.latip.src.lonip.src.asnumip.src.cityip.src.countryip.src.continentip.src.metro_codeip.src.postal_codeip.src.regionip.src.region_codeip.src.is_in_european_unionip.src.subdivision_1_iso_codeip.src.subdivision_2_iso_codesslcf.sequence.current_opcf.sequence.msec_since_opcf.sequence.previous_ops
Refer to Fields for reference information on these fields.
:::caution[Important]
-
To obtain the value of an HTTP request header using the
http.request.headersfield, specify the header name in lowercase. For example, to get the first value of theAccept-Encodingrequest header in an expression, use:http.request.headers["accept-encoding"][0]. -
Use the
to_string()function to get the string representation of a non-string value like an Integer value.
:::
For information on the available functions, refer to Functions.
Reference
import { DirectoryListing } from "~/components";
Refer to the following pages for more information about Bulk Redirects:
Bulk Redirects API JSON objects
A fully populated Bulk Redirect Rule object has the following JSON structure:
{
"action": "redirect",
"expression": "http.request.full_uri in $<LIST_NAME>",
"action_parameters": {
"from_list": {
"name": "<LIST_NAME>",
"key": "http.request.full_uri"
}
}
}
The JSON object properties must comply with the following:
-
actionmust beredirect -
action_parametersmust contain afrom_listobject with additional settings. -
from_listmust contain the following properties:name: The name of an existing Bulk Redirect List to associate with the current Bulk Redirect Rule.key: An expression that defines the value that will be matched against the configured URL redirect's source URL values, following the rules of the URL matching algorithm. Refer to Bulk Redirects concepts for more information.
-
expressionmust reference the request field used in thekeyproperty. Refer to Bulk Redirects concepts for more information.
URL redirect list item
A fully populated URL redirect list item object has the following JSON structure:
{
"id": "7c5dae5552338874e5053f2534d2767a",
"redirect": {
"source_url": "https://example.com/blog",
"target_url": "https://example.com/blog/latest",
"status_code": 301,
"include_subdomains": false,
"subpath_matching": false,
"preserve_query_string": false,
"preserve_path_suffix": true
},
"created_on": "2021-10-11T12:39:02Z",
"modified_on": "2021-10-11T12:39:02Z"
}
For details on the redirect object properties, refer to URL redirect parameters.
URL redirect parameters
import { Type, MetaInfo, Render } from "~/components";
A URL redirect has a source URL, a target URL, a status code, and some additional parameters that affect its URL matching behavior and runtime behavior.
Source URL
API field: source_url
The URL string that the incoming request URL must match for the redirect to be applied. This property is mandatory. The maximum length of the source URL is 32 KB.
The value must be a valid URL, but the URL scheme is not required (for example, https); when the scheme is omitted, the redirect applies to both http and https URL schemes.
A Bulk Redirect List cannot contain several URL redirects with the exact same source URL. The exact behavior of the URL matching algorithm, which matches an incoming request with the redirect's source URL, depends on the values of the Include subdomains and Subpath matching parameters.
For more information on the supported URL components, refer to Supported URL components in Bulk Redirects.
Target URL
API field: target_url
The URL where the client will be redirected to when there is a match for the URL redirect. This property is mandatory. The maximum length of the target URL is 32 KB.
The value must be a valid URL. The final target URL depends on the values of the Preserve query string and Preserve path suffix parameters.
For more information on the supported URL components, refer to Supported URL components in Bulk Redirects.
Subpath matching
API field: subpath_matching
If true, the current redirect will apply the subpath matching algorithm to the request URL when determining if there is a match for the current URL redirect.
For example, a URL redirect from /my-folder/ to /other-folder/ with Subpath matching enabled will also redirect a request from /my-folder/item to /other-folder/item. However, the redirect will only include the item part when Preserve path suffix is true.
For more information, refer to Matching the source URL of redirects.
Include subdomains
API field: include_subdomains
If true, the source URL hostname will also apply to any subdomains — the redirect will match for all subdomains to the left of the domain portion of the source URL, as well as the specified domain.
For example, a redirect with source URL defined as http://example.com/about will also apply to requests with source URL http://a.example.com/about or http://a.b.example.com/about.
For more information, refer to Matching the source URL of redirects.
Preserve query string
API field: preserve_query_string
If true, the redirect URL will keep the query string of the original request.
For example, a URL redirect from /my-folder/ to /other-folder/ with Preserve query string enabled will redirect a request from /my-folder/?name=value to /other-folder/?name=value. If Preserve query string is disabled, the request will be redirected from /my-folder/?name=value to /other-folder/.
:::caution When Preserve query string is enabled, the final redirect URL uses the original request's query string. Any query string on the target URL is discarded — even when the original request has no query string of its own. To add a fixed query parameter to redirected URLs, leave Preserve query string disabled and include the parameter directly in the target URL. :::
Preserve path suffix
API field: preserve_path_suffix
Applicable only when Subpath matching is enabled. If true, defines that the redirect URL will include the remaining (non-matched) path elements of the source URL, if any.
For example, when both Subpath matching and Preserve path suffix are enabled, a URL redirect from /my-folder/ to /another-folder/ will redirect an incoming request from /my-folder/foo to /another-folder/foo. If Preserve path suffix is disabled, the same request would still match the URL redirect, but it would redirect from /my-folder/foo to /another-folder/.
Status code
API field: status_code
API values: 301, 302, 307, or 308.
The HTTP status code returned to the client when redirecting:
Supported URL components in Bulk Redirects
The source and target URLs of a URL redirect support different URL components.
The provided URL component examples in the reference table are based on the following URL:
https://user:password@www.example.com:443/search?q=term#results
| URL component | Supported in source URL [^1] | Supported in target URL |
|---|---|---|
| Scheme For example: https |
Yes, http or https only(optional) |
Yes |
| User information For example: user:password |
No | Yes (optional) |
| Host For example: www.example.com |
Yes | Yes (optional) |
| Port For example: 443 |
No | Yes (optional) |
| Path For example: /search |
Yes | Yes |
| Query string For example: q=term |
No | Yes, if Preserve query string is false (optional)You can only add a query string to the target URL if you do not keep the original query string (that is, if Preserve query string is false). If you set Preserve query string to true, the query string of the request will be passed along when there is a match for the source URL. |
| Fragment For example: results |
No | Yes (optional) |
[^1]: Supported in source URL = No means that you cannot include the component in the source URL to match against the URL of incoming requests.
Bulk Redirects also support target URLs without an authority component [^2], like the following URL:
magnet:?xt=urn:btih:2bd9d334e8d1e5bd7768755173222db5c6dea13b&dn=archlinux-2021.07.01-x86_64.iso
Redirect examples
import { ResourcesBySelector } from "~/components";
<ResourcesBySelector directory="rules/url-forwarding/examples/" types={["example"]} />
Perform mobile redirects
import { Example } from "~/components";
The following examples will redirect visitors using mobile devices — based on the request user agent string — to a different hostname.
Redirect mobile users dropping the original URI path
This example static redirect will redirect requests for the current zone (example.com) from mobile users to m.example.com without preserving the URI path in the original HTTP request.
When incoming requests match
- Enter the following expression in the Expression Editor:
not http.host in {"m.example.com"} and (http.user_agent contains "mobi" or http.user_agent contains "Mobi")
Then
- Type: Static
- URL:
m.example.com - Status code: 301
Notes about this example:
- The
not http.host in {"m.example.com"}condition prevents redirect loops. - The user agent condition follows Mozilla's recommendation for identifying mobile devices.
- The Then > URL value should be the same as the one you entered in the
http.hostcondition of the rule's filter expression. - You can redirect users to other zones on Cloudflare or to other hostnames not on Cloudflare.
Redirect mobile users keeping the original path
This example single redirect will redirect requests for the current zone (example.com) from mobile users to m.example.com, keeping the URI path of the original HTTP request.
When incoming requests match
- Enter the following expression in the Expression Editor:
not http.host in {"m.example.com"} and (http.user_agent contains "mobi" or http.user_agent contains "Mobi")
Then
- Type: Dynamic
- Expression:
concat("https://m.example.com", http.request.uri.path) - Status code: 301
Notes about this example:
- The
not http.host in {"m.example.com"}condition prevents redirect loops. - The user agent condition follows Mozilla's recommendation for identifying mobile devices.
- The hostname in Then > Expression should be the same as the one you entered in the
http.hostcondition of the rule's filter expression. - Depending on your use case, you may want to enable Then > Preserve query string to also keep the query string of the original request.
- You can redirect users to other zones on Cloudflare or to other hostnames not on Cloudflare.
Redirect admin area requests to HTTPS
import { Example } from "~/components";
This example single redirect for zone example.com will redirect requests for the administration area of a specific subdomain (store.example.com) to HTTPS, keeping the original path and query string.
When incoming requests match
- Wildcard pattern
- Request URL:
http://store.example.com/admin*
- Request URL:
Then
- Target URL:
https://store.example.com/admin${1} - Status code: 301
- Preserve query string: Enabled
For example, the redirect rule would perform the following redirects:
| Request URL | Target URL | Status code |
|---|---|---|
http://store.example.com/admin/products/ |
https://store.example.com/admin/products/ |
301 |
https://store.example.com/admin/products/ |
(unchanged) | n/a |
http://store.example.com/admin/?logged_out=true |
https://store.example.com/admin/?logged_out=true |
301 |
http://store.example.com/?all_items=true |
(unchanged) | n/a |
http://example.com/admin/ |
(unchanged) | n/a |
Redirect requests from one domain to another
import { Example } from "~/components";
In this example the original domain was replaced with a different domain. All functionality was maintained, except for the HTTP service (port 80) which was discontinued.
Create a redirect rule with the following configuration:
When incoming requests match
- Wildcard pattern
- Request URL:
http*://example.com/*
- Request URL:
Then
- Target URL:
https://example.net/${2} - Status code: 301
- Preserve query string: Enabled
This configuration will perform the following redirects:
| Request URL | URL after redirect | Status code |
|---|---|---|
http://example.com/ |
https://example.net/ |
301 |
https://example.com/ |
https://example.net/ |
301 |
https://example.com/my/path/to/page.htm |
https://example.net/my/path/to/page.htm |
301 |
https://example.com/search?q=term |
https://example.net/search?q=term |
301 |
Redirect requests from one country to a domain
import { Steps } from "~/components";
In this example, all website visitors from the United Kingdom will be redirected to a different domain, but maintaining current functionality in the same paths.
-
Create a Bulk Redirect List named
uk_redirect_listwith the following URL redirect:- Source URL:
https://example.com/ - Target URL:
https://example.co.uk/ - Subpath matching: Enabled
- Preserve query string: Enabled
- Source URL:
-
Create a Bulk Redirect Rule that enables the previous Bulk Redirect List and set the rule expression to the following:
ip.src.country == "GB" and http.request.full_uri in $uk_redirect_list
This configuration will perform the following redirects for UK visitors:
| Request URL | URL after redirect |
|---|---|
https://example.com/ |
https://example.co.uk/ |
https://example.com/my/path/to/page.htm |
https://example.co.uk/my/path/to/page.htm |
https://example.com/search?q=term |
https://example.co.uk/search?q=term |
Redirect requests for a domain to a new domain
import { Example } from "~/components";
In this example, an old website was discontinued and replaced by a new one in a different domain. The functionality is different, and all URLs should now point to the root of the new domain. The same applies to any subdomains of the old domain.
Create a redirect rule with the following configuration:
When incoming requests match
- Wildcard pattern
- Request URL:
http*://*example.com/*
- Request URL:
Then
- Target URL:
https://example.net/ - Status code: 301
For example, the redirect rule would perform the following redirects:
| Request URL | Target URL | Status code |
|---|---|---|
http://example.com/ |
https://example.net/ |
301 |
https://example.com/ |
https://example.net/ |
301 |
https://subdomain.example.com/ |
https://example.net/ |
301 |
https://example.com/my/path/to/page.htm |
https://example.net/ |
301 |
https://example.com/search?q=term |
https://example.net/ |
301 |
Redirect requests to a different hostname
import { Example } from "~/components";
This example single redirect will redirect all requests for smallshop.example.com to a different hostname globalstore.example.net using HTTPS, keeping the original path and query string.
When incoming requests match
- Wildcard pattern
- Request URL:
http*://smallshop.example.com/*
- Request URL:
Then
- Target URL:
https://globalstore.example.net/${2} - Status code: 301
- Preserve query string: Enabled
For example, the redirect rule would perform the following redirects:
| Request URL | Target URL | Status code |
|---|---|---|
http://smallshop.example.com/ |
https://globalstore.example.net/ |
301 |
http://smallshop.example.com/admin/?logged_out=true |
https://globalstore.example.net/admin/?logged_out=true |
301 |
https://smallshop.example.com/?all_items=1 |
https://globalstore.example.net/?all_items=1 |
301 |
http://example.com/about/ |
(unchanged) | n/a |
Redirect local visitors to specific subdomains
import { Example } from "~/components";
This example single redirect for zone example.com will redirect United Kingdom and France visitors requesting the website's root path (/) to their localized subdomains https://gb.example.com and https://fr.example.com, respectively.
When incoming requests match
Using the Expression Editor:
(ip.src.country eq "GB" or ip.src.country eq "FR") and http.request.uri.path eq "/"
Then
- Type: Dynamic
- Expression:
lower(concat("https://", ip.src.country, ".example.com")) - Status code: 301
For example, the redirect rule would perform the following redirects:
| Visitor country | Request URL | Target URL | Status code |
|---|---|---|---|
| United Kingdom | example.com |
https://gb.example.com |
301 |
| France | example.com |
https://fr.example.com |
301 |
| United States | example.com |
(unchanged) | n/a |
Redirect visitors to a new page URL
import { Example } from "~/components";
This example static redirect for zone example.com will redirect visitors requesting the /contact-us/ page to the new page URL /contacts/.
When incoming requests match
- Field: URI Path
- Operator: equals
- Value:
/contact-us/
If you are using the Expression Editor, enter the following expression:
http.request.uri.path eq "/contact-us/"
Then
- Type: Static
- URL:
/contacts/ - Status code: 301
- Preserve query string: Enabled
For example, the redirect rule would perform the following redirects:
| Request URL | Target URL | Status code |
|---|---|---|
example.com/contact-us/ |
example.com/contacts/ |
301 |
example.com/contact-us/?state=TX |
example.com/contacts/?state=TX |
301 |
example.com/team/ |
(unchanged) | n/a |
Redirect from root to WWW
import { Example } from "~/components";
This example creates a redirect rule that forwards HTTPS requests from the root domain (example.com) to the WWW subdomain (www.example.com), while retaining the original path and query string.
When incoming requests match
- Wildcard pattern
- Request URL:
https://example.com/*
- Request URL:
Then
- Target URL:
https://www.example.com/${1} - Status code: 301
- Preserve query string: Enabled
This rule ensures that only HTTPS requests from the root domain are redirected to the WWW subdomain, leaving other requests (such as HTTP or requests to other subdomains) unchanged.
For example, the redirect rule would perform the following redirects:
| Request URL | Target URL | Status code |
|---|---|---|
https://example.com/products/ |
https://www.example.com/products/ |
301 |
https://store.example.com/products/ |
(unchanged) | n/a |
https://example.com/admin/?logged_out=true |
https://www.example.com/admin/?logged_out=true |
301 |
http://example.com/?all_items=true |
(unchanged) | n/a |
http://www.example.com/admin/ |
(unchanged) | n/a |
Make sure to replace example.com with your actual hostname before deploying your rule.
Redirect from WWW to root
import { Example } from "~/components";
This example creates a redirect rule that forwards HTTPS requests from the WWW subdomain (www.example.com) to the root domain (example.com), while retaining the original path and query string.
When incoming requests match
- Wildcard pattern
- Request URL:
https://www.*
- Request URL:
Then
- Target URL:
https://${1} - Status code: 301
- Preserve query string: Enabled
This rule ensures that only HTTPS requests from www. subdomains are redirected to the root domain, leaving other requests (such as HTTP or non-WWW) unchanged.
For example, the redirect rule would perform the following redirects:
| Request URL | Target URL | Status code |
|---|---|---|
https://www.example.com/products/ |
https://example.com/products/ |
301 |
https://www.store.example.com/products/ |
https://store.example.com/products/ |
301 |
https://store.example.com/products/ |
(unchanged) | n/a |
https://www.example.com/admin/?logged_out=true |
https://example.com/admin/?logged_out=true |
301 |
http://www.example.com/?all_items=true |
(unchanged) | n/a |
http://example.com/admin/ |
(unchanged) | n/a |
Remove locale from URL path
import { Example } from "~/components";
This example single redirect for zone example.com will redirect visitors from an old URL format that included the locale (for example, /en-us/<page_name>) to the new format /<page_name>.
When incoming requests match
- Field: URI Path
- Operator: matches regex
- Value:
^/[A-Za-z]{2}-[A-Za-z]{2}/
If you are using the Expression Editor, enter the following expression:
http.request.uri.path matches "^/[A-Za-z]{2}-[A-Za-z]{2}/"
Then
- Type: Dynamic
- Expression:
regex_replace(http.request.uri.path, "^/[A-Za-z]{2}-[A-Za-z]{2}/(.*)", "/${1}") - Status code: 301
- Preserve query string: Enabled
The function regex_replace() allows you to extract parts of the URL using regular expressions' capture groups. Create capture groups by putting part of the regular expression in parentheses. Then, reference a capture group using ${<num>} in the replacement string, where <num> is the number of the capture group.
For example, the redirect rule would perform the following redirects:
| Request URL | Target URL | Status code |
|---|---|---|
example.com/en-us/meet-our-team |
example.com/meet-our-team |
301 |
example.com/pt-BR/meet-our-team |
example.com/meet-our-team |
301 |
example.com/en-us/calendar?view=month |
example.com/calendar?view=month |
301 |
example.com/meet-our-team |
(unchanged) | n/a |
example.com/robots.txt |
(unchanged) | n/a |
Create a redirect rule via API
import { Details, Render, APIRequest } from "~/components";
Use the Rulesets API to create a redirect rule via API.
Add redirect rules to the entry point ruleset of the http_request_dynamic_redirect phase at the zone level. Refer to the Rulesets API documentation for more information on creating a ruleset and supplying a list of rules for the ruleset.
<Render file="url-forwarding/requires-proxied-site" product="rules" params={{ forwardingRuleName: "Single Redirects" }} />
Basic rule settings
A redirect rule must have:
actionset toredirect- An
action_parametersobject with additional configuration settings — refer to Single Redirects settings for details.
Example requests
The following request of the Create a zone ruleset operation creates a phase entry point ruleset for the http_request_dynamic_redirect phase at the zone level, and defines a single redirect rule with a dynamic URL redirect. Use this operation if you have not created a phase entry point ruleset for the http_request_dynamic_redirect phase yet.
<APIRequest path="/zones/{zone_id}/rulesets" method="POST" json={{ name: "Redirect rules ruleset", kind: "zone", phase: "http_request_dynamic_redirect", rules: [ { ref: "redirect_gb_fr_to_localized", expression: '(ip.src.country eq "GB" or ip.src.country eq "FR") and http.request.uri.path eq "/"', description: "Redirect GB and FR users in home page to localized site.", action: "redirect", action_parameters: { from_value: { target_url: { expression: 'lower(concat("https://", ip.src.country, ".example.com"))', }, status_code: 307, preserve_query_string: true, }, }, }, ], }} />
{
"result": {
"id": "528f4f03bf0da53a29907199625867be",
"name": "Redirect rules ruleset",
"kind": "zone",
"version": "1",
"rules": [
{
"ref": "redirect_gb_fr_to_localized",
"id": "235e557b92fd4e5e8753ee665a9ddd75",
"version": "1",
"expression": "(ip.src.country eq \"GB\" or ip.src.country eq \"FR\") and http.request.uri.path eq \"/\"",
"description": "Redirect GB and FR users in home page to localized site.",
"action": "redirect",
"action_parameters": {
"from_value": {
"target_url": {
"expression": "lower(concat(\"https://\", ip.src.country, \".example.com\"))"
},
"status_code": 307,
"preserve_query_string": true
}
},
"last_updated": "2022-09-28T09:20:42Z"
}
],
"last_updated": "2022-09-28T09:20:42Z",
"phase": "http_request_dynamic_redirect"
},
"success": true,
"errors": [],
"messages": []
}
If there is already a phase entry point ruleset for the http_request_dynamic_redirect phase, use the Update a zone ruleset operation instead, like in the following example:
<APIRequest path="/zones/{zone_id}/rulesets/{ruleset_id}" method="PUT" json={{ name: "Redirect rules ruleset", kind: "zone", phase: "http_request_dynamic_redirect", rules: [ { ref: "redirect_gb_fr_to_localized", expression: '(ip.src.country eq "GB" or ip.src.country eq "FR") and http.request.uri.path eq "/"', description: "Redirect GB and FR users in home page to localized site.", action: "redirect", action_parameters: { from_value: { target_url: { expression: 'lower(concat("https://", ip.src.country, ".example.com"))', }, status_code: 307, preserve_query_string: true, }, }, }, { ref: "redirect_contacts_to_new_page", expression: 'http.request.uri.path eq "/contacts.html"', description: "Redirect to new contacts page.", action: "redirect", action_parameters: { from_value: { target_url: { value: "https://example.com/contact-us/", }, status_code: 308, }, }, }, ], }} />
{
"result": {
"id": "528f4f03bf0da53a29907199625867be",
"name": "Redirect rules ruleset",
"description": "",
"kind": "zone",
"version": "2",
"rules": [
{
"ref": "redirect_gb_fr_to_localized",
"id": "235e557b92fd4e5e8753ee665a9ddd75",
"version": "1",
"action": "redirect",
"action_parameters": {
"from_value": {
"target_url": {
"expression": "lower(concat(\"https://\", ip.src.country, \".example.com\"))"
},
"status_code": 307,
"preserve_query_string": true
}
},
"expression": "(ip.src.country eq \"GB\" or ip.src.country eq \"FR\") and http.request.uri.path eq \"/\"",
"description": "Redirect GB and FR users in home page to localized site.",
"last_updated": "2022-10-03T15:38:51.658387Z",
"ref": "235e557b92fd4e5e8753ee665a9ddd75",
"enabled": true
},
{
"ref": "redirect_contacts_to_new_page",
"id": "cfad5efbfcd1440fb5b30cf30f95ece3",
"version": "1",
"action": "redirect",
"action_parameters": {
"from_value": {
"target_url": {
"value": "https://example.com/contact-us/"
},
"status_code": 308
}
},
"expression": "http.request.uri.path eq \"/contacts.html\"",
"description": "Redirect to new contacts page.",
"last_updated": "2022-10-03T15:38:51.658387Z",
"ref": "cfad5efbfcd1440fb5b30cf30f95ece3",
"enabled": true
}
],
"last_updated": "2022-10-03T15:38:51.658387Z",
"phase": "http_request_dynamic_redirect"
},
"success": true,
"errors": [],
"messages": []
}
Required API token permissions
The API token used in API requests to manage redirect rules must have at least the following permission:
- Zone > Single Redirect > Edit
Create a redirect rule in the dashboard
import { Render, Steps, DashButton } from "~/components";
-
In the Cloudflare dashboard, go to the Rules Overview page.
-
Select Create rule > Redirect Rule.
-
(Optional) Select one of the rule templates that address common use cases. Then, review and adjust the proposed rule configuration.
-
Enter a descriptive name for the rule in Rule name.
-
Under When incoming requests match, select one of the following options:
- Wildcard pattern: The rule will only apply to traffic matching the wildcard pattern.
- Request URL: Enter the wildcard pattern using the asterisk (
*) character to match multiple requests. For example,http*://*.example.com/files/*. - Then: Define the URL redirect settings including:
- Target URL: Enter the target URL, which can be static (for example,
https://example.com) or dynamic (for example,https://example.com/${1}/files/${2}). Use wildcard replacement such as${1}and${2}to define dynamic target URLs. - Status code: Select the status code for the redirect (for example,
301). - Preserve query string: Choose whether to keep the query string from the original request.
- Target URL: Enter the target URL, which can be static (for example,
- Request URL: Enter the wildcard pattern using the asterisk (
- All incoming requests: The rule will apply to all traffic.
- Then: Define the URL redirect settings for all incoming requests.
- Custom filter expression: The rule will only apply to traffic matching a custom expression. Define the rule expression to configure which requests should be redirected.
- Then: Define the URL redirect settings for requests matching the custom rule expression.
- Wildcard pattern: The rule will only apply to traffic matching the wildcard pattern.
-
To save and deploy your rule, select Deploy. If you are not ready to deploy your rule, select Save as Draft.
<Render file="url-forwarding/requires-proxied-site" product="rules" params={{ forwardingRuleName: "Single Redirects" }} />
Single Redirects
import { GlossaryTooltip, Render } from "~/components";
Single Redirects allow you to create static or dynamic URL redirects. Static redirects send users to a fixed target URL, while dynamic redirects build the target URL from components of the original request. A wildcard-based interface allows you to define source and target URL patterns without complex functions or regular expressions, efficiently handling thousands of URLs with a single rule. Dynamic URL redirects also support advanced features such as string replacement operations and regular expressions (depending on your Cloudflare plan).
<Render file="snippets-alternative" product="rules" params={{ suffix: "and customized redirect logic" }} />
Related resources
- Availability: Information on the Single Redirects quotas and features per Cloudflare plan.
- Execution order: Execution order of the different Rules products.
- Simulate a request: Use Rules simulator to determine if a redirect rule is triggering for a specific URL.
Single Redirects settings
import { Details, Render } from "~/components";
The following sections describe the settings of redirect rules to configure static and dynamic URL redirects.
Wildcard URL Redirect
Performs a URL redirect using wildcard patterns to match multiple requests. This method simplifies defining source and target URL patterns without needing complex expressions.
A wildcard URL redirect has the following configuration parameters:
-
Request URL: Enter the wildcard pattern using the asterisk (
*) character to match multiple requests. For example,https://*.example.com/files/*. -
Target URL: Enter the target URL, which can be static (for example,
https://example.com) or dynamic (for example,https://example.com/${1}/files/${2}). Use wildcard replacement like${1},${2}, etc., to define dynamic targets. -
Status code: The HTTP status code of the redirect response (301 - Permanent Redirect by default). Must be one of the following:
-
Preserve query string: Whether to preserve the query string when redirecting (disabled by default).
Wildcard URL redirects are regular dynamic URL redirects that use the wildcard_replace() function in the target_url.expression parameter.
The full syntax of the "action_parameters" field for a redirect rule performing a wildcard URL redirect is the following:
"action_parameters": {
"from_value": {
"target_url": {
"expression": "wildcard_replace(http.request.full_uri, r\"<REQUEST_URL_PATTERN>\", r\"<TARGET_URL_PATTERN>\")"
},
"status_code": <STATUS_CODE>,
"preserve_query_string": <BOOLEAN_VALUE>
}
}
The required parameters are <REQUEST_URL_PATTERN> and <TARGET_URL_PATTERN>.
Static URL redirect
Performs a static URL redirect with a given HTTP status code and optionally preserves the query string.
A static URL redirect has the following configuration parameters:
-
URL: A literal string that will be used in the
LocationHTTP header returned in the redirect response. -
Status code: The HTTP status code of the redirect response (301 - Permanent Redirect by default). Must be one of the following:
-
Preserve query string: Whether to preserve the query string when redirecting (disabled by default).
The full syntax of the "action_parameters" field for a redirect rule performing a static URL redirect is the following:
"action_parameters": {
"from_value": {
"target_url": {
"value": "<STATIC_URL_VALUE>"
},
"status_code": <STATUS_CODE>,
"preserve_query_string": <BOOLEAN_VALUE>
}
}
The only required parameter is <STATIC_URL_VALUE>.
Dynamic URL redirect
Performs a dynamic URL redirect, where the target URL is determined by an expression. You can configure the redirect HTTP status code and whether to preserve the query string when redirecting.
A dynamic URL redirect has the following configuration parameters:
-
Expression: An expression that defines the target URL of the redirect. The result of evaluating this expression will be used in the
LocationHTTP header returned in the redirect response. Refer to the fields and functions you can use in expressions. -
Status code: The HTTP status code of the redirect response (301 - Permanent Redirect by default). Must be one of the following:
-
Preserve query string: Whether to preserve the query string when redirecting (disabled by default).
The full syntax of the "action_parameters" field for a redirect rule performing a dynamic URL redirect is the following:
"action_parameters": {
"from_value": {
"target_url": {
"expression": "<DYNAMIC_URL_EXPRESSION>"
},
"status_code": <STATUS_CODE>,
"preserve_query_string": <BOOLEAN_VALUE>
}
}
The only required parameter is <DYNAMIC_URL_EXPRESSION>.
Create a redirect rule using Terraform
import { Render } from "~/components";
The following example defines a single redirect rule for a zone using Terraform. The rule creates a static URL redirect for visitors requesting the contacts page using an old URL.
# Single Redirects resource
resource "cloudflare_ruleset" "single_redirects_example" {
zone_id = "<ZONE_ID>"
name = "redirects"
description = "Redirects ruleset"
kind = "zone"
phase = "http_request_dynamic_redirect"
rules {
ref = "redirect_old_url"
description = "Redirect visitors still using old URL"
expression = "(http.request.uri.path matches \"^/contact-us/\")"
action = "redirect"
action_parameters {
from_value {
status_code = 301
target_url {
value = "/contacts/"
}
preserve_query_string = false
}
}
}
}





























































