Rules

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:

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.

Control incoming traffic by filtering requests to a zone. You can block or challenge incoming requests according to rules you define.

<RelatedProduct header="Rate limiting rules" href="/waf/rate-limiting-rules/" product="waf"

Define rate limits for requests matching an expression, and the action to
perform when those rate limits are reached.

<RelatedProduct header="Cache rules" href="/cache/how-to/cache-rules/" product="cache"

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:

:::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:

  1. In the Cloudflare dashboard, go to the Cloud Connector page.

  2. Select your cloud provider (Cloudflare R2 or an external provider).

  3. 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. :::

  4. Enter a descriptive name for the rule in Cloud Connector name.

  5. 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.

  6. 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:

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:

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:

:::note[Additional configurations you may need]

Cloud Connector will not apply any of the following configurations:

:::

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

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):

Cloud Connector supports both subdomain and URI path-style URLs:

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:

Get the bucket URL

  1. Go to the Amazon S3 console and select Buckets in the navigation pane.
  2. Select the bucket name.
  3. Go to the Properties tab.
  4. 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):

Cloud Connector supports both subdomain and URI path-style URLs:

Get the bucket URL

  1. Go to the Google Cloud console and select Buckets.

  2. Select the bucket name.

  3. 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.com format.
    To obtain the URI path bucket URL, remove https:// 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:

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

  1. Go to the Azure portal and select your storage account.
  2. In the menu pane, under Settings, select Endpoints.
  3. 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:

  1. In the Cloudflare dashboard, go to the Cloud Connector page.

  2. Select Amazon S3 as your cloud provider.

  3. 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.jpg will map to https://<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 named images, and files should be placed inside this folder. The URI path will then include the bucket name, like https://<YOUR_HOSTNAME>/<BUCKET_NAME>/images/file.jpg mapping to https://s3.amazonaws.com/<BUCKET_NAME>/images/file.jpg.
  4. (Optional) Use URL Rewrite Rules to adjust the URL structure. For example, you can create a URL rewrite that changes /images to /<BUCKET_NAME>/images to match the URI path-style URL structure.

  5. (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
  6. Select Next and enter a descriptive name like Route images to S3 in Cloud Connector name.

  7. 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.

  8. 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:

  1. In the Cloudflare dashboard, go to the Cloud Connector page.

  2. Select Google Cloud Storage as your cloud provider.

  3. 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.html will map to https://<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 want https://<YOUR_HOSTNAME>/eu/index.html to map to a file in your bucket, the file should be placed at https://storage.googleapis.com/<BUCKET_NAME>/eu/index.html.
  4. (Optional) Use URL Rewrite Rules to adjust the URL structure. For example, you can create a URL rewrite that changes /eu to /<BUCKET_NAME> to match the URI path-style URL structure.

  5. (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 /eu URI path for seven days (defined through the Edge TTL setting).

  6. Select Next and enter a descriptive name like Route EU visitors to GCP in Cloud Connector name.

  7. 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.

  8. 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:

  1. In the Cloudflare dashboard, go to the Cloud Connector page.

  2. Select Microsoft Azure as your cloud provider.

  3. 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 named static-assets, and files should be placed inside this folder. For example, https://<YOUR_HOSTNAME>/static-assets/style.css will map to https://<BUCKET_NAME>.blob.core.windows.net/static-assets/style.css.
  4. (Optional) Use URL Rewrite Rules to adjust the URL structure. For example, you can create a URL rewrite that changes /static-assets to /my-pages-project/static-assets to match the file structure of your object storage bucket.

  5. (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-assets URI path for seven days (defined through the Edge TTL setting).

  6. Click Next and enter a descriptive name like Serve static assets from Azure in Cloud Connector name.

  7. Under If, select Custom filter expression and enter the following expression: http.request.full_uri wildcard "http*://<YOUR_HOSTNAME>/static-assets/*"

  8. 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:

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";

  1. In the Cloudflare dashboard, go to the Rules Overview page.

  2. Select Create rule > Compression Rule.

  3. (Optional) Select one of the rule templates that address common use cases. Then, review and adjust the proposed rule configuration.

  4. Enter a descriptive name for the rule in Rule name.

  5. 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.

  6. (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.

  7. Under Compression options, set the compression options.

  8. 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

<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:

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:

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

Then

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

Then

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

Then

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

Then

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

Then

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:

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:

Create a configuration rule in the dashboard

import { Render, Steps, DashButton } from "~/components";

  1. In the Cloudflare dashboard, go to the Rules Overview page.

  2. Select Create rule > Configuration Rule.

  3. (Optional) Select one of the rule templates that address common use cases. Then, review and adjust the proposed rule configuration.

  4. Enter a descriptive name for the rule in Rule name.

  5. 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.

  6. (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.

  7. Under Then the settings are, add the configuration settings you wish to change for requests matching the rule expression.

  8. 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:

  1. Verify that the origin sends data incrementally without Cloudflare.
  2. Check the origin response's Content-Type and Cache-Control headers.
  3. Review features and rules that apply to the response path.
  4. Create a path-specific Configuration Rule that sets Response Body Buffering to None.
  5. 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
}

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:

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:

:::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:

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:

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 */}

  1. In the Cloudflare dashboard, go to the Rules Overview page.

  2. Select Create rule > Custom Error Rule.

  3. Enter a descriptive name for the rule in Rule name.

  4. 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 400 status code or above, except for block and challenge actions issued by Cloudflare’s security products.

  5. 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.

  6. (Optional) In Response code, enter the HTTP status code of the response (an integer value between 400 and 999). If provided, this value will override the current response status code.

  7. (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).

  8. 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 */}

  1. In the Create Custom Error Asset sidebar, enter a name for the asset in Asset name.
  2. (Optional) Enter a description for the asset in Description.
  3. 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.
  4. 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:

  1. (Optional) Create a custom error asset based on a URL you provide.
  2. Create a custom error rule in the http_custom_errors phase, 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:

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:

  1. Use the List zone rulesets operation to check if there is already a ruleset for the http_custom_errors phase at the zone level.

  2. 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_errors phase.

    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:

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]

:::

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:

  1. In the Cloudflare dashboard, go to the Error Pages page.

  2. Identify your desired custom error page type.

  3. (Optional) To preview the current error page (default or custom), select the link in the Show column.

  4. To edit the error page, select the three dots > Edit next to the page type you previously identified.

  5. 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.

  6. Select Confirm.

To update an account-level custom error page:

  1. In the Cloudflare dashboard, go to the Settings page.

  2. Go to Error Pages and identify your desired custom error page type.

  3. (Optional) To preview the current error page (default or custom), select the link in the Show column.

  4. To edit the error page, select the three dots > Edit next to the page type you previously identified.

  5. 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.

  6. 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:

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:

<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:

<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:

<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:

<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:

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:

  1. Search for a configured Error Page at the account level for the specific error.
  2. 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).
  3. 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.
  4. 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.
  5. 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.
  6. 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:

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:

