Firewall Rules language

Fields

When an HTTP request reaches Cloudflare’s edge, we create a table of field–value pairs against which to match expressions. This table exists for as long as the current request is being processed.

Field values come from a variety of sources:

  • Primitive properties obtained directly from the request (http.request.uri.path, for example).
  • Derived values resulting from a transformation, composition, or basic operation. For example, the transformation lower(http.request.uri.patch) converts the value of http.request.uri.path to lowercase.
  • Computed values that are the product of a lookup, computation, or other intelligence. For example, Cloudflare uses a machine learning process to dynamically calculate threat scores, represented by the cf.threat_scorefield.

Standard fields

Standard fields generally adopt the naming convention of the analogous Wireshark display fields; however, there are some subtle differences:

  • Wireshark supports CIDR (Classless Inter-Domain Routing) notation for expressing IP address ranges in equality comparisons (ip.src == 1.2.3.0/24, for example); Cloudflare does not. Use the in comparison operator to evaluate a range of addresses using CIDR notation.

    Example:
    ip.src in {1.2.3.0/24 4.5.6.0/24}.

  • In Wireshark, ssl is a protocol field containing hundreds of other fields of various types that are available for comparison in multiple ways. However, in Firewall Rules ssl is a single boolean field that indicates whether the connection from the client to Cloudflare is encrypted.
  • Cloudflare does not support the slice operator.

These are the standard fields supported by Firewall Rules:


Field Name Type Description
http.cookie String Entire cookie as a string

Example value:

session=8521F670545D7865F79C3D7BEDC29CCE;-background=light

http.host String The host name used in the full request URI

Example value:

www.example.org

http.referer String The HTTP referer header

Example value:

Referer: https://developer.example.org/en-US/docs/Web/JavaScript

