A retail client turned on AWS WAF’s managed SQL injection rule set the week before a big sale, tested it against a handful of pages, and called it done. On sale day, checkout started failing for a meaningful slice of customers — a promo code field that legitimately contained an apostrophe was tripping the SQLi rule and getting blocked with a 403. The rule wasn’t wrong, exactly. It was doing what SQL injection rules do. It just hadn’t been tuned against the actual shape of the site’s real traffic before going live under load.

WAF failures are almost always about scope and tuning, not about WAF being broken. Here’s how to find out what’s actually being blocked and why.

The Problem

Legitimate requests get blocked, or attacks that should be blocked aren’t:

Symptom What It Means
Real users get 403 Forbidden A rule (managed or custom) matched their request as malicious
All requests from an office or NAT gateway get rate-limited Rate-based rule counting by source IP, and many users share one IP
Managed rule group blocks valid input Rule tuned for generic threats, not your application’s actual input shapes
WAF shows “blocked” but the app still processed the request WAF in Count mode, not Block mode, or associated with the wrong resource
Bot traffic gets through despite Bot Control enabled Rule group not associated with the right web ACL, or traffic bypassing the protected endpoint

WAF rules are pattern matches against real-world variety, and real-world variety always eventually collides with a pattern that was written to be general-purpose.

Why Does This Happen?

  • Managed rule groups tuned for the general case, not your app: AWS Managed Rules (SQLi, XSS, Core Rule Set) are written to catch broad attack patterns. Legitimate input containing SQL-like syntax, HTML fragments, or special characters (a bio field, a code snippet, a name with an apostrophe) can match.
  • Rate-based rules counting by IP behind NAT or a corporate proxy: A rate-based rule keyed on source IP treats every user behind the same NAT gateway, corporate VPN, or mobile carrier CGNAT as one client — a handful of real users can trip a rate limit meant for a single abusive client.
  • Rule evaluated in Count mode, not Block mode: New rules are often deployed in Count-only mode to observe behavior before enforcing — if a rule stays in Count mode indefinitely by mistake, it logs matches but never actually blocks, giving a false sense of protection.
  • Web ACL not associated with the actual entry point: A web ACL created and configured correctly but associated with the wrong CloudFront distribution, ALB, or API Gateway stage does nothing for the traffic you think it’s protecting.
  • Rule priority ordering causing unexpected short-circuits: Rules evaluate in priority order, and an early Allow or Block rule can prevent a later, more specific rule from ever being evaluated.
  • Label-based rule dependencies misconfigured: Rules that key off labels set by earlier rules in the same web ACL will silently never fire if the labeling rule was modified or reordered and stopped applying the expected label.

The Fix

Step 1: Find the Specific Rule That Blocked the Request

Enable and query WAF logs (via Kinesis Data Firehose to S3, or CloudWatch Logs):

aws logs filter-log-events \
  --log-group-name aws-waf-logs-orders-app \
  --filter-pattern '{ $.action = "BLOCK" }' \
  --start-time $(date -d '1 hour ago' +%s000) \
  --limit 20

Each log entry includes the terminatingRuleId — the exact rule that caused the block, not just “WAF blocked it.”

Step 2: Check Whether the Rule Is in Count or Block Mode

aws wafv2 get-web-acl \
  --name orders-web-acl \
  --scope REGIONAL \
  --id abc12345-6789-def0-1234-56789abcdef0 \
  --query "WebACL.Rules[*].{Name:Name,Action:keys(OverrideAction || Action)}" \
  --output table

If the offending rule shows CountAction when you expected enforcement, or BlockAction when you expected observation-only, that’s your mismatch.

Step 3: Reproduce the Blocked Request and Identify the Match

Use the sampled requests API to see the exact request that got flagged, including headers and matched rule:

aws wafv2 get-sampled-requests \
  --web-acl-arn arn:aws:wafv2:us-east-1:111122223333:regional/webacl/orders-web-acl/abc12345 \
  --rule-metric-name AWSManagedRulesSQLiRuleSet \
  --scope REGIONAL \
  --time-window StartTime=$(date -u -d '1 hour ago' +%s),EndTime=$(date -u +%s) \
  --max-items 50

Step 4: Add a Scope-Down Statement or Exclude the Specific Rule

For a managed rule group producing false positives on a known field, exclude the specific rule inside the group rather than disabling the whole group:

aws wafv2 update-web-acl \
  --name orders-web-acl \
  --scope REGIONAL \
  --id abc12345-6789-def0-1234-56789abcdef0 \
  --lock-token TOKEN123 \
  --default-action Allow={} \
  --rules file://rules-with-exclusions.json

Where rules-with-exclusions.json includes an ExcludedRules entry for the specific sub-rule (e.g., SQLi_BODY) rather than removing the entire managed rule group’s protection.

Step 5: Fix Rate-Based Rules for Shared-IP Traffic

Scope the rate limit to a more specific key, such as a session cookie or a custom header, instead of raw IP when you know large user populations share an egress IP:

aws wafv2 update-web-acl \
  --name orders-web-acl \
  --scope REGIONAL \
  --id abc12345-6789-def0-1234-56789abcdef0 \
  --lock-token TOKEN123 \
  --rules file://rate-rule-by-header.json

Or raise the threshold for known corporate/NAT egress ranges via an IP set exception combined with a higher limit rule evaluated first.

Step 6: Confirm the Web ACL Is Associated With the Right Resource

aws wafv2 list-resources-for-web-acl \
  --web-acl-arn arn:aws:wafv2:us-east-1:111122223333:regional/webacl/orders-web-acl/abc12345 \
  --resource-type APPLICATION_LOAD_BALANCER

If your ALB or API Gateway stage isn’t listed, associate it:

aws wafv2 associate-web-acl \
  --web-acl-arn arn:aws:wafv2:us-east-1:111122223333:regional/webacl/orders-web-acl/abc12345 \
  --resource-arn arn:aws:elasticloadbalancing:us-east-1:111122223333:loadbalancer/app/orders-alb/abc123

Step 7: Check Rule Priority Ordering

aws wafv2 get-web-acl \
  --name orders-web-acl \
  --scope REGIONAL \
  --id abc12345-6789-def0-1234-56789abcdef0 \
  --query "WebACL.Rules[*].{Name:Name,Priority:Priority}" \
  --output table

A lower-priority-number rule (evaluated first) with a terminating action will prevent later rules from running at all — reorder if a broad rule is short-circuiting a more specific one.

Is This Safe?

The get, list, and get-sampled-requests calls are read-only. Updating a web ACL takes effect within about a minute and applies to new requests immediately — test rule exclusions against a staging web ACL or in Count mode first before rolling changes to a production ACL that’s actively protecting traffic. Never disable an entire managed rule group as a quick fix for one false positive — exclude the specific sub-rule so the rest of the protection stays intact.

Key Takeaway

WAF false positives are a tuning problem, not a broken-service problem. Use the sampled requests API and WAF logs to find the exact rule and exact request that triggered a block before changing anything, exclude specific sub-rules instead of whole managed groups, and scope rate-based rules to something more specific than raw IP when you know real users share egress addresses. WAF protects what you tell it to protect, exactly as specifically as you tell it — vague rules produce vague (and sometimes wrong) outcomes.


Have questions or ran into a different AWS WAF issue? Connect with me on LinkedIn or X.