Subchapter 96.3
references/adding-rate-based-rules.mdMarkdown10 KBView on GitHub
Domain expertise for throttling HTTP floods and brute force with AWS WAF rate-based rules. Covers the aggregation key choice, the request floor and the allowed evaluation windows, the small cap on rate-based rules per web ACL and the composite-key way around rule sprawl, scope-down to limit a subset of requests, and the Count-mode-first path that also matters for Shield cost-protection eligibility.
Does not cover managed rules, match rules, bot, or fraud rule groups; those are separate references. Account-based abuse (credential stuffing, fake accounts) goes to the fraud control reference, not here.
Execute commands using the AWS MCP server when connected (sandboxed execution, audit logging, observability). Fall back to the AWS CLI otherwise.
To add a rate-based rule end to end, follow the procedure exactly. See the Procedure section below.
The procedure covers:
| Key | Use when | Avoid when |
|---|---|---|
IP | Direct client connections, no proxy or CDN | Behind a CDN; all requests collapse to the CDN IP |
FORWARDED_IP | A trusted proxy forwards the real client IP in a header | The header is absent or attacker-controlled |
CUSTOM_KEYS | Per-user or per-tenant limits (API key, session, user ID); up to 5 components | The key field is absent on many requests |
CONSTANT | A hard ceiling on total requests to a path; always requires scope-down | Per-client limits are needed |
Constraints:
FORWARDED_IP (not IP) when the application sits behind a CDN, or the limit
acts on the CDN address (see the recovering-the-real-client-IP reference)CONSTANT; the API rejects it otherwiseCustomers assume an arbitrary threshold and window and get surprised by the constraints.
Constraints:
A web ACL allows only a small number of these high-cost rules (roughly ten). Customers building one rate-based rule per URI path hit the cap.
Constraints:
CUSTOM_KEYS (for example IP plus API key) and a
scope-down statement, rather than rule sprawlCustomers expect a rate-based rule to count only a specific path, then find it counting all traffic, because they did not scope it down.
Constraints:
LOWERCASE text transformation on the path match so casing does not cause the
scope-down to missTurning a new rate-based rule straight to Block catches legitimate bursts. Leaving it in Count has a second consequence on CloudFront and Application Load Balancer resources.
Constraints:
The scope-down is not matching (often a casing issue), or the threshold is too high. Add
LOWERCASE, or remove the scope-down to test (Scope-down to a subset of requests).
CONSTANT was used without a scope-down statement, or the window is not one of the allowed values
(Request floor and evaluation windows).
Traffic arrives via a proxy and the rule reads the proxy IP. Switch to FORWARDED_IP (Decision:
aggregation key).
This procedure adds a rate-based rule with a measured threshold, a valid window, and a scope-down, runs it in Count, then switches to Block, and surfaces the console link.
IP, FORWARDED_IP, CUSTOM_KEYS, or CONSTANT.aggregation_key is CUSTOM_KEYS): Up to 5 key
components (for example IP, a header name such as x-api-key) used for composite aggregation.CONSTANT, recommended otherwise): The statement narrowing which
requests are counted.Constraints for parameter acquisition:
Constraints:
aws sts get-caller-identityConstraints:
You MUST add the rule with a valid window and a limit at or above 10, in Count mode
You MUST fetch the current LockToken with get-web-acl immediately before update-web-acl and
pass the full rule set
You MUST preserve the web ACL’s existing DefaultAction from the get-web-acl response and pass
it back as {default_action}; do not assume Allow={}, since that would silently open all
unmatched traffic on a web ACL whose default action is Block:
aws wafv2 get-web-acl --name {web_acl_name} --scope {scope} --id {web_acl_id} --region {region}
aws wafv2 update-web-acl --name {web_acl_name} --scope {scope} --id {web_acl_id} \
--lock-token {lock_token} --default-action {default_action} \
--rules '[{"Name":"RateLimit","Priority":1,"Action":{"Count":{}},"Statement":{"RateBasedStatement":{"Limit":{limit},"EvaluationWindowSec":{window},"AggregateKeyType":"CUSTOM_KEYS","CustomKeys":[{"Header":{"Name":"x-api-key","TextTransformations":[{"Priority":0,"Type":"NONE"}]}},{"IP":{}}]}},"VisibilityConfig":{"SampledRequestsEnabled":true,"CloudWatchMetricsEnabled":true,"MetricName":"RateLimit"}}]' \
--visibility-config SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName={web_acl_name} \
--region {region}You MUST include a scope-down statement for CONSTANT, and use composite CUSTOM_KEYS rather
than many per-path rules
Constraints:
update-web-acl with the
rule’s "Action" changed from {"Count":{}} to {"Block":{}} (fetch a fresh LockToken first
and pass the full rule set)Constraints:
You MUST present the web ACL console link and tell the customer to open it and confirm the rate-based rule and its action:
https://us-east-1.console.aws.amazon.com/wafv2/homev2/web-acls?region={region}{
"web_acl_name": "example-webacl",
"web_acl_id": "abc",
"scope": "REGIONAL",
"aggregation_key": "CUSTOM_KEYS",
"custom_key_components": ["IP", "x-api-key"],
"limit": 20,
"window": 300,
"scope_down": "URI path starts with /login"
}Added a CUSTOM_KEYS (IP + x-api-key) rate-based rule, limit 20 per 300s, scoped to /login, in Count.
Confirmed the threshold does not catch legitimate logins, switched to Block.
Open the web ACL and confirm the rate-based rule and action:
https://us-east-1.console.aws.amazon.com/wafv2/homev2/web-acls?region=us-east-1Scope-down casing or threshold too high. Add LOWERCASE or lower the limit (Step 2).
CONSTANT without scope-down, or an invalid window. Fix both (Step 2).
This procedure modifies a security control, so misconfiguration directly weakens the application’s defenses.
wafv2: actions a task needs (for example wafv2:GetWebACL and wafv2:UpdateWebACL) rather than wafv2:* or the AWSWAFFullAccess managed policy.aws sts assume-role) rather than long-lived IAM user access keys when running these WAF CLI commands.wafv2 management events and set CloudWatch alarms on critical web ACL configuration changes (such as DeleteWebACL and UpdateWebACL rule removals) and on the web ACL’s BlockedRequests and CountedRequests metrics, so rule changes and sudden spikes in blocked or counted traffic are detected.