eCDN Rate Limiting Rules
Rate limiting rules allow you to specify rate limits for requests that match a particular expression and then perform an action when those rate limits are reached. With rate limiting rules, you can improve storefront availability by targeting specific traffic patterns and combating threats, such as bots.
This guide provides an overview on the various rate limiting parameters involved, as well as how to use CDN-API to enable these rules in your zones. Please also refer to the FAQ section at the bottom of this page.
The behavior of a rate limiting rule depends on a number of rate limiting parameters:
- Description
- Rule expression
- Characteristics
- Action
- Period
- Requests per period
- Mitigation timeout (only for Block or Log rules)
- Counting expression (optional)
- Enabled (optional)
- Position (optional)
- Usage in API:
description
- A description of the rate limiting rule.
-
Usage in API:
expression
-
Defines when to apply the rule. The rule expression is evaluated to either true or false. If the rule expression evaluates to true, then the rule is executed. For rate limiting rules, the rule's action is only applied if the rate limiting criteria is met at that time.
-
Comparison operators:
eq
,ne
,contains
,matches
,in
,lt
,le
,gt
,ge
-
Logical operators:
not
,and
,or
,xor
-
Functions:
Function Description Usage in Expression Type Example any Returns true
when the comparison operator in the argument returnstrue
for any of the values in the argument array. Returnsfalse
otherwise.any(Array<Boolean>)
Boolean any(http.request.headers[\"content-type\"][*] eq \"application/x-www-form-urlencoded\")
all Returns true
when the comparison operator in the argument returnstrue
for all values in the argument array. Returnsfalse
otherwise.all(Array<Boolean>)
Boolean all(http.request.headers[\"content-type\"][*] eq \"application/json\")
concat Takes a comma-separated list of values. Concatenates the argument values into a single String. concat( String
|
Integer
|
Bytes
|
Array elements)
String concat(http.request.uri.path,\"String\") eq \"sampleString\"
ends_with Returns true
when the source ends with the given substring. Returnsfalse
otherwise. The source cannot be a literal value (for example,"foo"
).ends_with(sourceString, substringString)
Boolean ends_with(http.request.uri.path, \".html\")
len Returns the byte length of a String or Bytes field. len(String
|
Bytes)
Integer len(http.host) lt 20
lookup_json_integer Returns the integer value associated with the supplied key
infield
.
Note: Thefield
must be a string representation of a valid JSON document. Thekey
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. This function only works for plain integers. For example, it will not work for floating numbers with a zero decimal part such as42.0
.lookup_json_integer(fieldString, keyString
|
Integer [, keyString
|
Integer, …])
Integer lookup_json_integer(http.cookie, \"sampleCookie\") eq 10
lookup_json_integer(http.cookie, 1) eq 10
lookup_json_integer(http.cookie, 1, \"sampleCookie \") eq 10
lookup_json_string Returns the string value associated with the supplied key
infield
.
Note: Thefield
must be a string representation of a valid JSON document. Thekey
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.lookup_json_string(fieldString, keyString
|
Integer [, keyString
|
Integer, …])
String lookup_json_string(http.cookie, \"sampleCookie\") eq \"sampleString\"
lookup_json_string(http.cookie, 1) eq \"sampleString\"
lookup_json_string(http.cookie, 1, \"sampleCookie\") eq \"sampleString\"
lower Converts a string field to lowercase. Only uppercase ASCII bytes are converted. All other bytes are unaffected. lower(String)
String lower(http.host) eq \"www.example.com\"
starts_with Returns true
when the source starts with a given substring. Returnsfalse
otherwise. The source cannot be a literal value (for example,"foo"
).starts_with(sourceString, substringString)
Boolean starts_with(http.request.uri.path, \"/blog\")
substring Returns part of the field
value (the value of a String or Bytes field) from thestart
byte index up to (but excluding) theend
byte index.
The first byte infield
has index0
. If you do not provide the optionalend
index, the function returns the part of the string fromstart
index to the end of the string. Theend
index must be greater than thestart
index. Thestart
andend
indexes can be negative integer values, which allows you to access characters from the end of the string instead of the beginning.substring(fieldString
|
Bytes, startInteger [, endInteger])
String substring(http.request.uri.path, 2, 5) eq \"sampleString\"
substring(http.request.uri.path, 2) eq \"sampleString\"
upper Converts a string field to uppercase. Only lowercase ASCII bytes are converted. All other bytes are unaffected. upper(String)
String upper(http.host) eq \"WWW.EXAMPLE.COM\"
url_decode Decodes a URL-formatted string defined in source
, as in the following:
-%20
and+
decode to a space character (
Theoptions
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. For example,%E2%98%81%EF%B8%8F
will be decoded to a cloud emoji.url_decode(sourceString [, optionsString])
String url_decode(http.request.full_uri) contains \"sampleString\"
url_decode(http.request.full_uri, \"ur\") contains \"sampleString\"
-
Fields:
Field Description Usage in Expression Type Example AS Number The Autonomous System (AS) number associated with the client IP address. ip.geoip.asnum
Integer ip.geoip.asnum eq 12345
Cookie Represents the entire cookie as a string. http.cookie
String http.cookie eq \"session=8521F670545D7865F79C3D7BEDC29CCE;-background=light\"
Country Represents the 2-letter country code in ISO 3166-1 Alpha 2 format. ip.geoip.country
String not ip.geoip.country eq \"US\"
Host Name Represents the host name used in the full request URI. http.host
String http.host eq \"www.example.com\"
IP Address Represents the client TCP IP address. ip.src
IP address ip.src in {93.184.216.34 192.168.123.132}
HTTP Headers Represents HTTP request headers as a Map (or associative array). http.request.headers
Map\String\Array any(http.request.headers[\"content-type\"][*] eq \"application/json\")
URI Full Represents the full URI as received by the web server. http.request.full_uri
String http.request.full_uri eq \"https://www.example.com/path/index?section=123456&expand=comments\"
URI Represents the URI path and query string of the request. http.request.uri
String http.request.uri eq \"/path/index?section=123456&expand=comments\"
URI Path Represents the URI path of the request. http.request.uri.path
String http.request.uri.path eq \"/path/index\"
URI Query String Represents the entire query string, without the ?
delimiter.http.request.uri.query
String http.request.uri.query eq \"section=123456&expand=comments\"
Raw URI Full Similar to the http.request.full_uri
non-raw field. Represents the full URI as received by the web server without the URI fragment (if any) and without any transformation. Note that this raw field may include some basic normalization by the HTTP server.raw.http.request.full_uri
String raw.http.request.full_uri eq \"https://www.example.com/path/index?section=123456&expand=comments\"
Raw URI Similar to the http.request.uri
non-raw field. Represents the URI path and query string of the request without any transformation. Note that this raw field may include some basic normalization by the HTTP server.raw.http.request.uri
String raw.http.request.uri eq \"/path/index?section=123456&expand=comments\"
Raw URI Path Similar to the http.request.uri.path
non-raw field. Represents the URI path of the request without any transformation. Note that this raw field may include some basic normalization by the HTTP server.raw.http.request.uri.path
String raw.http.request.uri.path eq \"/path/index\"
Raw URI Query String Similar to the http.request.uri.query
non-raw field. Represents the entire query string without the?
delimiter and without any transformation. Note that this raw field may include some basic normalization by the HTTP server.raw.http.request.uri.query
String raw.http.request.uri.query eq \"section=123456&expand=comments\"
HTTP Referer Represents the HTTP Referer request header, which contains the address of the web page that linked to the currently requested page. http.referer
String http.referer contains \"www.example.com\"
User Agent Represents the HTTP user agent, a request header that contains a characteristic string to allow identification of the client operating system and web browser. http.user_agent
String http.user_agent eq \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.61 Safari/537.36\"
Request Method Represents the HTTP method, returned as a string of uppercase characters. http.request.method
String http.request.method eq \"GET\"
Continent Represents the continent code associated with client IP address. AF
– AfricaAN
– AntarcticaAS
– AsiaEU
– EuropeNA
– North AmericaOC
– OceaniaSA
– South AmericaT1
– Tor networkip.geoip.continent
String ip.geoip.continent in {\"NA\" \"EU\"}
Bot Score* Represents the likelihood that a request originates from a bot using a score from 1–99. A low score indicates that the request comes from a bot or an automated agent. cf.bot_management.score
Integer cf.bot_management.score le 10
Threat Score Represents a threat score from 0 through 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.threat_score
Integer cf.threat_score gt 50
JA3 Fingerprint* Provides an SSL/TLS fingerprint to help you identify potential bot requests. cf.bot_management.ja3_hash
String cf.bot_management.ja3_hash eq \"e7d705a3286e19ea42f587b344ee6865\"
Verified Bot* When true
, this field indicates the request originated from a known good bot or crawler. Provides the same information ascf.client.bot
.cf.bot_management.verified_bot
Boolean cf.bot_management.verified_bot
-
To use cf.bot.management.*
fields, you must implement your own Cloudflare account (o2o) with Bot Management enabled.
The maximum length of a rate limiting rule expression is 4096 characters.
- Usage in API:
characteristics
- Takes an array of Strings. The characteristics determine how incoming requests are grouped together. Requests with the same values for the defined characteristics are grouped together and share a rate counter.
- For example, say that IP is used as the rule’s characteristics. If the rate of requests coming from a single IP address exceeds the defined limit, then further requests from that IP address are rate limited. Requests coming from other IP addresses do not share the same rate counter. In other words, the request rate of one IP address does not affect the request rate of other IP addresses.
- The following characteristics are supported:
Characteristic | Description | Usage in Characteristics Array |
---|---|---|
IP | Groups requests based on the client TCP IP address. | ip.src |
IP with NAT support | Uses privacy-preserving techniques to identify unique visitors, including in situations such as requests under NAT sharing the same IP address. | cf.unique_visitor_id |
-
Please note that you cannot use both IP with NAT support and IP as characteristics of the same rate limiting rule. Therefore, the API usage examples are as follows:
"characteristics": ["ip.src"]
or
"characteristics": ["cf.unique_visitor_id"]
Our recommendation is to use IP with NAT support. Please see the FAQ section for more details.
- Usage in API:
action
- The action to perform when the rate limiting criteria is met.
- The following actions are supported:
block
,log
,legacy_captcha
,js_challenge
, ormanaged_challenge
- See documentation here for more info on each action.
- Usage in API:
period
- The period of time to consider when evaluating the request rate.
- The following values are supported:
10
,60
,120
,300
, or600
(in seconds).
- Usage in API:
requestsPerPeriod
- Any positive integer value. Represents the limit for the number of requests in the specified period of time.
- Usage in API:
mitigationTimeout
- The “timeout” time. Once the rate limiting criteria is met, the rate limiting rule continues to apply the action to further requests matching the rule expression for the period of time defined in this field.
- Only relevant for rules using the
block
orlog
action. This parameter may not be provided for rules using a challenge action (legacy_captcha
,js_challenge
,managed_challenge
).
- Only relevant for rules using the
- The following values are supported:
60
,120
,300
,600
,3600
, or86400
(in seconds).- The
mitigationTimeout
value must also be greater than or equal to the definedperiod
.
- The
- Usage in API:
countingExpression
- Defines the request criteria, which we want to rate limit. In other words, this parameter defines when to increment the rate counter. The rate counter keeps track of the number of requests per period. When the rate counter exceeds the defined requests per period limit we have set, the rule’s action is executed.
- If the counting expression is not provided, then the counting expression is the same as the rule expression.
- The counting expression supports the option to rate limit based on HTTP response code. This is not possible when using only the rule expression. The counting expression should be used if you wish to rate limit based on the HTTP response code. Please refer to the FAQ for additional examples of when to use the counting expression.
- The validation for the counting expression is the same as the rule expression, with the addition of the following field:
Field | Description | Usage in Counting Expression | Type | Example |
---|---|---|---|---|
HTTP Response Code | Represents the HTTP status code returned to the client. | http.response.code | Integer | ``http.response.code eq 400 |
- Usage in API:
enabled
- Whether the rate limiting rule is enabled. Default value is
true
.
-
Usage in API:
position
-
Providing this parameter inserts the rule at a certain relative position in the ruleset. This parameter may be used when creating or updating a rate limiting rule.
- We validate that the provided index matches a rule in the current ruleset. If the position is not provided when creating a rate limiting rule, the rule is added to the end of the ruleset by default.
The following section covers common use cases for rate limiting rules. You may use these examples, combine them as needed, and adjust them to your own business needs.
- Enforce granular access control to resources, including access control based on criteria such as user agent, IP address, host, country, and world region.
- Example A: Limit the rate of requests performed by individual user agents.
- Rule expression:
http.user_agent eq "MobileApp"
- Rate: 100 requests / 10 minutes
- Action: Managed Challenge
- Rule expression:
- Example B: Exclude or include specific IP addresses or ASNs from a rate limiting rule.
- Rule expression:
http.request.uri.path eq "/status" and http.request.method eq "GET" and not ip.src in { <defined IPs> }
- Rate: 10 requests / 1 minute
- Action: Managed Challenge
- Rule expression:
- Protect against credential stuffing and account takeover attacks. A typical use case is to protect a login endpoint. You may also create multiple rate limiting rules to define increasing penalties to manage clients making too many requests.
- Example A: Protect against too many failed login attempts.
- Rule expression:
http.host eq "example.com" and http.request.uri.path eq "/login" and http.request.method eq "POST"
- Counting expression:
http.request.uri.path eq "/login" and http.request.method eq "POST" and http.response.code in {401 403}
- Rate: 5 requests / 1 minute
- Action: Managed Challenge
- Rule expression:
- Example B: Create another protection layer for if a user were to pass the challenge presented by the rule defined in Example A. If the user exceeds this second threshold, they are blocked from accessing the login page for 10 minutes.
- Rule expression:
http.host eq "example.com" and http.request.uri.path eq "/login"
- Counting expression:
http.request.uri.path eq "/login" and http.request.method eq "POST" and http.response.code in {401 403}
- Rate: 20 requests / 10 minutes
- Action: Block for 10 minutes
- Rule expression:
- Limit the number of operations performed by individual clients. Prevent scraping by bots, accessing sensitive data, bulk creation of new accounts, and programmatic buying.
- Example A: Prevent content scraping (via query string).
- Rule expression:
http.request.uri.path eq "/merchant" and http.request.uri.query contains "action=lookup_price"
- Rate: 10 requests / 1 minute
- Action: Managed Challenge
- Rule expression:
- Example B: Limit requests from bots. A general approach to identify traffic from bots is to rate limit requests that trigger a large volume of error response status codes.
- Rule expression:
http.host eq "example.com"
- Counting expression:
http.response.code in {401 403}
- Rate: 25 requests / 2 minutes
- Action: Managed Challenge
- Rule expression:
- Example C: Consider using the
bot_management
fields if your zone has Bot Management entitled.- Rule expression:
cf.bot_management.score lt 30 and http.request.uri.query contains "action=delete"
- Rate: 20 requests / 5 minutes
- Action: Block for 1 hour
- Rule expression:
The following sections contain common use cases.
This endpoint creates a rate limiting rule in the specified zone.
- Please refer to the Rate Limiting Parameters section above for more information on parameter validation.
Newly created rules are enabled by default and added to the end of the ruleset unless specified otherwise. A maximum of 3 rate limiting rules is allowed in a single zone.
Sample Success Response - 201 HttpStatus Code Response body contains the rate limiting rule that was created.
This endpoint returns all of the rate limiting rules in the specified zone in order of priority. If no rate limiting rules exist, a 404 Not Found response is returned.
Sample Success Response - 200 HttpStatus Code Response body contains all of the current rate limiting rules in the specified zone.
This endpoint returns the requested rate limiting rule. If the requested rule does not exist, a 404 Not Found response is returned.
Sample Success Response - 200 HttpStatus Code Response body contains the requested rate limiting rule.
This endpoint updates the requested rate limiting rule. If the requested rule does not exist, a 404 Not Found response is returned.
- You must provide at least one rate limiting parameter in the request body.
- In the scenario where you have set the
countingExpression
but wish to remove it (and thus have it default to the rule expression), you must include"countingExpression": ""
in the request body to reset the value.
Sample Success Response - 200 HttpStatus Code Response body contains the requested rate limiting rule.
This endpoint deletes the requested rate limiting rule. If the requested rule does not exist, a 404 Not Found response is returned.
Sample Success Response - 204 HttpStatus Code (No Content)
- How does the counting expression differ from the rule expression?
- The rule expression defines when to evaluate the rate limiting rule. If a request matches the rule expression, then the rate limiting rule is evaluated. When we evaluate a rate limiting rule, we check if the rate counter exceeds the defined requests per period. If so, then the rule’s action is applied.
- The counting expression defines the kind of requests we want to rate limit. If a request matches the counting expression, then the rate counter is incremented. The counter keeps tracks of how many requests have met the rate limiting criteria.
- When should I use the counting expression?
- In most use cases, you do not need to provide the counting expression as it is sufficient to have the counting expression be the same as the rule expression.
- Example A: Rate limit high traffic going to a particular path. If the number of requests surpasses 10,000 requests in 1 minute, then block further requests to that path for 10 minutes.
- Rule expression:
http.request.uri.path eq "/path"
- Counting expression (by default):
http.request.uri.path eq "/path"
- Rate: 10000 requests / 1 minute
- Action: Block for 10 minutes
- Rule expression:
- Example A: Rate limit high traffic going to a particular path. If the number of requests surpasses 10,000 requests in 1 minute, then block further requests to that path for 10 minutes.
- In some cases, we want to rate limit based on the HTTP response code. For these use cases, you must provide the counting expression as it offers the additional response code field. Rate limiting based on HTTP response code is not possible when using only the rule expression.
- Example B: Prevent automated actors from overloading a form with malicious traffic by presenting them with a managed challenge. Rate limit traffic going to a particular form that returns a 400 response code.
- Rule expression:
http.request.uri.path eq "/form"
- Counting expression:
http.request.uri.path eq "/form" and http.response.code eq 400
- Rate: 15 requests / 5 minutes
- Action: Managed Challenge
- Rule expression:
- Example B: Prevent automated actors from overloading a form with malicious traffic by presenting them with a managed challenge. Rate limit traffic going to a particular form that returns a 400 response code.
- Another example for where you may want the counting expression to differ from the rule expression is the following:
- Example C: Protect a login endpoint from malicious actors. Once a user makes 15 bad login requests in 10 minutes, block them from visiting the entire site for 1 hour.
- Rule expression:
http.host eq "example.com"
- Counting expression:
http.request.uri.path eq "/login" and http.request.method eq "POST" and http.response.code in {401 403}
- Rate: 15 requests / 10 minutes
- Action: Block for 1 hour
- Rule expression:
- Example C: Protect a login endpoint from malicious actors. Once a user makes 15 bad login requests in 10 minutes, block them from visiting the entire site for 1 hour.
- In most use cases, you do not need to provide the counting expression as it is sufficient to have the counting expression be the same as the rule expression.
- How do the characteristics work?
- Separate rate counters are kept for each value combination of the defined rule characteristics. In other words, the characteristics determine how incoming requests are grouped together.
- Consider a rule configured with the following characteristics:
- IP address
- HTTP header
x-api-key
- In this case, two incoming requests with the same value for the HTTP header
X-API-Key
but different IP addresses are counted separately, since the value combination is different. Additionally, counters are not shared across data centers.
- Consider a rule configured with the following characteristics:
- Please note that CDN-API currently only accepts IP or IP with NAT support as characteristics, and that these two characteristics cannot be used in the same rate limiting rule. Therefore, the usage example presented above is not relevant at this time, but serves as example to help understand how characteristics work.
- Separate rate counters are kept for each value combination of the defined rule characteristics. In other words, the characteristics determine how incoming requests are grouped together.
- Why is it recommended to use IP with NAT support?
- We recommend using IP with NAT support because it is able to identify unique visitors even in situations where requests may come into the eCDN under the same IP address.
- Using IP with NAT support is necessary for customers who have stacked eCDN configurations. This is because requests route through the stacked provider before hitting the eCDN. Using IP with NAT support ensures that each unique visitor is properly identified and that the stacked provider source IPs are not rate limited.
- Why does rule order matter?
- Rate limiting rules are evaluated in the order they are returned in the response body, starting with the first rule listed. To update the priority of a particular rule, you may use the PATCH endpoint and provide the
position
parameter in the request body. - If a request meets the rate limiting criteria and triggers the rate limiting rule to be executed, the rule’s action is executed and no other rate limiting rules are evaluated for that particular request. When the next request comes in, rule evaluation starts back at the top of the ruleset.
- Rate limiting rules are evaluated in the order they are returned in the response body, starting with the first rule listed. To update the priority of a particular rule, you may use the PATCH endpoint and provide the
- How do I construct the rule expression?
- Please see the documentation here for more information regarding constructing rule expressions.
- How do I use a particular field in the rule expression?
- Please see the documentation here for more information on a particular field’s usage.