---
title: Functions reference · Cloudflare Ruleset Engine docs
description: "The Cloudflare Rules language provides functions for manipulating
  and validating values in an expression:"
lastUpdated: 2025-09-26T09:26:45.000Z
chatbotDeprioritize: false
source_url:
  html: https://developers.cloudflare.com/ruleset-engine/rules-language/functions/
  md: https://developers.cloudflare.com/ruleset-engine/rules-language/functions/index.md
---

The Cloudflare Rules language provides functions for manipulating and validating values in an expression:

* [Transformation functions](#transformation-functions) manipulate values extracted from an HTTP request.
* The [HMAC validation function](#hmac-validation) tests the validity of an HMAC token. Use it to write expressions that target requests based on the presence of a valid HMAC token.

## Transformation functions

The Rules language supports several functions that transform values extracted from HTTP requests. A common use case for transformation functions is the conversion of a string of characters to uppercase or lowercase, since by default, string evaluation is case-sensitive.

For example, the `lower()` function converts all uppercase characters in a string to lowercase.

In the expression below, the `lower()` function transforms `http.host` values to lowercase so that they match the target value `"www.cloudflare.com"`:

```sql
lower(http.host) == "www.cloudflare.com"
```

Transformation functions that do not take arrays as an argument type require the `[*]` index notation. Refer to [Arrays](https://developers.cloudflare.com/ruleset-engine/rules-language/values/#arrays) for more information.

The Rules language supports these transformation functions:

### `any`

`any(Array<Boolean>)`: Boolean

Returns `true` when the comparison operator in the argument returns `true` for *any* of the values in the argument array. Returns `false` otherwise.

Example:

```txt
any(url_decode(http.request.body.form.values[*])[*] contains "an xss attack")
```

### `all`

`all(Array<Boolean>)`: Boolean

Returns `true` when the comparison operator in the argument returns `true` for *all* values in the argument array. Returns `false` otherwise.

Example:

```plaintext
all(http.request.headers["content-type"][*] == "application/json")
```

### `cidr`

`cidr(address IP address, ipv4_network_bits Integer, ipv6_network_bits Integer)`: IP address

Returns the network address corresponding to an IP address (IPv4 or IPv6), given the provided IPv4 and IPv6 network bits (which determine the corresponding netmasks).

The `address` parameter must be a field, that is, it cannot be a literal String.

The `ipv4_network_bits` value must be between 1 and 32, and the `ipv6_network_bits` value must be between 1 and 128.

Examples:

* If `ip.src` is `113.10.0.2`, `cidr(ip.src, 24, 24)` will return `113.10.0.0`.
* If `ip.src` is `2001:0000:130F:0000:0000:09C0:876A:130B`, `cidr(ip.src, 24, 24)` will return `2001:0000:0000:0000:0000:0000:0000:0000`.

Note

You can only use the `cidr()` function in [custom rules](https://developers.cloudflare.com/waf/custom-rules/) and [rate limiting rules](https://developers.cloudflare.com/waf/rate-limiting-rules/).

### `cidr6`

`cidr6(address IP address, ipv6_network_bits Integer)`: IP address

Returns the IPv6 network address corresponding to an IPv6 address, given the provided network bits (which determine the netmask). If you provide an IPv4 address in the first parameter, it will be returned unchanged.

The `address` parameter must be a field, that is, it cannot be a literal String.

The `ipv6_network_bits` value must be between 1 and 128.

This function is equivalent to: `cidr(<address>, 32, <ipv6_network_bits>)`.

Examples:

* If `ip.src` is `2001:0000:130F:0000:0000:09C0:876A:130B`, `cidr6(ip.src, 24)` will return `2001:0000:0000:0000:0000:0000:0000:0000`.
* If `ip.src` is `113.10.0.2`, `cidr6(ip.src, 24)` will return `113.10.0.2` (unchanged).

Note

You can only use the `cidr6()` function in [custom rules](https://developers.cloudflare.com/waf/custom-rules/) and [rate limiting rules](https://developers.cloudflare.com/waf/rate-limiting-rules/).

### `concat`

`concat(String | Bytes | Array)`: String | Array

Takes a comma-separated list of values. Concatenates the argument values into a single String or array.

The return type depends on the type of input arguments. For example, if you concatenate arrays, the function will return an array.

For example, `concat("String1", " ", "String", "2")` will return `"String1 String2"`.

### `decode_base64`

`decode_base64(source String)`: String

Decodes a Base64-encoded String specified in `source`.

`source` must be a field, that is, it cannot be a literal String.

For example, with the following HTTP request header: `client_id: MTIzYWJj`, `(any(decode_base64(http.request.headers["client_id"][*])[*] eq "123abc"))` would return `true`.

Note

You can only use the `decode_base64()` function in [Transform Rules](https://developers.cloudflare.com/rules/transform/), [custom rules](https://developers.cloudflare.com/waf/custom-rules/), and [rate limiting rules](https://developers.cloudflare.com/waf/rate-limiting-rules/).

### `ends_with`

`ends_with(source String, substring String)`: Boolean

Returns `true` when the source ends with a given substring. Returns `false` otherwise. The source cannot be a literal value (like `"foo"`).

For example, if `http.request.uri.path` is `"/welcome.html"`, then `ends_with(http.request.uri.path, ".html")` will return `true`.

### `len`

`len(String | Bytes | Array)`: Integer

Returns the byte length of a String or Bytes value, or the number of elements in an array.

For example, if the value of `http.host` is `"example.com"`, then `len(http.host)` will return `11`.

### `lookup_json_integer`

`lookup_json_integer(field String, key String | Integer, key String | Integer optional, ...)`: Integer

Returns the integer value associated with the supplied `key` in `field`.

The `field` must be a string representation of a valid JSON document.

The `key` can be an attribute name, a zero-based position number in a JSON array, or a combination of these two options (as extra function parameters), while following the hierarchy of the JSON document to obtain a specific integer value.

Note: This function only works for plain integers. For example, it will not work for floating numbers with a zero decimal part such as `42.0`.

Examples:

* Given the following JSON object contained in the `http.request.body.raw` field:\
  `{ "record_id": "aed53a", "version": 2 }`\
  Then `lookup_json_integer(http.request.body.raw, "version")` will return `2`.

* Given the following nested object:\
  `{ "product": { "id": 356 } }`\
  Then `lookup_json_integer(http.request.body.raw, "product", "id")` will return `356`.

* Given the following JSON array at the root level:\
  `["first_item", -234]`\
  Then `lookup_json_integer(http.request.body.raw, 1)` will return `-234`.

* Given the following array in a JSON object attribute:\
  `{ "network_ids": [123, 456] }`\
  Then `lookup_json_integer(http.request.body.raw, "network_ids", 0)` will return `123`.

* Given the following root-level array of JSON objects:\
  `[{ "product_id": 123 }, { "product_id": 456 }]`\
  Then `lookup_json_integer(http.request.body.raw, 1, "product_id")` will return `456`.

### `lookup_json_string`

`lookup_json_string(field String, key String | Integer, key String | Integer optional, ...)`: String

Returns the string value associated with the supplied `key` in `field`.

The `field` must be a string representation of a valid JSON document.

The `key` can be an attribute name, a zero-based position number in a JSON array, or a combination of these two options (as extra function parameters), while following the hierarchy of the JSON document to obtain a specific value.

Examples:

* Given the following JSON object contained in the `http.request.body.raw` field:\
  `{ "company": "cloudflare", "product": "rulesets" }`\
  Then `lookup_json_string(http.request.body.raw, "company") == "cloudflare"` will return `true`.

* Given the following nested object:\
  `{ "network": { "name": "cloudflare" } }`\
  Then `lookup_json_string(http.request.body.raw, "network", "name") == "cloudflare"` will return `true`.

* Given the following JSON array at the root level:\
  `["other_company", "cloudflare"]`\
  Then `lookup_json_string(http.request.body.raw, 1) == "cloudflare"` will return `true`.

* Given the following array in a JSON object attribute:\
  `{ "networks": ["other_company", "cloudflare"] }`\
  Then `lookup_json_string(http.request.body.raw, "networks", 1) == "cloudflare"` will return `true`.

* Given the following root-level array of JSON objects:\
  `[{ "network": "other_company" }, { "network": "cloudflare" }]`\
  Then `lookup_json_string(http.request.body.raw, 1, "network") == "cloudflare"` will return `true`.

### `lower`

`lower(String)`: String

Converts a string field to lowercase. Only uppercase ASCII bytes are converted. All other bytes are unaffected.

For example, if `http.host` is `"WWW.cloudflare.com"`, then `lower(http.host) == "www.cloudflare.com"` will return `true`.

### `regex_replace`

`regex_replace(source String, regular_expression String, replacement String)`: String

Replaces a part of a source string matched by a regular expression with a replacement string, returning the result. The replacement string can contain references to regular expression capture groups (for example, `${1}` and `${2}`), up to eight replacement references.

Examples:

* Literal match replace:\
  `regex_replace("/foo/bar", "/bar$", "/baz") == "/foo/baz"`

* If there is no match, the input string does not change:\
  `regex_replace("/x", "^/y$", "/mumble") == "/x"`

* Match is case-sensitive by default:\
  `regex_replace("/foo", "^/FOO$", "/x") == "/foo"`

* When there are multiple matches, only one replacement occurs (the first one):\
  `regex_replace("/a/a", "/a", "/b") == "/b/a"`

* Escape a `$` in the replacement string by prefixing it with another `$`:\
  `regex_replace("/b", "^/b$", "/b$$") == "/b$"`

* Replace with capture groups:\
  `regex_replace("/foo/a/path", "^/foo/([^/]*)/(.*)$", "/bar/${2}/${1}") == "/bar/path/a/"`

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.

You can only use the `regex_replace()` function once in an expression, and you cannot nest it with the [`wildcard_replace()`](https://developers.cloudflare.com/ruleset-engine/rules-language/functions/#wildcard_replace) function.

Note

Currently, the `regex_replace()` function is only available in rewrite expressions of [Transform Rules](https://developers.cloudflare.com/rules/transform/) and target URL expressions of [dynamic URL redirects](https://developers.cloudflare.com/rules/url-forwarding/single-redirects/).

### `remove_bytes`

`remove_bytes(Bytes)`: Bytes

Returns a new byte array with all the occurrences of the given bytes removed.

For example, if `http.host` is `"www.cloudflare.com"`, then `remove_bytes(http.host, "\x2e\x77")` will return `"cloudflarecom"`.

### `remove_query_args`

`remove_query_args(field String, query_param1 String, query_param2 String, ...)`: String

Removes one or more query string parameters from a URI query string. Returns a string without the specified parameters.

The `field` must be one of the following:

* `http.request.uri.query`
* `raw.http.request.uri.query`

The `field` cannot be a literal value such as `"search=foo&order=asc"`.

The `remove_query_args()` function will remove all specified parameters (as `query_param1`, `query_param2`, etc.) , including repeated occurrences of the same parameter.

The ordering of unaffected query parameters will be preserved.

Examples:

```txt
// If http.request.uri.query is "order=asc&country=GB":


remove_query_args(http.request.uri.query, "country")  will return "order=asc"
remove_query_args(http.request.uri.query, "order")    will return "country=GB"
remove_query_args(http.request.uri.query, "search")   will return "order=asc&country=GB" (unchanged)


// If http.request.uri.query is "category=Foo&order=desc&category=Bar":


remove_query_args(http.request.uri.query, "order")    will return "category=Foo&category=Bar"
remove_query_args(http.request.uri.query, "category") will return "order=desc"
```

Note

You can only use the `remove_query_args()` function in [rewrite expressions of Transform Rules](https://developers.cloudflare.com/rules/transform/).

### `starts_with`

`starts_with(source String, substring String)`: Boolean

Returns `true` when the source starts with a given substring. Returns `false` otherwise. The source cannot be a literal value (like `"foo"`).

For example, if `http.request.uri.path` is `"/blog/first-post"`, then `starts_with(http.request.uri.path, "/blog")` will return `true`.

### `substring`

`substring(field String | Bytes, start Integer, end Integer optional)`: String

Returns part of the `field` value (the value of a String or Bytes [field](https://developers.cloudflare.com/ruleset-engine/rules-language/fields/reference/)) from the `start` byte index up to (but excluding) the `end` byte index. The first byte in `field` has index `0`. If you do not provide the optional `end` index, the function returns the part of the string from `start` index to the end of the string.

The `start` and `end` indexes can be negative integer values, which allows you to access characters from the end of the string instead of the beginning.

Examples:

```txt
// If http.request.body.raw is "asdfghjk":


substring(http.request.body.raw, 2, 5)   will return "dfg"
substring(http.request.body.raw, 2)      will return "dfghjk"
substring(http.request.body.raw, -2)     will return "jk"
substring(http.request.body.raw, 0, -2)  will return "asdfgh"
```

### `to_string`

`to_string(Integer | Boolean | IP address)`: String

Returns the string representation of an Integer, Boolean, or IP address value.

Examples:

```txt
// If cf.bot_management.score is 5:
to_string(cf.bot_management.score)   will return "5"


// If ssl is true:
to_string(ssl)                       will return "true"
```

Note

You can only use the `to_string()` function in rewrite expressions of [Transform Rules](https://developers.cloudflare.com/rules/transform/) and target URL expressions of [dynamic URL redirects](https://developers.cloudflare.com/rules/url-forwarding/single-redirects/).

### `upper`

`upper(String)`: String

Converts a string field to uppercase. Only lowercase ASCII bytes are converted. All other bytes are unaffected.

For example, if `http.host` is`"www.cloudflare.com"`, then `upper(http.host)` will return `"WWW.CLOUDFLARE.COM"`.

### `url_decode`

`url_decode(source String, options String optional)`: String

Decodes a URL-formatted string defined in `source`, as in the following:

* `%20` and `+` decode to a space character (` `).

* `%E4%BD` decodes to `ä½`.

The `source` must be a field, that is, it cannot be a literal string.

The `options` parameter is optional. You must provide any options as a single string wrapped in quotes, such as `"r"` or `"ur"`. The available options are the following:

* `r`: Applies recursive decoding. For example, `%2520` will be decoded twice (recursively) to a space character (` `).
* `u`: Enables Unicode percent decoding. The result will be encoded in UTF-8. For example, `"%u2601"` would be decoded to a cloud emoji (`☁️`) encoded in UTF-8 (`"\xe2\x98\x81"`, with a size of 3 bytes).

Examples:

```txt
url_decode("John%20Doe")   will return "John Doe"
url_decode("John+Doe")     will return "John Doe"
url_decode("%2520")        will return "%20"
url_decode("%2520", "r")   will return " "


// Using url_decode() with the any() function:
any(url_decode(http.request.body.form.values[*])[*] contains "an xss attack")


// Using the u option to match a specific alphabet
url_decode(http.request.uri.path) matches "(?u)\p{Hangul}+"
```

### `uuidv4`

`uuidv4(source Bytes)`: String

Generates a random UUIDv4 (Universally Unique Identifier, version 4) based on the given argument (a source of randomness). To obtain an array of random bytes, use the [`cf.random_seed`](https://developers.cloudflare.com/ruleset-engine/rules-language/fields/reference/cf.random_seed/) field.

For example, `uuidv4(cf.random_seed)` will return a UUIDv4 similar to `49887398-6bcf-485f-8899-f15dbef4d1d5`.

Note

You can only use the `uuidv4()` function in [rewrite expressions of Transform Rules](https://developers.cloudflare.com/rules/transform/).

### `wildcard_replace`

`wildcard_replace(source Bytes, wildcard_pattern Bytes, replacement Bytes, flags Bytes optional)`: String

Replaces a `source` string, matched by a literal with zero or more `*` wildcard metacharacters, with a replacement string, returning the result. The replacement string can contain references to wildcard capture groups (for example, `${1}` and `${2}`), up to eight replacement references.

If there is no match, the function will return `source` unchanged.

The `source` parameter must be a field (it cannot be a literal string). Additionally, the entire `source` value must match the `wildcard_pattern` parameter (it cannot match only part of the field value).

To enter a literal `*` character in the `wildcard_pattern` parameter, you must escape it using `\*`. Additionally, you must also escape `\` using `\\`. Two unescaped `*` characters in a row (`**`) in this parameter are considered invalid and cannot be used. If you need to perform character escaping, it is recommended that you use the [raw string syntax](https://developers.cloudflare.com/ruleset-engine/rules-language/values/#raw-string-syntax) for the `wildcard_pattern` parameter.

To enter a literal `$` character in the `replacement` parameter, you must escape it using `$$`.

To perform case-sensitive wildcard matching, set the `flags` parameter to `"s"`.

This function uses lazy matching, that is, it tries to match each `*` metacharacter with the shortest possible string.

You can only use the `wildcard_replace()` function once in an expression, and you cannot nest it with the [`regex_replace()`](https://developers.cloudflare.com/ruleset-engine/rules-language/functions/#regex_replace) function.

Examples:

* If the full URI is `https://apps.example.com/calendar/admin?expand=true`,\
  `wildcard_replace(http.request.full_uri, "https://*.example.com/*/*", "https://example.com/${1}/${2}/${3}")` will return `https://example.com/apps/calendar/admin?expand=true`

* If the full URI is `https://example.com/applications/app1`,\
  `wildcard_replace(http.request.full_uri, "/applications/*", "/apps/${1}")` will return `https://example.com/applications/app1` (unchanged value, since there is no match for the full URI value; you should use the `http.request.uri.path` field for URI path matching).

* If the URI path is `/calendar`,\
  `wildcard_replace(http.request.uri.path, "/*", "/apps/${1}")` will return `/apps/calendar`.

* If the URI path is `/Apps/calendar`,\
  `wildcard_replace(http.request.uri.path, "/apps/*", "/${1}")` will return `/calendar` (case-insensitive match by default).

* If the URI path is `/Apps/calendar`,\
  `wildcard_replace(http.request.uri.path, "/apps/*", "/${1}", "s")` will return `/Apps/calendar` (unchanged value) because there is no case-sensitive match.

* If the URI path is `/apps/calendar/login`,\
  `wildcard_replace(http.request.uri.path, "/apps/*/login", "/${1}/login")` will return `/calendar/login`.

For more examples of wildcard matching, refer to [Wildcard matching](https://developers.cloudflare.com/ruleset-engine/rules-language/operators/#wildcard-matching).

Note

Currently, you can only use the `wildcard_replace()` function in rewrite expressions of [URL rewrites](https://developers.cloudflare.com/rules/transform/url-rewrite/) and target URL expressions of [dynamic URL redirects](https://developers.cloudflare.com/rules/url-forwarding/single-redirects/).

## Magic Firewall Functions

### `bit_slice`

`bit_slice(protocol String, offset_start Number, offset_end Number)`: Number

This function looks for matches on a given slice of bits.

The offset starts on the given protocol header. For example, to match on the first bit of payload for a UDP packet, you must set `offset_start` to `64`.

This is primarily intended for use with `ip`, `udp`, and `tcp`.

The slice (`offset_end` – `offset_start`) cannot be longer than 32 bits, but multiple calls can be joined together by using logical expressions.

The `bit_slice` offset cannot exceed 2,040 bits.

## HMAC validation

Note

Access to the HMAC validation function requires a Cloudflare Pro, Business, or Enterprise plan.

### Overview

You can validate hash-based message authentication code (HMAC) tokens in a rule expression by using the `is_timed_hmac_valid_v0()` function, which has this signature:

```java
is_timed_hmac_valid_v0(
  <String literal as Key>,
  <String field as MessageMAC>,
  <Integer literal as ttl>,
  <Integer as currentTimeStamp>,
  <Optional Integer literal as lengthOfSeparator, default: 0>,
  <Optional String literal as flags>
) -> <Bool as result>
```

The `is_timed_hmac_valid_v0()` function has these parameter definitions:

* `Key` String literal

  * Specifies the secret cryptographic key for validating the HMAC.

* `MessageMAC` String

  * Contains a concatenation of these HMAC elements: `message`, `separator`, `timestamp`, `mac`. For a definition and an example, refer to [MessageMAC](#messagemac).

* `ttl` Integer literal

  * Defines the time-to-live for the HMAC token, expressed in seconds. Determines how long the token is valid, relative to the time it was issued.

* `currentTimeStamp` Integer

  * Represents the UNIX timestamp when Cloudflare received the request, expressed in seconds. Pass the `http.request.timestamp.sec` field as an approximate value to this argument.

* `lengthOfSeparator` Integer literal optional

  * Specifies the length of the `separator` between the `timestamp` and the `message` in the `MessageMAC`. Expressed in bytes, with a default value of `0`.

* `flags` String literal optional

  * When you set this optional argument to `'s'`, the function expects the value of the Base64-encoded `mac` in the `MessageMAC` argument to use the URL-safe character set with no padding.

  * When you do **not** set the value of `flags` to `'s'`, you must URL encode the Base64 value for `mac` in the `MessageMAC` argument.

### Usage

The `is_timed_hmac_valid_v0()` function uses the supplied *Key* to generate a message authentication code (MAC) from the `message` and the `timestamp` regions of the MessageMAC. When the generated MAC matches the `mac` region of the MessageMAC and the token has not expired, the HMAC is valid and the function returns `true`.

For example, the following expression matches requests to `downloads.example.com` that do not include valid HMAC tokens:

```java
http.host == "downloads.example.com"
and not is_timed_hmac_valid_v0("mysecretkey", http.request.uri, 100000, http.request.timestamp.sec, 8)
```

For examples of rules that use HMAC validation, refer to [Configure token authentication](https://developers.cloudflare.com/waf/custom-rules/use-cases/configure-token-authentication/) in the WAF documentation.

### MessageMAC

A valid MessageMAC satisfies the following regular expression:

```txt
(.+)(.*)(\d{10})-(.{43,})
```

and is composed of these parentheses-delimited expressions:

| Expression | Description | Example |
| - | - | - |
| `(.+)` | The `message` to validate. | `/download/cat.jpg` |
| `(.*)` | The `separator` between message and timestamp, commonly a parameter name. | `&verify=` |
| `(\d{10})` | The 10-digit UNIX `timestamp` when the MAC was issued, expressed in seconds. | `1484063137` |
| `(.{43,})` | A Base64-encoded version of the `mac`. When you do not set the value of the `urlSafe` argument in the HMAC validation function to `'s'`, you must URL-encode the Base64 value for `mac`. When the Base64 MAC encoding is URL-safe, the `mac` value contains 43 bytes. Otherwise, the value will be 44 bytes or more, because of URL encoding. | `IaLGSmELTvlhfd0ItdN6PhhHTFhzx73EX8uy%2FcSDiIU%3D` |

For details on generating a MessageMAC, refer to [HMAC token generation](https://developers.cloudflare.com/waf/custom-rules/use-cases/configure-token-authentication/#hmac-token-generation).

## HMAC validation examples

Note

When you do not use the optional `flags` argument for `is_timed_hmac_valid_v0()`, you must URL-encode the Base64 value for `mac` in the `MessageMAC` argument.

For more information, refer to [HMAC Validation: Overview](#overview).

### MessageMAC in a single field

Consider the case where the MessageMAC is contained entirely within a single field, as in this example URI path:

```txt
/download/cat.jpg?verify=1484063787-IaLGSmELTvlhfd0ItdN6PhhHTFhzx73EX8uy%2FcSDiIU%3D
```

Note how the URI maps to the elements of the MessageMAC:

| Element | Value |
| - | - |
| `message` | `/download/cat.jpg` |
| `separator` | `?verify=` (with length `8`) |
| `timestamp` | `1484063787` |
| `mac` | `IaLGSmELTvlhfd0ItdN6PhhHTFhzx73EX8uy%2FcSDiIU%3D` |

When the MessageMAC is contained entirely within a single field such as `http.request.uri`, pass the field name to the `MessageMAC` argument of the HMAC validation function:

```java
is_timed_hmac_valid_v0(
  "mysecretkey",
  http.request.uri,
  100000,
  http.request.timestamp.sec,
  8
)
```

### Concatenated MessageMAC argument

To compose a MessageMAC from more than one field, use the [`concat()`](#concat) function.

This example constructs the value of the `MessageMAC` argument by concatenating the request URI and two header fields:

```java
is_timed_hmac_valid_v0(
  "mysecretkey",
  concat(
    http.request.uri,
    http.request.headers["timestamp"][0],
    "-",
    http.request.headers["mac"][0]),
  100000,
  http.request.timestamp.sec,
  0
)
```
