Overview
The idea of the rate limitation feature is to limit the number of specific requests within a specific period.
StackPath’s unique implementation of rate limitation is that it is a conditioning segment within a rule and it can be embedded within any other condition segments.
This guide aims to expand our existing Advanced Rules Guide and focus on rate limitation's advanced functionality.
Rate Limitation Method
rate_limit(
[ip <string>, ...],
url <string>,
interval <int>,
requests <int>,
[method <string>,...],
[status_code <int>,...],
content_type <string>,
scope <string>
)
Returns true if the count of requests(4) under the granularity(8) with the filters(1,2,5,6,7) exceeded the limit for a given time(3).
Parameter | Description |
IP |
List of IP addresses that the rule applies to.
|
URL | Regex pattern to match requests' URI (URL + query_params) against. |
Interval | The time limit, in seconds, within which we are only allowing n number of requests' to URI matching the pattern. |
Requests | The maximum number of requests accepted within the given period before an action is taken (minimum value 20 requests). |
Request Method | List of method types the request aggregation will be applied to. |
Status Code | List of status codes the request aggregation will be applied to. |
Content-Type | Regex pattern to match request content_types against. |
Scope |
If the granularity will not be set to IP, the default aggregation will set to
|
Implementation Methods
Advanced Rule via API
curl --request POST \
--url https://gateway.stackpath.com/waf/v1alpha/stacks/test_stack_id/sites/test_site_id/advanced_rules \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--data '
{
"ruleAction": {"block": {"statusCode": "FORBIDDEN_403"}},
"phase": "ACCESS",
"name": "Block Scrappers",
"description": "Block IPs that hit more than 200 requests per 5 seconds for any `events` paths",
"enabled": false,
"source": "request.rate_limit([], '.*events', 5, 200, [], [], '', 'ip') and not ('mb-web-ui' in request.headers['Cookie'] or 'mb-mobile-ios' in request.headers['Cookie'] or 'mobile-android' in request.headers['Cookie'] or 'mb-mobile-android' in request.headers['Cookie'] or 'session-token' in request.headers['Cookie']) and not request.headers['session']"
}
'
Custom rule via Portal
Best Practices
The following examples will be shown via the Advanced Rules API syntax, as some of the complex condition functionality is not available via Custom Rules:
- Block certain IPs when the number of requests per interval is exceeded for any
events
path - each request will be counted individually for each IP, i.e. if IP'1.2.3.4'
exceeds 200 requests per 5 seconds, it will be blocked, but IP'1.2.3.5'
will not (if it was not exceeded too):
curl --request POST \
--url https://gateway.stackpath.com/waf/v1alpha/stacks/test_stack_id/sites/test_site_id/advanced_rules \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--data '
{
"ruleAction": {"block": {"statusCode": "FORBIDDEN_403"}},
"phase": "ACCESS",
"name": "Limit Certain IPs",
"description": "Limit Certain IPs",
"enabled": false,
"source": "request.rate_limit(['1.2.3.4', '1.2.3.5'], '.*events', 5, 200, [], [], '', 'ip')"
}
'
- Embedding additional conditions into the rate limit feature. This can be done, but note that an additional condition such as an IP is bad practice, for example
request.ip in ['1.2.3.4'] and request.rate_limit([], '', 5, 200, [], [], '', 'ip')
, since the rate limit in this case will count requests per interval for every IP but will set an action only when1.2.3.4
will exceed the number of requests. This is linked to the rule you are creating and not to the rate limit condition, i.e. blank IP list and embedded IP condition, the rate limit will still count for all the IPs requests:
curl --request POST \
--url https://gateway.stackpath.com/waf/v1alpha/stacks/test_stack_id/sites/test_site_id/advanced_rules \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--data '
{
"ruleAction": {"block": {"statusCode": "FORBIDDEN_403"}},
"phase": "ACCESS",
"name": "Embedding additional condition to rate limit feature",
"description": "Embedding additional condition to rate limit feature",
"enabled": false,
"source": "request.headers['User-Agent'] == 'Firefox' AND request.rate_limit(['1.2.3.4', '1.2.3.5'], '.*events', 5, 200, [], [], '', 'ip')"
}
'
- Rate limit complexed URL regex:
curl --request POST \
--url https://gateway.stackpath.com/waf/v1alpha/stacks/test_stack_id/sites/test_site_id/advanced_rules \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--data '
{
"ruleAction": {"block": {"statusCode": "FORBIDDEN_403"}},
"phase": "ACCESS",
"name": "Rate limit complexed URL regex",
"description": "Rate limit complexed URL regex",
"enabled": false,
"source": "request.rate_limit([], '.*(?<!aif|aiff|au|avi|bin|bmp|cab|carb|cct|cdf|class|css|doc|dor|dtd|exe|flv|gcf|gff|gif|grv hdmt hqx|ico|ini|jpeg|jpg|js|mov|mp3|nc|pct|pdf|png|ppc|pws|svg|swa|swf|txt|vbs|w32|wav|wbmp|wml|wmlc|wmls|wmlsc|xsd|zip|webp|jxr|hdp|wdp|webm|ogv|mp4|tif|woff|wot|woff)$', 120, 20, [], [], '', 'ip')"
}
'
- Embedding IP range to the condition:
curl --request POST \
--url https://gateway.stackpath.com/waf/v1alpha/stacks/test_stack_id/sites/test_site_id/advanced_rules \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--data '
{
"ruleAction": {"block": {"statusCode": "FORBIDDEN_403"}},
"phase": "ACCESS",
"name": "Embedding IP range to the condition",
"description": "Embedding IP range to the condition",
"enabled": false,
"source": "request.ip_in_range('10.0.0.0', '10.255.255.255') and request.rate_limit([], '.*[.]jpg', 120, 20, [], [], '', 'ip')"
}
'
- Cluster(POP) granularity - Rate limit all GET or HEAD redirected (status code = 302) requests with specific content type:
curl --request POST \
--url https://gateway.stackpath.com/waf/v1alpha/stacks/test_stack_id/sites/test_site_id/advanced_rules \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--data '
{
"ruleAction": {"block": {"statusCode": "FORBIDDEN_403"}},
"phase": "ACCESS",
"name": "Embedding IP range to the condition",
"description": "Embedding IP range to the condition",
"enabled": false,
"source": "request.rate_limit([], '.*url', 120, 20, ['GET', 'HEAD'], [302], 'text/html; charset=[uU][tT][fF]-8', 'cluster')"
}
'