WAF Rate-Limit Syntax Comparison: Cloudflare, AWS WAF, Akamai, Nginx

June 5, 2026 | 12 min read | Configuration Reference

Same goal, four different syntaxes. Rate limiting an API endpoint to 50 requests per minute per source IP requires a completely different configuration command on Cloudflare versus AWS WAF versus Akamai versus Nginx. This post provides a direct syntax comparison so engineers can implement equivalent policies across different platforms, or understand the translation when inheriting a configuration from a different technology stack.

The reference scenario: rate limit GET /api/v1/products to 50 requests per minute per source IP, return 429 with Retry-After: 60 when exceeded, and block the source for 30 minutes.

Cloudflare WAF Custom Rules

# Cloudflare — WAF Custom Rule with rate limiting action
# Rule expression (matches path and method):
(http.request.uri.path eq "/api/v1/products" and http.request.method eq "GET")

# Rate limit configuration:
# Characteristics: [ip.src]
# Period: 60 seconds
# Requests per period: 50
# Mitigation action: Block
# Response: 429 (automatic for rate limit actions)
# Mitigation timeout: 1800 seconds (30 minutes)

# Add Retry-After via Transform Rule on responses where http.response.code eq 429:
# Set header: Retry-After = "60"

# Key advantage: sub-minute granularity (10s minimum), per-IP or per-user keying,
# managed challenge option to avoid hard-blocking mobile clients

AWS WAF v2

# AWS WAF v2 — Rate-based rule
# Note: minimum window is 300 seconds; 50/min = 250 per 5-min window
aws wafv2 put-rule-group --name "ProductApiLimit" --scope CLOUDFRONT \
  --rules '[{
    "Name": "LimitProductsGet",
    "Priority": 1,
    "Action": {"Block": {"CustomResponse": {
      "ResponseCode": 429,
      "ResponseHeaders": [{"Name": "Retry-After", "Value": "60"}]
    }}},
    "Statement": {
      "RateBasedStatement": {
        "Limit": 250,
        "AggregateKeyType": "IP",
        "ScopeDownStatement": {
          "AndStatement": {"Statements": [
            {"ByteMatchStatement": {
              "SearchString": "/api/v1/products",
              "FieldToMatch": {"UriPath": {}},
              "TextTransformations": [{"Priority": 0, "Type": "NONE"}],
              "PositionalConstraint": "EXACTLY"
            }},
            {"ByteMatchStatement": {
              "SearchString": "GET",
              "FieldToMatch": {"Method": {}},
              "TextTransformations": [{"Priority": 0, "Type": "NONE"}],
              "PositionalConstraint": "EXACTLY"
            }}
          ]}
        }
      }
    },
    "VisibilityConfig": {...}
  }]'
# Note: no native 30-minute block duration; requests are re-evaluated per window

Akamai Kona Site Defender

# Akamai — Rate Policy (via API or Control Center)
{
  "name": "ProductsGetLimit",
  "averageThreshold": 50,
  "burstThreshold": 55,
  "clientIdentifier": "IP",
  "path": {
    "values": ["/api/v1/products"],
    "positiveMatch": true
  },
  "method": {
    "values": ["GET"],
    "positiveMatch": true
  },
  "action": "deny",
  "penaltyPeriod": 1800,
  "timeWindow": 60,
  "denyResponse": {
    "statusCode": 429,
    "headers": [{"name": "Retry-After", "value": "60"}]
  }
}
# Key advantage: direct per-second or per-minute window, native 30-min penalty period

Nginx

# nginx.conf
http {
    limit_req_zone $binary_remote_addr zone=products_get:10m rate=50r/m;

    server {
        location = /api/v1/products {
            # Apply only to GET requests:
            if ($request_method != GET) { return 405; }

            limit_req zone=products_get burst=10 nodelay;
            limit_req_status 429;
            add_header Retry-After 60 always;

            # Note: Nginx cannot natively implement a 30-minute block duration.
            # Use fail2ban or a Lua module to track and block IPs that trigger limit_req.
            # Alternatively, log violations and feed to Cloudflare IP blocklist via API.
        }
    }
}
# Key limitation: no native per-IP block duration; requires external tooling for sticky blocks

Key differences that affect policy equivalence

AWS WAF's 5-minute minimum window means it cannot rate limit at true per-minute granularity -- a burst of 250 requests in 60 seconds passes even if the 300-second aggregate limit has not been reached. Cloudflare and Akamai support true per-minute limits. Nginx has no native sticky-block duration and requires external tooling for the 30-minute block period.

Translate Findings to Your Vendor's Syntax

DDactic assessments deliver hardening recommendations in the exact syntax of your deployed WAF vendor, with parameters derived from measured saturation thresholds.

Run a Free Scan
WAF SyntaxCloudflareAWS WAFAkamaiNginxRate LimitingComparison