http.request.full_uri String The full URI as received by the web server (does not include #fragment, which is not sent to web servers)

Example value:

htt­ps://www.example.org/articles/index?section=539061&expand=comments

http.request.method String The HTTP method, in uppercase

Example value:

GET

http.request.uri String The absolute URI of the request

Example value:

/articles/index?section=539061&expand=comments

http.request.uri.path String The path of the request

Example value:

/articles/index

http.request.uri.query String The whole query string, minus the ? delimiter

Example value:

section=539061&expand=comments

http.user_agent String The HTTP user agent

Example value:

Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36

http.version (Enterprise plans only) Number The version of the protocol used

This can be useful when different checks are required for different versions of the protocol.

Example Values:

  • HTTP/1.1
  • HTTP/3
http.x_forwarded_for String The full X-Forwarded-For HTTP header

Example value:

X-Forwarded-For: 203.0.113.195, 70.41.3.18

ip.src IP Address The client TCP IP address, which may be adjusted to reflect the real client IP of the original client as applicable (i.e. using HTTP headers like X-Forwarded-For or X-Real-IP)

Example value:

93.184.216.34

ip.geoip.asnum Number The 16- or 32-bit integer representing the Autonomous System (AS) number associated with the request
ip.geoip.continent String The continent code for this location Possible codes are:
  • AF – Africa
  • AN – Antarctica
  • AS – Asia
  • EU – Europe
  • NA – North America
  • OC – Oceania
  • SA – South America
  • T1 – Tor network
ip.geoip.country String The 2-letter country code in ISO 3166-1 Alpha 2 format.

Example value:

GB

ip.geoip.subdivision_1_iso_code String The ISO 3166-2 code for the first level region associated with the IP address. If the actual value is not available, this field will contain an empty string.

Example value:

GB-ENG

ip.geoip.subdivision_2_iso_code String The ISO 3166-2 code for the second level region associated with the IP address. If the actual value is not available, this field will contain an empty string.

Example value:

GB-SWK

Ip.geoip.is_in_european_union Boolean True when the request originates from an EU country
ssl Boolean True when the HTTP connection to the client is encrypted

Argument and value fields for URIs (Enterprise plans only)

The following fields represent the URI arguments and values associated with an HTTP request. Many of these fields return arrays containing the respective values. URI argument and value fields are only available to Enterprise plans.

Field Name Type Description
http.request.uri.args Map <Array> <String>> HTTP URI arguments represented in a map (or associative array)

If an argument is repeated, then the array will contain multiple items in the order they appear in the request. Values are not pre-processed and retain the case used in the request.

Decoding: no decoding Non-ascii: preserved

Example:
any(http.request.uri.args["search"][*] == "red+apples")

Example value:
{"search": ["red+apples"]}

http.request.uri.args. names Array <String> Names of arguments in the HTTP URI query string

Names are not pre-processed and retain the case used in the request.

If a name is repeated, then the array will contain multiple items in the order they appear in the request. Decoding: no decoding is performed Non-ascii: preserved

Example:
any(http.request.uri.args.names[*] == "search")

Example value:
["search"]

http.request.uri.args. values Array <String> Values of arguments in the HTTP URI query string

Values are not pre-processed and retain the case used in the request. They are in the same order as in the request. Duplicated values are listed multiple times. Decoding: no decoding Non-ascii: preserved

Example:
any(http.request.uri.args.values[*] == "red+apples")

Example value:
["red+apples"]

Header and body fields (Enterprise plans only)

The following fields represent the header and body for an HTTP request. These fields return arrays containing the respective values. Header and body fields are only available to Enterprise plans.

Field Name Type Description
http.request.headers Map<Array<<String>>

HTTP request headers represented in a map (or associative array). If headers are repeated, then the array will contain multiple items in the order they appear in the request. The keys will be converted to lowercase. Decoding: no decoding Whitespace: preserved Non-ascii: preserved

Example:
any(http.request.headers["content-type"][*] == "application/json")

Example value:
{ "content-type": ["application/json"] }

http.request.headers. names Array <String> Names of headers in the HTTP request.

The names are not pre-processed and retain the case used in the request. The order of header names is not guaranteed but will match http.request.headers. values. Duplicate headers are listed multiple times. Decoding: no decoding Whitespace: preserved Non-ascii: preserved

Example:
any(http.request.headers.names[*] == "content-type")

Example value:
["content-type"]

http.request.headers.values <Array> <String>

Values of headers in the HTTP request. Values are not pre-processed and retain the case used in the request. The order of header values is not guaranteed but will match http.request.headers. names. Duplicate headers are listed multiple times. Decoding: no decoding Whitespace: preserved Non-ascii: preserved

Example:
any(http.request.headers.values[*] == "application/json")

Example value:
["application/json"]

http.request.headers. truncated Boolean Returns true when HTTP request contained too many headers; otherwise, returns false.

If true, http.request.headers, http.request.headers.names, and http.request.headers.values may not contain all of the headers sent in the HTTP request.

http.request.body. raw String String representing the unaltered HTTP request body.

The return value may be truncated if the value of http.request.body.truncated is true.

Decoding: no decoding
Whitespace: preserved
Non-ascii: preserved

http.request.body.truncated Boolean Returns true if the HTTP request body was too long; otherwise, returns false.

When true, http.request.body.\* fields may not contain all of the HTTP request body.

http.request.body.form Map <Array><String>>

HTTP body represented in a map (or associative array). Populated if the Content-Type header is application/x-www-form- urlencoded. The values are not pre-processed and retain the case used in the request. If a field is repeated, then the array will contain multiple items in the order they are in the request. The return value may be truncated if http.request.body.truncated is true.

Decoding: no decoding
Whitespace: preserved
Non-ascii: preserved

Example:
any(http.request.body.form["username"][*] == "admin")

Example value:
{ "username": ["admin"] }

http.request.body. form.names Array <String> Names of form fields in the HTTP request

Names are not pre-processed and retain the case found in the request. They are listed in the same order as in the request. Duplicate names are listed multiple times. The return value may be truncated if http.request.body.truncated is true.

Decoding: no decoding
Whitespace: preserved
Non-ascii: preserved

Example:
any(http.request.body.form.names[*] == "username")

Example value:
["username"]

http.request.body. form.values Array <String>

Values of form fields in the HTTP request. The values are not pre-processed and retain the case used in the request. They are in the same order as in the request. Duplicated values are listed multiple times. The return value may be truncated if http.request.body.truncated is true.

Decoding: no decoding
Whitespace: preserved
Non-ascii: preserved

Example:
any(http.request.body.form.values[*] == "admin")

Example value:
["admin"]

Dynamic fields

In addition to the standard fields outlined above, Cloudflare supports the following dynamic fields, representing intelligence about the potential threat posed by the request:

Field Name Type Description
cf.bot_ management. verified_bot Boolean When this field is true, Cloudflare has determined the request originates from a known bot or crawler, regardless of good or bad intent.
cf.threat_score Number

Represents a Cloudflare threat score from 0–100, where 0 indicates low risk. Values above 10 may represent spammers or bots, and values above 40 identify bad actors on the internet. It is rare to see values above 60. A common recommendation is to challenge requests with a score above 10 and to block those above 50.

cf.edge.server_port (Enterprise plans only) Number

This is the port number at which Cloudflare's network received the request. Useful for specifying traffic on a specific port. The value is a port number in the range 1-65535.


Operators

Comparison operators

This table lists supported comparison operators. Since some operators only support specific data types, the list is broken down by data type.

Comparison Operators Supported in Firewall Rules
Operator Notation Data Types Supported Example (operator in bold)
English C-like String IP Number
Equal eq == http.request.uri.path eq "/articles/2008/"
Not equal ne != ip.src ne 93.184.216.0
Less than lt < cf.threat_score lt 10
Less than or equal le <= cf.threat_score le 20
Greater than gt > cf.threat_score gt 25
Greater than or equal ge >= cf.threat_score ge 60
Exactly contains contains http.request.uri.path contains "/articles/"
Matches Google re2 regular expression matches ~ http.request.uri.path matches "^/articles/200[7-8]/$"
Value is in set of values in ip.src in { 93.184.216.0 93.184.216.1 }

Logical operators

Firewall Rules supports the following logical operators:

Logical Operators Supported in Firewall Rules
English

Notation

C-like

Notation

Example (operator in bold) Order of Precedence
Logical NOT not ! not ( http.host eq "www.cloudflare.com" and ip.src in 93.184.216.0/24 ) 1
Logical AND and && http.host eq "www.cloudflare.com" and ip.src in 93.184.216.0/24 2
Logical XOR (Exclusive OR) xor ^^ http.host eq "www.cloudflare.com" xor ip.src in 93.184.216.0/24 3
Logical OR or || http.host eq "www.cloudflare.com" or ip.src in 93.184.216.0/24 4

Order of evaluation and precedence for logical operators

Note in the above table that each logical operator is associated with an order of precedence, which determines the order in which the operators are evaluated. When writing compound expressions, it is important to be aware of the precedence of logical operators so that your expression is evaluated the way you expect.

For example, consider the following generic expression (operators are in bold for clarity):

Expression1 and Expression2 or Expression3

Without precedence, this compound expression is ambiguous – it’s not clear which of the two interpretations is correct:

  1. Match when Expression 1 and Expression 2 are both true or when Expression 3 is true.
  2. Match when Expression 1 is true and either Expression 2 or Expression 3 is true.

However, since the logical AND operator has precedence over logical OR, we know the AND operator must be evaluated first. Thus, Interpretation 1 is correct.


Grouping symbols

An advantage of the Expression Editor is that it supports parentheses as grouping symbols, so you can explicitly group expressions that should be evaluated together:

(Expression1 and Expression2) or Expression3

In this case, the addition of parentheses does not change how the compound expression is evaluated; it simply reinforces what we established earlier with operator precedence. However, because parentheses unambiguously call out which logical operators to evaluate first, you are less likely to make errors if you use them when writing your own compound expressions.

You can also use parentheses to _force _the order of evaluation. Consider the following example, which forces the logical OR operator to be evaluated before the logical AND:

Expression1 and (Expression2 or Expression3)

Without parentheses, the logical AND operator would take precedence.

You can nest expressions grouped by parentheses inside other groups to create very precise but complex expressions, such as in this example for a rule designed to block access to a domain (parentheses and logical operators in bold):

(
 (http.host eq "api.example.com" and http.request.uri.path eq "/api/v2/auth") or
 (http.host matches "^(www|store|blog)\.example.com" and http.request.uri.path contains "wp-login.php") or 
 ip.geoip.country in {"CN" "TH" "US" "ID" "KR" "MY" "IT" "SG" "GB"} or 
 ip.geoip.asnum in {12345 54321 11111}
) and not ip.src in {11.22.33.0/24}

Note that when evaluating the precedence of logical operators, parentheses inside strings are ignored, such as those in the following regular expression, drawn from the example above:

"^(www|store|blog)\.example\.com"

Grouping expressions to force precedence is an extremely powerful tool. Currently, only the Expression Editor and the Cloudflare API support this feature; the Expression Builder does not.


Values

Working with String values

Evaluation of expressions using String values is case-sensitive, so you may need to write more than one simple expression to capture variants. Cloudflare Business and Enterprise customers can use the matches operator with a Google RE2 regular expression to capture multiple variations of a string pattern with a single expression.

Escaping characters in values

You must manually escape the backslash (\) and double quote (“) characters with a backslash when using them as part of a literal value in an expression. You can automatically escape these characters in the Expression Builder, which prepends a backslash so that \ and " become\\ and \", respectively.

The following image highlights how double quotes used in a literal value are automatically escaped to \" in the Expression Builder:

In the Expression Preview, note the double quotes that delimit the string value itself. The first and last quotes in **“token-type=\“JWT\“”) are not escaped, because they are not part of the literal value.

