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 NameTypeDescription
http.cookieStringEntire cookie as a string Example value:session=8521F670545D7865F79C3D7BEDC29CCE;-background=light
http.hostStringThe host name used in the full request URI Example value:www.example.org
http.refererStringThe HTTP referer header Example value: Referer: https://developer.example.org/en-US/docs/Web/JavaScript
http.request.full_uriStringThe 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.methodStringThe HTTP method, in uppercase Example value: GET
http.request.uriStringThe absolute URI of the request Example value:/articles/index?section=539061&expand=comments
http.request.uri.pathStringThe path of the request Example value: /articles/index
http.request.uri.queryStringThe whole query string, minus the ? delimiter Example value: section=539061&expand=comments
http.user_agentStringThe 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.request.versionNumberThe version of the protocol used for the request

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_forStringThe full X-Forwarded-For HTTP header Example value:X-Forwarded-For: 203.0.113.195, 70.41.3.18
ip.srcIP AddressThe 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 likeX-Forwarded-For or X-Real-IP) Example value:93.184.216.34
ip.geoip.asnumNumberThe 16- or 32-bit integer representing the Autonomous System (AS) number associated with the request
ip.geoip.continentStringThe 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.countryStringThe 2-letter country code in ISO 3166-1 Alpha 2 format. Example value: GB
ip.geoip.subdivision_1_iso_codeStringThe 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_codeStringThe 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_unionBooleanTrue when the request originates from an EU country
sslBooleanTrue when the HTTP connection to the client is encrypted

Argument and value fields for URIs

The following fields represent the URI arguments and values associated with an HTTP request. Many of these fields return arrays containing the respective values.

Field NameTypeDescription
http.request.uri.argsMap <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. namesArray <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. valuesArray <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 fields

The following fields represent the header for an HTTP request. These fields return arrays containing the respective values.

Field NameTypeDescription
http.request.headersMap <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. namesArray <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 matchhttp.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. truncatedBooleanReturns true when HTTP request contained too many headers; otherwise, returns false.

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

Body fields (Enterprise plans only)

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

Field NameTypeDescription
http.request.body. rawStringString representing the unaltered HTTP request body.

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

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

http.request.body.truncatedBooleanReturns 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.formMap <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 ifhttp.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.namesArray <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 ifhttp.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.valuesArray <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 NameTypeDescription
cf.bot_management.verified_bot (Enterprise plans only)BooleanWhen this field is true, Cloudflare has determined the request originates from a known bot or crawler, regardless of good or bad intent.
cf.threat_scoreNumber

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_portNumberThis 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.
cf.client.botBooleanIdentifies whether a client is a known good bot. When traffic comes from a good bot, cf.client.bot is set to true.
cf.worker.upstream_zoneStringIdentifies whether a request comes from a worker or not. If a request comes from a worker this field will hold the name the zone for this worker. Otherwise cf.worker.upstream_zone will be empty

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 NotationData Types SupportedExample (operator in bold)
EnglishC-likeStringIPNumber
Equaleq==http.request.uri.path eq "/articles/2008/"
Not equalne!=ip.src ne 93.184.216.0
Less thanlt<cf.threat_score lt 10
Less than or equalle<=cf.threat_score le 20
Greater thangt>cf.threat_score gt 25
Greater than or equalge>=cf.threat_score ge 60
Exactly containscontainshttp.request.uri.path contains "/articles/"
Matches Google re2 regular expressionmatches~http.request.uri.path matches"^/articles/200[7-8]/$"
Value is in set of valuesinip.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 NOTnot!not ( http.host eq "www.cloudflare.com" and ip.src in 93.184.216.0/24 )1
Logical ANDand&&http.host eq "www.cloudflare.com" and ip.src in 93.184.216.0/242
Logical XOR (Exclusive OR)xor^^http.host eq "www.cloudflare.com" xor ip.src in 93.184.216.0/243
Logical ORor||http.host eq "www.cloudflare.com" or ip.src in 93.184.216.0/244

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:

firewall rules language1

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.

NameArgument TypeReturn TypeNotes
any() 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() 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() 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() 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")