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:

      FunctionDescriptionUsage in ExpressionTypeExample
      anyReturns true when the comparison operator in the argument returns true for any of the values in the argument array. Returns false otherwise.any(Array<Boolean>)Booleanany(http.request.headers[\"content-type\"][*] eq \"application/x-www-form-urlencoded\")
      allReturns true when the comparison operator in the argument returns true for all values in the argument array. Returns false otherwise.all(Array<Boolean>)Booleanall(http.request.headers[\"content-type\"][*] eq \"application/json\")
      concatTakes a comma-separated list of values. Concatenates the argument values into a single String.concat( String | Integer | Bytes | Array elements)Stringconcat(http.request.uri.path,\"String\") eq \"sampleString\"
      ends_withReturns true when the source ends with the given substring. Returns false otherwise. The source cannot be a literal value (for example, "foo").ends_with(sourceString, substringString) Booleanends_with(http.request.uri.path, \".html\")
      lenReturns the byte length of a String or Bytes field.len(String | Bytes) Integerlen(http.host) lt 20
      lookup_json_integerReturns the integer value associated with the supplied key in field.

      Note: 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. 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.
      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_stringReturns the string value associated with the supplied key in field.

      Note: 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.
      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\"
      lowerConverts a string field to lowercase. Only uppercase ASCII bytes are converted. All other bytes are unaffected.lower(String)Stringlower(http.host) eq \"www.example.com\"
      starts_withReturns true when the source starts with a given substring. Returns false otherwise. The source cannot be a literal value (for example, "foo").starts_with(sourceString, substringString)Booleanstarts_with(http.request.uri.path, \"/blog\")
      substringReturns part of the field value (the value of a String or Bytes field) 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 end index must be greater than the start index. 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.
      substring(fieldString | Bytes, startInteger [, endInteger])String
      substring(http.request.uri.path, 2, 5) eq \"sampleString\"

      substring(http.request.uri.path, 2) eq \"sampleString\"
      upperConverts a string field to uppercase. Only lowercase ASCII bytes are converted. All other bytes are unaffected.upper(String)Stringupper(http.host) eq \"WWW.EXAMPLE.COM\"
      url_decodeDecodes a URL-formatted string defined in source, as in the following:
      - %20 and + decode to a space character ( ).

      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. 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:

      FieldDescriptionUsage in ExpressionTypeExample
      AS NumberThe Autonomous System (AS) number associated with the client IP address.ip.geoip.asnumIntegerip.geoip.asnum eq 12345
      CookieRepresents the entire cookie as a string.http.cookieStringhttp.cookie eq \"session=8521F670545D7865F79C3D7BEDC29CCE;-background=light\"
      CountryRepresents the 2-letter country code in ISO 3166-1 Alpha 2 format.ip.geoip.countryStringnot ip.geoip.country eq \"US\"
      Host NameRepresents the host name used in the full request URI.http.hostStringhttp.host eq \"www.example.com\"
      IP AddressRepresents the client TCP IP address.ip.srcIP addressip.src in {93.184.216.34 192.168.123.132}
      HTTP HeadersRepresents HTTP request headers as a Map (or associative array).http.request.headersMap\String\Arrayany(http.request.headers[\"content-type\"][*] eq \"application/json\")
      URI FullRepresents the full URI as received by the web server.http.request.full_uriStringhttp.request.full_uri eq \"htt­ps://www.example.com/path/index?section=123456&expand=comments\"
      URIRepresents the URI path and query string of the request.http.request.uriStringhttp.request.uri eq \"/path/index?section=123456&expand=comments\"
      URI PathRepresents the URI path of the request.http.request.uri.pathStringhttp.request.uri.path eq \"/path/index\"
      URI Query StringRepresents the entire query string, without the ? delimiter.http.request.uri.queryStringhttp.request.uri.query eq \"section=123456&expand=comments\"
      Raw URI FullSimilar 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_uriStringraw.http.request.full_uri eq \"htt­ps://www.example.com/path/index?section=123456&expand=comments\"
      Raw URISimilar 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.uriStringraw.http.request.uri eq \"/path/index?section=123456&expand=comments\"
      Raw URI PathSimilar 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.pathStringraw.http.request.uri.path eq \"/path/index\"
      Raw URI Query StringSimilar 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.queryStringraw.http.request.uri.query eq \"section=123456&expand=comments\"
      HTTP RefererRepresents the HTTP Referer request header, which contains the address of the web page that linked to the currently requested page.http.refererStringhttp.referer contains \"www.example.com\"
      User AgentRepresents 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_agentStringhttp.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 MethodRepresents the HTTP method, returned as a string of uppercase characters.http.request.methodStringhttp.request.method eq \"GET\"
      ContinentRepresents the continent code associated with client IP address.
      AF – Africa
      AN – Antarctica
      AS – Asia
      EU – Europe
      NA – North America
      OC – Oceania
      SA – South America
      T1 – Tor network
      ip.geoip.continentStringip.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.scoreIntegercf.bot_management.score le 10
      Threat ScoreRepresents 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_scoreIntegercf.threat_score gt 50
      JA3 Fingerprint*Provides an SSL/TLS fingerprint to help you identify potential bot requests.cf.bot_management.ja3_hashStringcf.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 as cf.client.bot.cf.bot_management.verified_botBooleancf.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:
CharacteristicDescriptionUsage in Characteristics Array
IPGroups requests based on the client TCP IP address.ip.src
IP with NAT supportUses 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, or managed_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, or 600 (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 or log action. This parameter may not be provided for rules using a challenge action (legacy_captcha, js_challenge, managed_challenge).
  • The following values are supported: 60, 120, 300, 600, 3600, or 86400 (in seconds).
    • The mitigationTimeout value must also be greater than or equal to the defined period.
  • 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:
FieldDescriptionUsage in Counting ExpressionTypeExample
HTTP Response CodeRepresents the HTTP status code returned to the client.http.response.codeInteger``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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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

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
    • 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
    • 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
  • 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.
    • 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.
  • 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.
  • 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.