Every WAF vendor has different syntax for the same rate limiting concept. This is a reference for engineers who need to implement identical rate limiting policies across different platforms -- whether because an organization uses multiple WAFs across its stack, or because a DDactic assessment recommendation needs to be translated into the specific syntax the target environment uses.
The scenario used throughout: rate limit the REST API endpoint /api/v1/transactions to 30 POST requests per minute per authenticated user identifier, with a 1-hour block on violation, returning 429 with a Retry-After: 60 header.
Cloudflare WAF (Rules Engine)
# Cloudflare — Rate limit /api/v1/transactions POST to 30/min per user token
# Rule expression (WAF Custom Rules):
(http.request.uri.path eq "/api/v1/transactions" and http.request.method eq "POST")
# Rate limiting action settings:
# Characteristics: http.request.headers["authorization"]
# (hash of the Authorization header value, stable per token)
# Period: 60 seconds
# Requests per period: 30
# Mitigation action: Block
# Mitigation timeout: 3600 seconds
# Response: 429
# Add Retry-After header via Transform Rule:
# (http.response.code eq 429)
# Set: Retry-After = "60"
# Via Terraform (cloudflare_ruleset):
resource "cloudflare_ruleset" "api_rate_limit" {
zone_id = var.zone_id
name = "API Rate Limiting"
kind = "zone"
phase = "http_ratelimit"
rules {
expression = "(http.request.uri.path eq \"/api/v1/transactions\" and http.request.method eq \"POST\")"
action = "block"
ratelimit {
characteristics = ["http.request.headers[\"authorization\"]"]
period = 60
requests_per_period = 30
mitigation_timeout = 3600
}
}
}
AWS WAF v2
# AWS WAF v2 — Rate limit via rate-based rule
# Limitation: minimum window is 300 seconds (5 min); set limit at 5x per-minute value
# For 30/min: set limit = 150 over 300 seconds
aws wafv2 create-rule-group \
--name "TransactionRateLimit" \
--scope REGIONAL \
--capacity 25 \
--rules '[{
"Name": "LimitTransactionPost",
"Priority": 1,
"Action": {"Block": {"CustomResponse": {
"ResponseCode": 429,
"ResponseHeaders": [{"Name":"Retry-After","Value":"60"}]
}}},
"Statement": {
"RateBasedStatement": {
"Limit": 150,
"AggregateKeyType": "HEADER",
"AggregateKeyConfig": {
"Header": {"Name": "Authorization", "OversizeHandling": "COUNT"}
},
"ScopeDownStatement": {
"AndStatement": {"Statements": [
{"ByteMatchStatement": {
"SearchString": "/api/v1/transactions",
"FieldToMatch": {"UriPath": {}},
"TextTransformations": [{"Priority":0,"Type":"NONE"}],
"PositionalConstraint": "EXACTLY"
}},
{"ByteMatchStatement": {
"SearchString": "POST",
"FieldToMatch": {"Method": {}},
"TextTransformations": [{"Priority":0,"Type":"NONE"}],
"PositionalConstraint": "EXACTLY"
}}
]}
}
}
},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "LimitTransactionPost"
}
}]'
Nginx
# nginx.conf
http {
# Key on Authorization header value (or use $remote_addr for IP-based)
map $http_authorization $auth_key {
default $http_authorization;
"" $binary_remote_addr; # fallback to IP if no auth header
}
limit_req_zone $auth_key zone=transactions:20m rate=30r/m;
server {
location = /api/v1/transactions {
limit_except POST { deny all; }
limit_req zone=transactions burst=5 nodelay;
limit_req_status 429;
add_header Retry-After 60 always;
}
}
}
Akamai Kona Site Defender
# Akamai rate policy via API (simplified JSON body):
POST /appsec/v1/configs/{configId}/versions/{versionNumber}/rate-policies
{
"name": "TransactionPostLimit",
"description": "30 POSTs per minute per auth token on /api/v1/transactions",
"averageThreshold": 30,
"burstThreshold": 35,
"clientIdentifier": "REQUEST_HEADERS:Authorization",
"requestType": "CLIENT_REQ",
"path": {
"values": ["/api/v1/transactions"],
"positiveMatch": true
},
"method": {
"values": ["POST"],
"positiveMatch": true
},
"action": "deny",
"penaltyPeriod": 3600,
"timeWindow": 60
}
Key difference: time window granularity
Cloudflare and Nginx allow per-minute (60-second) windows. AWS WAF v2's minimum rate-based window is 5 minutes -- multiply your per-minute threshold by 5. Akamai supports per-minute windows directly via the timeWindow parameter. This difference matters for burst protection: a 5-minute window allows 5x the per-minute rate during the first minute of an attack before the limit fires.
Get Vendor-Specific Hardening Commands for Your Environment
DDactic assessments deliver per-endpoint rate limit recommendations in the exact syntax of your WAF vendor, ready to implement.
Run a Free Scan