DEV Community

Alexey Baltacov for AWS Community Builders

Posted on • Originally published at linkedin.com

AWS WAF Rate Limiting Based on Origin Response

AWS WAF Rate Limiting Based on Origin Response

Introduction

You have a public website fronted by Amazon CloudFront that serves static files from S3. Customers access these files via direct URLs and must be able to download any file at any time without interference. At the same time, you want to stop malicious actors from crawling your entire bucket.

The Challenge

  • Goal: Prevent automated scanning of all URLs while still allowing legitimate customers unlimited downloads of the specific files they need.
  • Constraint: No user login or authentication. Files are freely downloadable, so you cannot simply gate them behind a sign-in flow.

Why Plain AWS WAF Rate Limiting Is Not Enough

AWS WAF lets you define rate-limit rules keyed by source IP or by fingerprinting mechanisms such as JA3 and JA4. In theory, you could set:

  • A low limit such as 10 requests per minute, which blocks scanners effectively but risks blocking legitimate high-throughput customers.
  • A high limit, which lets scanners creep through, especially if attackers distribute requests across IPs or devices.

The result is an uncomfortable trade-off: too low hurts real users, too high fails to stop attackers.

AWS WAF's View of Origin Responses and ATP Rules

By default, custom AWS WAF rules only inspect request attributes. They do not know whether your origin returned 200 OK or 404 Not Found.

The only built-in AWS WAF rules that inspect responses are the Account Takeover Prevention (ATP) managed rules. Those require you to map login fields and are designed for authentication endpoints, not static file downloads.

Why Lambda@Edge Alone Cannot Solve It

Lambda@Edge runs per request and has no built-in shared global state. It cannot maintain counters across all executions, so by itself it cannot enforce a global request threshold.

A Hybrid Approach: WAF + Lambda@Edge

You can combine WAF's global counting capabilities with Lambda@Edge's ability to modify HTTP responses.

1) Primary WAF Rate-Limit Rule (Soft Threshold)

  • Type: Rate-based statement, for example 10 requests per 5 minutes
  • Action: Count (not Block)
  • Custom response header: Insert X-RateLimit-Exceeded: true

AWS WAF prefixes custom header names with x-amzn-waf-. So if you specify X-RateLimit-Exceeded, downstream components will see:

x-amzn-waf-x-ratelimit-exceeded
Enter fullscreen mode Exit fullscreen mode

2) Secondary WAF Rate-Limit Rule (Hard Threshold)

  • Type: Rate-based statement with a much higher threshold, for example 1,000 requests per 5 minutes
  • Action: Block

This immediately stops heavy-volume attacks at the WAF layer, prevents excessive Lambda@Edge invocations, and reduces cost.

3) Lambda@Edge Function (Origin Response Trigger)

  • Trigger: CloudFront Origin Response event
exports.handler = async (event) => {
  const response = event.Records[0].cf.response;
  const headers = response.headers;

  // AWS WAF prefixes headers with x-amzn-waf-
  const flag = headers['x-amzn-waf-x-ratelimit-exceeded'];

  if (flag && flag[0].value === 'true' && response.status !== '200') {
    return {
      status: '429',
      statusDescription: 'Too Many Requests',
      headers: {
        'content-type': [{ key: 'Content-Type', value: 'text/html' }]
      },
      body: '<html><body><h1>Rate Limit Reached</h1><p>Please try again later.</p></body></html>'
    };
  }

  return response;
};
Enter fullscreen mode Exit fullscreen mode

Associate and Deploy

  • Attach your Web ACL, containing both rate-limit rules and any ATP group you choose to use, to the CloudFront distribution.
  • Deploy the Lambda@Edge function through the CloudFront console or the AWS CLI.

Testing

Legitimate Access

Repeatedly fetch an existing file. The soft-limit counter will increment, but users will still receive the file until the hard threshold is crossed.

Scanning Attempts

Request many non-existent URLs. Errors quickly hit the soft threshold, causing your custom 429 response page. Extreme traffic volumes hit the hard threshold and are blocked at WAF before Lambda@Edge runs.

Demo video

Benefits of This Pattern

  • Precision: Rate limits are tied to actual Not Found or error responses.
  • User Experience: Legitimate customers getting 200 OK responses are not blocked unless they truly exceed your thresholds.
  • Cost Efficiency: High-volume attacks are stopped before Lambda@Edge runs.
  • AWS-native design: Uses AWS WAF, CloudFront, and Lambda@Edge without adding external state stores or proxy layers.

References


Originally published on LinkedIn on May 8, 2025.

Top comments (0)