Knowing you need rate limits is not the same as having them configured correctly. The gap between "we have Cloudflare" and "our API endpoints are protected" is where most DDoS attacks find their entry point. This post provides production-ready configuration syntax for the four most common API protection layers: Cloudflare, AWS WAF, Nginx, and Akamai.
Each vendor uses different terminology, different counting windows, and different scoping rules. A Cloudflare rate limit scoped to a path prefix does not behave identically to an AWS WAF rate-based rule with a URI condition. Understanding the differences matters when you are tuning under pressure during an incident, or when you are auditing whether existing rules actually cover the endpoints they claim to cover.
Cloudflare Rate Limiting (API Shield)
Cloudflare's rate limiting operates at the edge before requests reach your origin. Rules are expressed as threshold-per-period per matching criteria. The most important distinction is between counting by IP and counting by an authenticated identifier such as a JWT sub claim.
# Cloudflare Ruleset Engine — rate limit on API prefix
# Applied via API or dashboard under Security > WAF > Rate limiting rules
Rule name: API rate limit — /api/v1
Expression: (http.request.uri.path matches "^/api/v1/")
AND (not cf.bot_management.verified_bot)
Action: Block
Threshold: 200 requests
Period: 60 seconds
Counting dimension: ip
# Stricter rule for auth endpoint
Rule name: Login endpoint protection
Expression: (http.request.uri.path eq "/api/v1/auth/login")
Action: Block
Threshold: 5 requests
Period: 60 seconds
Counting dimension: ip
Mitigation timeout: 600 seconds
Cloudflare counting dimensions
Cloudflare supports counting by ip, ip.src + ASN, or a custom header value (e.g., X-User-ID). For authenticated APIs, counting by user ID catches credential-sharing attacks that rotating IPs would evade.
AWS WAF Rate-Based Rules
AWS WAF rate-based rules operate differently: they count requests in a 5-minute rolling window and block the IP once the threshold is crossed. The minimum window is 5 minutes; there is no sub-minute granularity. This means fast burst attacks under 5 minutes require a separate managed rule group or custom rule at the ALB layer.
# AWS WAF v2 — rate-based rule via CLI
aws wafv2 create-rule-group \
--name "APIRateLimit" \
--scope REGIONAL \
--capacity 100 \
--rules '[
{
"Name": "APIv1RateLimit",
"Priority": 1,
"Statement": {
"RateBasedStatement": {
"Limit": 2000,
"AggregateKeyType": "IP",
"ScopeDownStatement": {
"ByteMatchStatement": {
"SearchString": "/api/v1/",
"FieldToMatch": {"UriPath": {}},
"TextTransformations": [{"Priority": 0, "Type": "NONE"}],
"PositionalConstraint": "STARTS_WITH"
}
}
}
},
"Action": {"Block": {}},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "APIv1RateLimit"
}
}
]'
Nginx Rate Limiting
Nginx uses a token bucket algorithm via limit_req_zone and limit_req. The zone defines the key space and rate; the directive applies the limit. The burst parameter allows short spikes without immediate rejection, which is appropriate for legitimate clients with variable send rates. Setting nodelay serves burst requests immediately rather than queuing them.
# nginx.conf — define zones at http block level
http {
# Per-IP zone for all API traffic
limit_req_zone $binary_remote_addr zone=api_per_ip:10m rate=100r/m;
# Per-IP zone for auth endpoints (strict)
limit_req_zone $binary_remote_addr zone=auth_strict:10m rate=5r/m;
# Per-user zone (requires auth header extraction)
limit_req_zone $http_x_user_id zone=api_per_user:20m rate=500r/m;
server {
location /api/ {
limit_req zone=api_per_ip burst=20 nodelay;
limit_req_status 429;
# Return JSON error body for API clients
error_page 429 /429.json;
}
location /api/v1/auth/ {
limit_req zone=auth_strict burst=2 nodelay;
limit_req_status 429;
}
}
}
Akamai API Gateway Rate Controls
Akamai's API Gateway exposes rate limiting through its API Definition policies. Rules are attached to API endpoints and can reference client credential identifiers rather than raw IPs, which is essential for mobile API clients that share egress IPs via carrier NAT.
# Akamai API Gateway policy snippet (JSON)
{
"rateLimiting": {
"enabled": true,
"policies": [
{
"name": "api-standard",
"matchCriteria": {"path": "/api/v1/**"},
"quota": {
"value": 300,
"interval": "MINUTE",
"identifierType": "CLIENT_ID"
},
"penaltyBox": {
"enabled": true,
"duration": 300
}
},
{
"name": "auth-strict",
"matchCriteria": {"path": "/api/v1/auth/**"},
"quota": {
"value": 10,
"interval": "MINUTE",
"identifierType": "IP"
}
}
]
}
}
Do not rely on a single counting dimension
IP-based counting is bypassed by distributed botnets with per-IP rates below your threshold. Pair IP limits with per-user or per-session limits. If your API uses JWTs, extract the sub claim as a counting key.
Verifying Rules Are Active
Writing a rule does not guarantee it fires. Common failure modes include: rule attached to wrong listener, path expression not matching due to query string, rule evaluated after a more permissive allow rule, or counting zone running out of shared memory. For each vendor, verify by sending a request sequence above threshold from a test IP and confirming a 429 response and a corresponding log entry with the rule name.
Let DDactic Verify Your API Rate Limits
DDactic's scanner probes your API endpoints with calibrated request sequences to confirm rate limits fire at the thresholds you expect -- and reports the gaps when they do not.
Run a Free Scan