How to Fix API Returning 403 Forbidden on AWS CloudFront Due to WAF Blocked Requests
Quick answer
Legitimate requests to your API get rejected with a 403 when going through CloudFront, even though the same request works fine hitting the origin directly. If...
Legitimate requests to your API get rejected with a 403 when going through CloudFront, even though the same request works fine hitting the origin directly. If you have AWS WAF attached to the CloudFront distribution, this is very likely WAF blocking the request based on one of its configured rules β and the specific rule triggering the block is rarely obvious just from the plain 403 response itself.
The Problem
A request through CloudFront is rejected, while the same request against the origin server directly succeeds:
$ curl -i https://api.yourapp.com/users?search=O'Brien
HTTP/2 403
<?xml version="1.0" encoding="UTF-8"?>
<Error><Code>AccessDenied</Code><Message>Request blocked</Message></Error>
The bare response gives no indication of which rule caused the block or why β that detail only shows up in WAF's own logging, not in the response sent to the client.
Why It Happens
AWS WAF sits in front of CloudFront (or ALB) and evaluates every request against a configured set of rules β managed rule groups (like the AWS Managed Rules for common threats), custom rules, and rate-based rules β blocking anything that matches a rule configured to block rather than just monitor. A 403 from WAF means a rule matched and took action, but this can happen for both genuinely malicious requests and, unfortunately, for entirely legitimate ones that happen to superficially resemble an attack pattern. Common false-positive triggers:
- SQL injection or XSS managed rule groups flagging legitimate input that happens to contain characters or patterns resembling an attack β a search query containing an apostrophe (as in the example above), or user-generated content containing HTML-like syntax.
- Rate-based rules blocking a legitimate but bursty client, especially a mobile app that retries aggressively or a batch job making many rapid requests from a single IP.
- Size-restriction rules blocking a legitimately large request body or set of headers that exceeds a configured threshold.
- Geographic or IP reputation rules blocking traffic from a region or IP range that includes legitimate users alongside genuinely malicious traffic sources.
The Fix
First, find out exactly which rule is blocking the request by checking WAF's own logs β this is essential, since the 403 response itself deliberately doesn't reveal this information to avoid giving attackers useful feedback:
aws logs filter-log-events \
--log-group-name aws-waf-logs-my-distribution \
--filter-pattern '{ $.action = "BLOCK" }' \
--start-time 1723027200000
Each blocked request's log entry includes the specific terminatingRuleId that caused the block, which tells you exactly which managed rule group or custom rule to investigate:
{
"action": "BLOCK",
"terminatingRuleId": "SQLi_QUERYARGUMENTS",
"httpRequest": {"uri": "/users", "args": "search=O'Brien"}
}
If it's a false positive from a managed rule group, add a specific exclusion for that rule while keeping the rest of the rule group active, rather than disabling the entire protective rule group:
aws wafv2 update-rule-group \
--name my-managed-rules \
--scope CLOUDFRONT \
--id rule-group-id \
--rules '[{"Name": "SQLi_QUERYARGUMENTS", "Priority": 1, "OverrideAction": {"Count": {}}, ...}]'
Setting a specific rule's action to Count instead of Block makes it log-only for that rule, letting you monitor for genuine threats without blocking legitimate traffic matching that particular pattern, while keeping every other rule in the group fully enforcing.
For rate-based rule false positives affecting legitimate high-volume clients (mobile apps, internal batch jobs), consider raising the specific rate threshold or excluding known-legitimate IP ranges via an IP set exception:
aws wafv2 create-ip-set \
--name trusted-batch-clients \
--scope CLOUDFRONT \
--ip-address-version IPV4 \
--addresses "203.0.113.0/24"
Reference this IP set in a rule that explicitly allows or exempts these known-legitimate sources from the rate-based rule that was blocking them.
For size-restriction issues, either raise the configured size limit if legitimate requests genuinely need to exceed the default, or investigate why requests are larger than expected β sometimes this reveals an actual application issue (an unnecessarily verbose payload) worth fixing at the source rather than just raising the WAF limit to accommodate it.
Still Not Working?
If you need to quickly confirm whether WAF is truly the source of the block before diving deeper into rule-specific tuning, temporarily set the entire web ACL to count mode rather than block mode, and observe whether requests succeed β this isolates WAF definitively as the cause (or rules it out) before you invest time narrowing down a specific rule:
aws wafv2 update-web-acl \
--name my-web-acl \
--scope CLOUDFRONT \
--id web-acl-id \
--default-action Allow={} \
--rules "$(aws wafv2 get-web-acl --name my-web-acl --scope CLOUDFRONT --id web-acl-id --query 'WebACL.Rules')"
Only do this briefly for diagnostic purposes β switching to count-only mode removes actual protection while active, so revert to blocking mode as soon as you've confirmed and addressed the specific rule causing your false positives, rather than leaving the web ACL in a non-enforcing state any longer than necessary for diagnosis.