---
title: Custom metadata
description: Configure per-hostname settings such as URL rewriting and custom headers.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/cloudflare-for-platforms/llms.txt  
> Use this file to discover all available pages before exploring further.

[Skip to content](#%5Ftop) 

### Tags

[ JSON ](https://developers.cloudflare.com/search/?tags=JSON)[ TypeScript ](https://developers.cloudflare.com/search/?tags=TypeScript) 

# Custom metadata

You may wish to configure per-hostname (customer) settings beyond the scale of Rules or Rate Limiting.

To do this, you will first need to reach out to your account team to enable access to Custom Metadata. After configuring custom metadata, you can use it in the following ways:

* Read the metadata JSON from [Cloudflare Workers](https://developers.cloudflare.com/workers/) (requires access to Workers) to define per-hostname behavior.
* Use custom metadata values in [rule expressions](https://developers.cloudflare.com/ruleset-engine/rules-language/expressions/) of different Cloudflare security products to define the rule scope.

Note

Only certain customers have access to this feature. For more details, see the [Plans page](https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/plans/).

---

## Examples

* Per-customer URL rewriting — for example, customers 1-10,000 fetch assets from server A, 10,001-20,000 from server B, etc.
* Adding custom headers — for example, `X-Customer-ID: $number` based on the metadata you provided
* Setting HTTP Strict Transport Security (“HSTS”) headers on a per-customer basis

Please speak with your Solutions Engineer to discuss additional logic and requirements.

## Submitting custom metadata

You may add custom metadata to Cloudflare via the Custom Hostnames API. This data can be added via a [PATCH request](https://developers.cloudflare.com/api/resources/custom%5Fhostnames/methods/edit/) to the specific hostname ID to set metadata for that hostname, for example:

Required API token permissions

At least one of the following [token permissions](https://developers.cloudflare.com/fundamentals/api/reference/permissions/)is required:
* `SSL and Certificates Write`

Edit Custom Hostname

```

curl "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/custom_hostnames/$CUSTOM_HOSTNAME_ID" \

  --request PATCH \

  --header "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \

  --json '{

    "ssl": {

        "method": "http",

        "type": "dv"

    },

    "custom_metadata": {

        "customer_id": "12345",

        "redirect_to_https": true,

        "security_tag": "low"

    }

  }'


```

Changes to metadata will propagate across Cloudflare's edge within 30 seconds.

---

## Accessing custom metadata from a Cloudflare Worker

The metadata object will be accessible on each request using the `request.cf.hostMetadata` property. You can then read the data, and customize any behavior on it using the Worker.

In the example below we will use the user\_id in the Worker that was submitted using the API call above `"custom_metadata":{"customer_id":"12345","redirect_to_https": true,"security_tag":"low"}`, and set a request header to send the `customer_id` to the origin:

* [  JavaScript ](#tab-panel-4782)
* [  TypeScript ](#tab-panel-4783)

JavaScript

```

export default {

  /**

   * Fetch and add a X-Customer-Id header to the origin based on hostname

   * @param {Request} request

   */

  async fetch(request, env, ctx) {

    const customer_id = request.cf.hostMetadata.customer_id;

    const newHeaders = new Headers(request.headers);

    newHeaders.append("X-Customer-Id", customer_id);


    const init = {

      headers: newHeaders,

      method: request.method,

    };

    return fetch(request.url, init);

  },

};


```

TypeScript

```

export default {

  /**

   * Fetch and add a X-Customer-Id header to the origin based on hostname

   * @param {Request} request

   */

  async fetch(request, env, ctx): Promise<Response> {

    const customer_id = request.cf.hostMetadata.customer_id;

    const newHeaders = new Headers(request.headers);

    newHeaders.append("X-Customer-Id", customer_id);


    const init = {

      headers: newHeaders,

      method: request.method,

    };

    return fetch(request.url, init);

  },

} satisfies ExportedHandler<Env>;


```

## Accessing custom metadata in a rule expression

Use the [cf.hostname.metadata](https://developers.cloudflare.com/ruleset-engine/rules-language/fields/reference/cf.hostname.metadata/) field to access the metadata object in rule expressions. To obtain the different values from the JSON object, use the [lookup\_json\_string](https://developers.cloudflare.com/ruleset-engine/rules-language/functions/#lookup%5Fjson%5Fstring) function.

The following rule expression defines that there will be a rule match if the `security_tag` value in custom metadata contains the value `low`:

```

lookup_json_string(cf.hostname.metadata, "security_tag") eq "low"


```

---

## Best practices

* Ensure that the JSON schema used is fixed: changes to the schema without corresponding Cloudflare Workers changes will potentially break websites, or fall back to any defined “default” behavior
* Prefer a flat JSON structure
* Use string keys in snake\_case (rather than camelCase or PascalCase)
* Use proper booleans (true/false rather than `true` or `1` or `0`)
* Use numbers to represent integers instead of strings (`1` or `2` instead of `"1"` or `"2"`)
* Define fallback behaviour in the non-presence of metadata
* Define fallback behaviour if a key or value in the metadata are unknown

General guidance is to follow [Google's JSON Style guide ↗](https://google.github.io/styleguide/jsoncstyleguide.xml) where appropriate.

---

## Limitations

There are some limitations to the metadata that can be provided to Cloudflare:

* It must be valid JSON.
* Any origin resolution — for example, directing requests for a given hostname to a specific backend — must be provided as a hostname that exists within Cloudflare's DNS (even for non-authoritative setups). Providing an IP address directly will cause requests to error.
* The total payload must not exceed 4 KB.
* It requires a Cloudflare Worker that knows how to process the schema and trigger logic based on the contents.

Note

Be careful when modifying the schema. Adding, removing, or changing keys and possible values may cause the Cloudflare Worker to either ignore the data or return an error for requests that trigger it.

### Terraform support

[Terraform](https://developers.cloudflare.com/terraform/) only allows maps of a single type, so Cloudflare's Terraform support for custom metadata for custom hostnames is limited to string keys and values.

```json
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/cloudflare-for-platforms/","name":"Cloudflare for Platforms"}},{"@type":"ListItem","position":3,"item":{"@id":"/cloudflare-for-platforms/cloudflare-for-saas/","name":"Cloudflare for SaaS"}},{"@type":"ListItem","position":4,"item":{"@id":"/cloudflare-for-platforms/cloudflare-for-saas/domain-support/","name":"Custom hostnames"}},{"@type":"ListItem","position":5,"item":{"@id":"/cloudflare-for-platforms/cloudflare-for-saas/domain-support/custom-metadata/","name":"Custom metadata"}}]}
```