:::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:

Cloudflare normalization

When using the Cloudflare URL normalization, some extra normalization techniques will be applied to URLs of incoming requests, in the following order:

  1. Normalize back slashes (\) into forward slashes (/).
  2. Merge successive forward slashes (for example, // will be normalized to /).
  3. 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";

  1. In the Cloudflare dashboard, go to the Rules Settings page.

  2. Go to the URL Normalization tab.

  3. Configure the available URL normalization settings.

    Available URL normalization settings in the Cloudflare dashboard

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:

Normalize incoming URLs

Default value: On

Configures the URLs of all incoming traffic to Cloudflare:

Normalize URLs to origin

Default value: Off

Configures URLs sent to the origin:

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:

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:

Create an origin rule in the dashboard

import { Render, Steps, DashButton } from "~/components";

  1. In the Cloudflare dashboard, go to the Rules Overview page.

  2. Select Create rule > Origin Rule.

  3. (Optional) Select one of the rule templates that address common use cases. Then, review and adjust the proposed rule configuration.

  4. Enter a descriptive name for the rule in Rule name.

  5. Under When incoming requests match, define the rule expression.

  6. Under Set origin parameters, define the origin rule settings you wish to change for requests matching the rule expression.

  7. 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:

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]

:::

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]

:::

[^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

:::

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

Example A record

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:

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:

  1. Create a URL rewrite to remove /uploads from the path.
  2. Create an origin rule to modify the Host header 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

  1. In the Cloudflare dashboard, go to the Rules Overview page.

  2. Select Create rule > URL Rewrite Rule.

  3. Enter a descriptive name for the rule in Rule name.

  4. 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/.*"
    
  5. Under Set Rewrite parameters, select Path > Rewrite to, and select Dynamic.

  6. 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.jpg to /example.jpg.

  7. 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. :::

  1. In the Cloudflare dashboard, go to the Rules Overview page.

  2. Select Create rule > Origin Rule.

  3. Enter a descriptive name for the rule in Rule name.

  4. Under When incoming requests match, define the rule expression:

    Text in Expression Editor:

    raw.http.request.uri.path matches "^/uploads/.*"
    
  5. Under Set origin parameters, select Host Header > Rewrite to.

  6. Configure the rule to modify the Host header to desired hostname:

    Text after Host Header > Rewrite to:

    example.com
    

    This will set the Host header to example.com for matching requests. Make sure to replace example.com with your actual hostname.

  7. (Optional) To route requests to a different origin (DNS target), use DNS override:

    Text after DNS Record > Override to:

    example.com
    

    This will route requests to the DNS target of example.com instead of your default DNS record.

  8. 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:

  1. In the Cloudflare dashboard, go to the Workers & Pages page.

  2. Select your Pages project.

  3. Go to Custom domains.

  4. Select Set up a custom domain.

  5. Enter blogmirror.example.com and 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

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.

  1. Go to Rules > Overview.

  2. Select Create rule > URL Rewrite Rule.

  3. Enter a descriptive name for the rule in Rule name.

  4. In If incoming requests match, select Wildcard pattern.

  5. Enter the following value in Request URL:

    https://<YOUR_HOSTNAME>/blog/*
    

    In the current example, the value would be https://mycustomerexample.com/blog/*.

  6. In Then rewrite the path and/or query, enter the following values under Path:

    Target path Rewrite to
    [/] blog/* [/] ${1}
  7. 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

  1. In the Cloudflare dashboard, go to the R2 object storage page.

  2. Select your bucket.

  3. On the bucket page, select Settings.

  4. Under Public access > Custom Domains, select Connect Domain.

  5. Enter the domain name you want to connect to — imagesbucket.example.com in this example — and select Continue.

  6. 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

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.

  1. In the Cloudflare dashboard, go to the Rules Overview page.

  2. Select Create rule > URL Rewrite Rule.

  3. Enter a descriptive name for the rule in Rule name.

  4. In If incoming requests match, select Wildcard pattern.

  5. Enter the following value in Request URL:

    https://<YOUR_HOSTNAME>/images/*
    

    In the current example, the value would be https://mycustomerexample.com/images/*.

  6. In Then rewrite the path and/or query, enter the following values under Path:

    Target path Rewrite to
    [/] images/* [/] ${1}
  7. 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:

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:

  1. In the Cloudflare dashboard, go to the Page Rules page.

  2. Select Create Page Rule.

  3. For URL, enter the URL or URL pattern that should match the rule (more details about wildcard matching).

  4. 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.

  5. In the Order dropdown, specify the desired order: First, Last or Custom.

  6. 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]

Edit a page rule

To edit a page rule in the dashboard:

  1. In the Cloudflare dashboard, go to the Page Rules page.

  2. 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:

  1. In the Cloudflare dashboard, go to the Page Rules page.

  2. 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:

  1. In the Cloudflare dashboard, go to the Page Rules page.

  2. Select Create Page Rule.

  3. Under If the URL matches, enter the URL or URL pattern that should match the rule.

  4. In Pick a Setting, choose Forwarding URL from the drop-down menu.

  5. For Select status code, choose 301 - Permanent Redirect or 302 - Temporary Redirect.

  6. Enter the destination URL.

  7. 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:

Limitations include:

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:

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:

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

:::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:

This example page rule configuration defines the root as the canonical version of your domain:

This example redirects visitors to a specific page with an easy to remember URL:

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:

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.

The following example page rule configuration applies unique rules for critical pages that do not change very often.

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:

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/foo/* does not match example.com/foo, but example.com/foo* does match.

Helpful tips

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:

  1. In the Cloudflare dashboard, go to the Billing page.

  2. Go to Subscriptions.

  3. Find the associated website (listed in alphabetical order) and select Edit.

  4. 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).

  5. 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:

Example Page Rule configuration with two wildcards. The forwarding URL contains a $2 placeholder, which will be replaced with the content matched by the second

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:

Incorrect Page Rule configuration with a single wildcard, but still using the $2 placeholder in the forwarding URL. This configuration causes

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:

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:

Key differences

The evaluation and execution order of Rules features is different from Page Rules:

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:

A **Page Rules URL** like:

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

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:

How to migrate:

  1. 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://*
    • Then:
      • Target URL: https://${1}
      • Status code: 301
      • Preserve query string: Enabled
  2. Turn off your existing Page Rule and validate the behavior of the redirect you created.

  3. If your tests succeed, delete the existing Page Rule.

Page Rules configuration Migrate to a single redirect
Example Page Rule with 'Always Use HTTPS' setting Single redirect matching the 'Always Use HTTPS' setting of the example Page Rule

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:

How to migrate:

  1. 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")
    • Then the settings are:
      • Setting: Automatic HTTPS Rewrites
      • Value: On
  2. Turn off your existing Page Rule and validate the behavior of the configuration rule you created.

  3. If your tests succeed, delete the existing Page Rule.

Page Rules configuration Migrate to a configuration rule
Example Page Rule with 'Automatic HTTPS Rewrites' setting Configuration rule matching the 'Automatic HTTPS Rewrites' setting of the example Page 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:

How to migrate:

  1. 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")
    • Then:
      • Cache eligibility: Eligible for cache
      • Browser TTL: Override origin and use this TTL
      • Input time-to-live (TTL): 1 day
  2. Turn off your existing Page Rule and validate the behavior of the cache rule you created.

  3. If your tests succeed, delete the existing Page Rule.

Page Rules configuration Migrate to a cache rule
Example Page Rule with 'Browser Cache TTL' setting Cache rule matching the 'Browser Cache TTL' setting of the example Page 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:

How to migrate:

  1. 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")
    • Then the settings are:
      • Setting: Browser Integrity Check
      • Value: On
  2. Turn off your existing Page Rule and validate the behavior of the configuration rule you created.

  3. If your tests succeed, delete the existing Page Rule.

Page Rules configuration Migrate to a configuration rule
Example Page Rule with 'Browser Integrity Check' setting Configuration rule matching the 'Browser Integrity Check' setting of the example Page 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:

How to migrate:

  1. Create a cache rule to bypass cache for requests containing cookie test_cookie for any hostname containing example.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")
    • Then:
      • Cache eligibility: Bypass cache
  2. Turn off your existing Page Rule and validate the behavior of the cache rule you created.

  3. If your tests succeed, delete the existing Page Rule.

Page Rules configuration Migrate to a cache rule
Example Page Rule with 'Bypass Cache on Cookie' setting Cache rule matching the 'Bypass Cache on Cookie' setting of the example Page 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:

How to migrate:

  1. 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")
    • Then:
      • Cache eligibility: Eligible for cache
      • Setting: Cache key
        • Cache by device type: On
  2. Turn off your existing Page Rule and validate the behavior of the cache rule you created.

  3. If your tests succeed, delete the existing Page Rule.

Page Rules configuration Migrate to a cache rule
Example Page Rule with 'Cache By Device Type' setting Cache rule matching the 'Cache By Device Type' setting of the example Page 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:

How to migrate:

  1. 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")
    • Then:
      • Cache eligibility: Eligible for cache
      • Setting: Cache key
        • Cache deception armor: On
  2. Turn off your existing Page Rule and validate the behavior of the cache rule you created.

  3. If your tests succeed, delete the existing Page Rule.

Page Rules configuration Migrate to a cache rule
Example Page Rule with 'Cache Deception Armor' setting Cache rule matching the 'Cache Deception Armor' setting of the example Page 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:

How to migrate:

  1. 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")
    • Then:
      • Cache eligibility: Eligible for cache
  2. Turn off your existing Page Rule and validate the behavior of the cache rule you created.

  3. If your tests succeed, delete the existing Page Rule.

Page Rules configuration Migrate to a cache rule
Example Page Rule with 'Cache Level' set to 'Cache Everything' Cache rule matching the 'Cache Level: Cache Everything' setting of the example Page 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:

How to migrate:

  1. Create a cache rule to cache responses containing cookie test_cookie for any hostname containing example.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")
    • Then:
      • Cache eligibility: Eligible for cache
  2. Turn off your existing Page Rule and validate the behavior of the cache rule you created.

  3. If your tests succeed, delete the existing Page Rule.

Page Rules configuration Migrate to a cache rule
Example Page Rule with 'Cache on Cookie' setting Cache rule matching the 'Cache on Cookie' setting of the example Page 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:

How to migrate:

  1. Create a cache rule to cache responses with status code between 200 and 599 for 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")
      • 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
  2. Turn off your existing Page Rule and validate the behavior of the cache rule you created.

  3. If your tests succeed, delete the existing Page Rule.

Page Rules configuration Migrate to a cache rule
Example Page Rule with the 'Cache TTL by status code' setting Cache rule matching the 'Cache TTL by status code' setting of the example Page 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:

How to migrate:

  1. 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")
    • Then:
      • Cache eligibility: Eligible for cache
      • Setting: Cache key
        • Query string: All query string parameters
  2. Turn off your existing Page Rule and validate the behavior of the cache rule you created.

  3. If your tests succeed, delete the existing Page Rule.

Page Rules configuration Migrate to a cache rule
Example Page Rule with the 'Custom Cache Key' setting Cache rule matching the 'Custom Cache Key' setting of the example Page 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:

How to migrate:

  1. 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")
    • Then the settings are:
      • Setting: Disable Apps
  2. Turn off your existing Page Rule and validate the behavior of the configuration rule you created.

  3. If your tests succeed, delete the existing Page Rule.

Page Rules configuration Migrate to a configuration rule
Example Page Rule with 'Disable Apps' setting Configuration rule matching the 'Disable Apps' setting of the example Page 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:

How to replace:

  1. 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")
    • Then the settings are:
      • Polish: Off
      • Rocket Loader: Off
  2. Turn off your existing Page Rule and validate the behavior of the configuration rule you created.

  3. If your tests succeed, delete the existing Page Rule.

Page Rules configuration Migrate to a configuration rule
Example Page Rule with 'Disable Performance' setting Configuration rule partially matching the 'Disable Performance' setting of the example Page 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:

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:

  1. Create a configuration rule to turn off one or more security features:

  2. 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.

  3. Turn off your existing Page Rule and validate the behavior of the rules you created.

  4. 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:

How to migrate:

  1. 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")
    • Then the settings are:
      • Setting: Disable Zaraz
  2. Turn off your existing Page Rule and validate the behavior of the configuration rule you created.

  3. If your tests succeed, delete the existing Page Rule.

Page Rules configuration Migrate to a configuration rule
Example Page Rule with 'Disable Zaraz' setting Configuration rule matching the 'Disable Zaraz' setting of the example Page 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:

How to migrate:

  1. 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")
    • Then:
      • Cache eligibility: Eligible for cache
      • Setting: Edge TTL
        • Ignore cache-control header and use this TTL
        • Input time-to-live (TTL): 1 day
  2. Turn off your existing Page Rule and validate the behavior of the cache rule you created.

  3. If your tests succeed, delete the existing Page Rule.

Page Rules configuration Migrate to a cache rule
Example Page Rule with the 'Edge Cache TTL' setting Cache rule matching the 'Edge Cache TTL' setting of the example Page 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:

How to migrate:

  1. 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")
    • Then the settings are:
      • Setting: Email Obfuscation
        • Value: Off
  2. Turn off your existing Page Rule and validate the behavior of the configuration rule you created.

  3. If your tests succeed, delete the existing Page Rule.

Page Rules configuration Migrate to a configuration rule
Example Page Rule with 'Email Obfuscation' setting Configuration rule matching the 'Email Obfuscation > Off' setting of the example Page 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:

How to migrate:

  1. Create a single redirect to permanently redirect requests from https://www.example.com to https://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/*
    • Then:
      • Target URL: https://example.com/${1}
      • Status code: 301
      • Preserve query string: Enabled
  2. Turn off your existing Page Rule and validate the behavior of the redirect you created.

  3. 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 Page Rule #1 with 'Forwarding URL' setting Single redirect matching the 'Forwarding URL' setting of the example Page Rule #1

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:

How to migrate:

  1. Create a single redirect to permanently redirect requests for example.com/old-path to example.com/new-path:

    • If incoming requests match: Wildcard pattern
      • Request URL: https://example.com/old-path/*
    • Then:
      • Target URL: https://example.com/new-path/${1}
      • Status code: 301
      • Preserve query string: Enabled
  2. Turn off your existing Page Rule and validate the behavior of the redirect you created.

  3. 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
Example Page Rule #2 with 'Forwarding URL' setting Single redirect matching the 'Forwarding URL' setting of the example Page Rule #2

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:

How to migrate:

  1. Create an origin rule changing the Host header to example.saas-provider.com 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")
    • Then:
      • Set origin parameters:
        • Host Header > Rewrite to: example.saas-provider.com
  2. Turn off your existing Page Rule and validate the behavior of the origin rule you created.

  3. If your tests succeed, delete the existing Page Rule.

Page Rules configuration Migrate to an origin rule
Example Page Rule with 'Host Header Override' setting Origin rule matching the 'Host Header Override' setting of the example Page 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:

How to migrate:

  1. Turn on the Add visitor location headers Managed Transform — a Transform Rules feature — to add the CF-IPCountry and other location headers to all requests.
  2. Turn off your existing Page Rule and validate the behavior of the Managed Transform.
  3. If your tests succeed, delete the existing Page Rule.

Page Rules configuration Migrate to a Managed Transform
Example Page Rule with 'IP Geolocation Header' setting The 'Add visitor location headers' Managed Transform matching the 'IP Geolocation Header' setting of the example Page Rule

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:

How to migrate:

  1. 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")
    • Then the settings are:
      • Setting: Opportunistic Encryption
        • Value: Off
  2. Turn off your existing Page Rule and validate the behavior of the configuration rule you created.

  3. If your tests succeed, delete the existing Page Rule.

Page Rules configuration Migrate to a configuration rule
Example Page Rule with 'Opportunistic Encryption' setting Configuration rule matching the 'Opportunistic Encryption > Off' setting of the example Page 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:

How to migrate:

  1. 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")
    • Then:
      • Cache eligibility: Eligible for cache
      • Setting: Origin Cache Control
        • Enable Origin Cache Control: Off
  2. Turn off your existing Page Rule and validate the behavior of the cache rule you created.

  3. If your tests succeed, delete the existing Page Rule.

Page Rules configuration Migrate to a cache rule
Example Page Rule with 'Origin Cache Control' setting Cache rule matching the 'Origin Cache Control' setting of the example Page 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:

How to migrate:

  1. 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")
    • Then:
      • Cache eligibility: Eligible for cache
      • Setting: Origin error page pass-thru
        • Use Origin error page pass-thru: On
  2. Turn off your existing Page Rule and validate the behavior of the cache rule you created.

  3. If your tests succeed, delete the existing Page Rule.

Page Rules configuration Migrate to a cache rule
Example Page Rule with 'Origin Error Page Pass-thru' setting Cache rule matching the 'Origin Error Page Pass-thru > On' setting of the example Page Rule

Migrate Polish

Context:

You configured a Page Rule turning off Polish for all subdomains of example.com and the example.com domain itself:

How to migrate:

  1. 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")
    • Then the settings are:
      • Setting: Polish
        • Select value: Off
  2. Turn off your existing Page Rule and validate the behavior of the configuration rule you created.

  3. If your tests succeed, delete the existing Page Rule.

Page Rules configuration Migrate to a configuration rule
Example Page Rule with 'Polish' setting Configuration rule matching the 'Polish > Off' setting of the example Page 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:

How to migrate:

  1. 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")
    • Then:
      • Cache eligibility: Eligible for cache
      • Setting: Cache key
        • Sort query string: On
  2. Turn off your existing Page Rule and validate the behavior of the cache rule you created.

  3. If your tests succeed, delete the existing Page Rule.

Page Rules configuration Migrate to a cache rule
Example Page Rule with 'Query String Sort' setting Cache rule matching the 'Query String Sort > On' setting of the example Page 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:

How to migrate:

  1. Create an origin rule overriding the origin to example.saas-provider.com 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")
    • Then:
      • DNS Record > Override to: example.saas-provider.com
  2. Turn off your existing Page Rule and validate the behavior of the origin rule you created.

  3. If your tests succeed, delete the existing Page Rule.

Page Rules configuration Migrate to an origin rule
Example Page Rule with 'Resolve Override' setting Origin rule matching the 'Resolve Override' setting of the example Page 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:

How to migrate:

  1. 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")
    • Then:
      • Cache eligibility: Eligible for cache
      • Setting: Respect strong ETags
        • Use strong ETag headers: On
  2. Turn off your existing Page Rule and validate the behavior of the cache rule you created.

  3. If your tests succeed, delete the existing Page Rule.

Page Rules configuration Migrate to a cache rule
Example Page Rule with 'Respect Strong ETags' setting Cache rule matching the 'Respect Strong ETags > On' setting of the example Page 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:

How to migrate:

  1. 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")
    • Then the settings are:
      • Setting: Rocket Loader
        • Value: Off
  2. Turn off your existing Page Rule and validate the behavior of the configuration rule you created.

  3. If your tests succeed, delete the existing Page Rule.

Page Rules configuration Migrate to a configuration rule
Example Page Rule with 'Rocket Loader' setting Configuration rule matching the 'Rocket Loader > Off' setting of the example Page 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:

How to migrate:

  1. 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")
    • Then the settings are:
      • Setting: I'm Under Attack
      • Value: On
  2. Turn off your existing Page Rule and validate the behavior of the configuration rule you created.

  3. If your tests succeed, delete the existing Page Rule.

Page Rules configuration Migrate to a configuration rule
Example Page Rule with 'Security Level' setting Configuration rule matching the "Security Level > I'm Under Attack" setting of the example Page 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:

How to migrate:

  1. Turn on the Add "True-Client-IP" header Managed Transform — a Transform Rules feature — to add the True-Client-IP header to all requests.
  2. Turn off your existing Page Rule and validate the behavior of the Managed Transform.
  3. If your tests succeed, delete the existing Page Rule.

Page Rules configuration Migrate to a Managed Transform
Example Page Rule with 'True Client IP Header' setting The 'Add "True-Client-IP" header' Managed Transform matching the 'True Client IP Header' setting of the example Page Rule

Migrate SSL

Context:

You configured a Page Rule setting SSL to Strict for all subdomains of example.com and the example.com domain itself:

How to migrate:

  1. 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")
    • Then the settings are:
      • Setting: SSL
        • Select SSL/TLS encryption mode: Strict
  2. Turn off your existing Page Rule and validate the behavior of the configuration rule you created.

  3. If your tests succeed, delete the existing Page Rule.

Page Rules configuration Migrate to a configuration rule
Example Page Rule with 'SSL' setting Configuration rule matching the "SSL" setting of the example Page Rule

Settings that will not be migrated

The following Page Rules settings will not be migrated to other types of rules:

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 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:

If you configure a new URL rewrite with the following configuration:

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

Origin rule

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

  1. Log in to the Cloudflare dashboard, and select your account.
  2. Set configuration settings at the account level, or select a domain and configure settings for one or more Cloudflare products.

2. Build a simulation

  1. In the Cloudflare dashboard, go to the Simulator page.

  2. Enter a URL to simulate. The URL must include a hostname that belongs to your account.

  3. Select an HTTP method. If you select POST, PUT, or PATCH, you should enter a value in Request Body.

  4. (Optional) Define any custom request properties to simulate the conditions of a specific HTTP/S request. You can customize the following request properties:

    • Protocol (HTTP protocol version)
    • User Agent and Request Headers
    • Cookies
    • Geolocation (request source country, region, and city)
    • Bot Score
    • Request Body (for POST, PUT, and PATCH requests)
    • Skip Challenge (skips a Cloudflare-issued challenge, if any, allowing the simulation to continue)
  5. 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.

  1. 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.

  2. (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:

  1. Copy the JSON shown in the dashboard with the current simulation configuration.
  2. 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

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:

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:

Additionally, the following products will not appear in simulation results:

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:

:::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:

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";

  1. In the Cloudflare dashboard, go to the Snippets page.

  2. (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.

  3. In Snippet name, enter a descriptive name for the snippet. You cannot change the name after creating the snippet.

  4. 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.

  5. Select Snippet rule to configure when the snippet will run.

  6. 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.

  7. (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.

  8. Select Done.

  9. 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:

Resolution

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:

Diagram of the snippets execution workflow

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:

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:

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

What Snippets are not designed for

Key features


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;
	},
};

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:

Migrating to Snippets allows you to:

When to migrate workloads to Workers

You should migrate from Snippets to Workers if your logic:

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:

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:

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": "default-src 'self' example.com *.example.com",
"Strict-Transport-Security" : "max-age=63072000; includeSubDomains; preload",
"Permissions-Policy": "interest-cohort=()",
"X-XSS-Protection": "0",
"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:

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:

<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:

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

Then rewrite the path and/or query

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

Then rewrite the path and/or query

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

Then rewrite the path and/or query

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";

  1. In the Cloudflare dashboard, go to the Rules Settings page.

  2. 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:

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

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:

Add TLS client auth headers

Adds HTTP headers with Mutual TLS (mTLS) client authentication values to the request sent to the origin server:

Add visitor location headers

Adds HTTP headers with location information for the visitor's IP address to the request sent to the origin server:

:::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:

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:

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:

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:

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:

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:

  1. In the Cloudflare dashboard, go to the Rules Overview page.

  2. Select Create rule > Request Header Transform Rule.

  3. (Optional) Select one of the rule templates that address common use cases. Then, review and adjust the proposed rule configuration.

  4. Enter a descriptive name for the rule in Rule name.

  5. 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.

  6. (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. :::

  7. 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.
  8. 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.

  9. 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:

    Example configuration performing three request header modifications: set a dynamic header value, set a static header value, and remove an existing header.

  10. 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:

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

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:

Refer to Fields for reference information on these fields.

:::caution[Important]

:::

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:

To remove an HTTP request header via API, set the following parameter in the action_parameters field:

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:

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:

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:

  1. In the Cloudflare dashboard, go to the Rules Overview page.

  2. Select Create rule > Response Header Transform Rule.

  3. (Optional) Select one of the rule templates that address common use cases. Then, review and adjust the proposed rule configuration.

  4. Enter a descriptive name for the rule in Rule name.

  5. 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.

  6. (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. :::

  7. 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.
  8. 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.

  9. 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:

    Example configuration performing three response header modifications: set a dynamic header value, set a static header value, and remove an existing header.

  10. 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:

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

<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:

Refer to Fields for reference information on these fields.

:::caution[Important]

:::

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:

To add an HTTP response header, keeping any existing headers with the same name, use the following parameters in the action_parameters field:

To remove an HTTP response header, set the following parameter in the action_parameters field:

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:

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:

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:

  1. In the Cloudflare dashboard, go to the Rules Overview page.

  2. Select Create rule > URL Rewrite Rule.

  3. (Optional) Select one of the rule templates that address common use cases. Then, review and adjust the proposed rule configuration.

  4. Enter a descriptive name for the rule in Rule name.

    The URL rewrite rule creation page in the Cloudflare dashboard.

  5. 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.

  6. (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.html to 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.

  7. (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).

  8. 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:

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:

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:

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

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:

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 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:

<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:

A Bulk Redirect Rule enables a Bulk Redirect List, which contains one or more URL redirects.

Diagram outlining the hierarchy relationship between Bulk Redirect Rules, Bulk Redirect Lists, and 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

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:

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:

  1. Create a Bulk Redirect List via API.
  2. Add items (URL redirects) to the list created in step 1.
  3. 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:

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:

Create Bulk Redirects in the dashboard

import { Details, Render, DashButton, Steps } from "~/components";

To create Bulk Redirects in the Cloudflare dashboard you must:

  1. Create a Bulk Redirect List with one or more URL redirects.
  2. 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:

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

  1. (Optional) If you are not using the Bulk Redirect List creation wizard according to the instructions in the previous section:

    1. In the Cloudflare dashboard, go to the Bulk redirects page.

    2. Select Create Bulk Redirect Rule.

  2. In Rule name, enter a descriptive name for the rule.

  3. Select the Bulk Redirect List you previously created.

  4. (Optional) If necessary, select Or use the expression editor to edit the rule expression or the rule key.

  5. 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:

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:

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:

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:

  1. The scheme (http or https) is the same as the source URL of the URL redirect definition. Source URLs with no scheme will match both http and https.

  2. 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.

  3. 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/blog will also match requests to example.com/blog/foo and example.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:

  1. 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 /folder and /folder/subfolder, an incoming request for the /folder/subfolder/item URL path will match the second redirect (/folder/subfolder) because it is more specific.

  2. URL redirects with the exact hostname win over URL redirects with the Include subdomains option enabled.

  3. 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 hostnames bar.com and foo.bar.com, an incoming request to qux.foo.bar.com will match the second redirect (foo.bar.com) because it is more specific.

  4. URL redirects with a specific scheme (either http or https) 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:

For the complete request processing order, refer to Rules execution order.


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:

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

Available fields and functions

The available fields when defining a Bulk Redirect Rule filter expression are the following:

Refer to Fields for reference information on these fields.

:::caution[Important]

:::

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:

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

[^2]: The URL authority is the combination of user information, host, and port components.

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

Then

Notes about this example:

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

Then

Notes about this example:

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

Then

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

Then

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.

  1. Create a Bulk Redirect List named uk_redirect_list with the following URL redirect:

    • Source URL: https://example.com/
    • Target URL: https://example.co.uk/
    • Subpath matching: Enabled
    • Preserve query string: Enabled
  2. 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

Then

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

Then

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

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

If you are using the Expression Editor, enter the following expression:
http.request.uri.path eq "/contact-us/"

Then

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

Then

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

Then

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

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

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:

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:

Create a redirect rule in the dashboard

import { Render, Steps, DashButton } from "~/components";

  1. In the Cloudflare dashboard, go to the Rules Overview page.

  2. Select Create rule > Redirect Rule.

  3. (Optional) Select one of the rule templates that address common use cases. Then, review and adjust the proposed rule configuration.

  4. Enter a descriptive name for the rule in Rule name.

  5. 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.
    • All incoming requests: The rule will apply to all traffic.
    • 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.
  6. 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" }} />


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:

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:

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:

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
      }
    }
  }
}