Working with boolean values

Simple expressions using boolean fields do not require any operator notation, and they do not require a value. Simply asserting the field on its own is sufficient, as in the following ssl example:

ssl

Yes, this is a valid simple expression that matches requests where the value of the ssl field is true.

Use the boolean not operator to match requests where ssl is false:

not ssl

Transformation functions

The Firewall Rules Language supports a number of functions that transform values extracted from HTTP requests. For example, the lower() function takes a string as an argument and converts all uppercase characters to lowercase. In the expression below, thelower() function is used to transform http.host values to lowercase so that they can be compared to the target value "www.cloudflare.com".

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

This example illustrates a common use case for transformation functions – creating case-insensitive expressions. The use of the transformation function effectively overrides Firewall Rules’ otherwise case-sensitive evaluation of strings.

Supported transformation functions

Firewall Rules supports the following transformation functions. Several functions are available only to Enterprise plans.

Name Argument Type Return Type Notes
any()

(Enterprise plans only)

Array <Boolean> Boolean

Uses the [*] array operator, which takes each value in the array field and returns an array of those values. The any() function returns true if the comparison operator in the argument returns true for any of the values in the array. Returns false otherwise.

Example:
any(http.request.headers.values[*] contains "foo")

all()

(Enterprise plans only)

Array <Boolean> Boolean

Uses the [*] array operator, which takes each value in the array field and returns an array of those values. The all() function returns true if the comparison operator in the argument returns true for all of the values in the array. Returns false otherwise.

Example:
all(http.request. Headers['content-type'][*] == "application/json")

len() (Enterprise plans only) String, Bytes int

Returns the byte length of a String or Bytes field.

Example:
len(http.host)

lower() String String

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

Example:
lower(http.host) == "www.cloudflare.com"

upper() String String

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

Example:
upper(http.host) == "WWW.CLOUDFLARE.COM"

url_decode()

(Enterprise plans only)

String String Decodes a URL formatted string.For example:
  • '%20' and '+'decode to '' (space characters)
  • '%E4%BD%A0' decodes to '你'

Example:

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