AWS WAF
CDN and WAF
Also known as: AWS WAF Bot Control, AWS Shield, awswaf
The one product here whose challenge and CAPTCHA responses have documented, distinctive status codes: 202 for a challenge, 405 for a CAPTCHA, and 403 for a block.
AWS WAF is the most precisely documented product in this glossary, and it is worth learning for that reason alone. Where most vendors overload 403, AWS gives each action its own status code and its own header value, so a caller can tell exactly what happened without parsing HTML.
The behaviour turns on a token. AWS WAF documents that when a request carries a valid token, the CAPTCHA and Challenge actions behave like a Count action: labels and customisations are applied and evaluation continues through the rest of the web ACL. When the token is missing, invalid, or expired, evaluation stops and the request is answered with the challenge or CAPTCHA response instead.
One documented sharp edge catches people repeatedly: AWS WAF does not include CORS headers on CAPTCHA and challenge responses, so browser JavaScript cannot read the x-amzn-waf-action header cross-origin. The header is only available within the application's own domain.
How to identify it
- x-amzn-waf-action on the response, with a value of challenge or captcha, is definitive.
- A 202 or a 405 where the endpoint would never legitimately return either. 405 in particular almost never appears on a GET that the API otherwise supports.
- Interstitial pages referencing AWS WAF challenge or CAPTCHA scripts.
- An aws-waf-token cookie carrying the client's token.
Headers
- x-amzn-waf-action: Set to challenge on Challenge action responses and captcha on CAPTCHA action responses. Not readable by cross-domain browser JavaScript, because these responses carry no CORS headers.
Cookies
- aws-waf-token: The token AWS WAF creates and encrypts after a client successfully answers a silent challenge or a CAPTCHA. Sent on subsequent requests and decrypted and verified at the edge.
Status codes
202 Challenge action
Challenge. Documented by the vendor.
A silent challenge was issued because the request had no valid token. AWS documents this as HTTP status code 202 Request Accepted with the header x-amzn-waf-action set to challenge.
What triggers it
- A rule with the Challenge action matched a request whose token was missing, invalid, or expired.
- A first request from a client that has not yet acquired a token.
How to confirm it
- x-amzn-waf-action: challenge.
- A JavaScript interstitial in the body, but only when the request's Accept header contained text/html. A JSON client gets the status and header with no interstitial.
What to do about it
- Recognise 202 as a refusal in this context. It is one of the most misleading responses in the whole space, because 202 normally means accepted for processing and here it means the opposite: the request was stopped before it reached the destination.
- Any client parsing the response body as the resource will get nonsense. Check the header before parsing.
- In the browser, remember the header is unreadable cross-domain, so a front end diagnosing this from JavaScript needs the call to be same-origin.
HTTP/2 202 x-amzn-waf-action: challenge content-type: text/html; charset=UTF-8 <html><head><script type="text/javascript" src="/challenge.js"></script></head>...
405 CAPTCHA action
Challenge. Documented by the vendor.
A CAPTCHA puzzle was required. AWS documents this as HTTP status code 405 Method Not Allowed with the header x-amzn-waf-action set to captcha.
What triggers it
- A rule with the CAPTCHA action matched a request whose token was missing, invalid, or expired.
How to confirm it
- x-amzn-waf-action: captcha.
- A CAPTCHA interstitial in the body when the Accept header contained text/html.
What to do about it
- Do not chase the method. A 405 here has nothing to do with GET versus POST, and rewriting the request method will not help. This is the single most misdiagnosed response in this glossary.
- AWS documents that if the client has no token yet, the CAPTCHA interstitial runs the challenge script first to initialise one, then runs the puzzle. So a CAPTCHA response implies both steps are outstanding.
HTTP/2 405 x-amzn-waf-action: captcha content-type: text/html; charset=UTF-8 <html><head><script type="text/javascript" src="/captcha.js"></script></head>...
403 Block action
Blocked. Documented by the vendor.
A rule with the Block action matched. AWS documents that Block responds with an HTTP 403 Forbidden status code, and that in rules you define you can customize the response.
What triggers it
- A managed rule group such as Bot Control classifying the request as an unwanted bot.
- A rate-based rule exceeding its threshold.
- An IP set, geo match, or custom rule matching the request.
How to confirm it
- 403 with no x-amzn-waf-action header, since Block is not a challenge.
- A short body, or whatever custom response the operator configured.
What to do about it
- Because the block response is customizable, the status code may not be 403 at all on a given deployment. Absence of x-amzn-waf-action alongside an unexplained refusal is the more durable signal.
- Bot Control blocks frequently key on address reputation before any client-side signal is collected, so this can arrive on a first request with nothing to solve.
If you run a site behind it
- 202 and 405 are unusual enough to break naive clients, which is partly the point, but they also break your own integrations and monitoring. Make sure your dashboards and your mobile clients understand them before you enable the actions widely.
- Both actions carry additional charges, which AWS notes explicitly, so a broad CAPTCHA rule has a direct cost as well as a friction cost.
- Set token domains deliberately if you serve several hostnames, or clients will re-challenge as they move between them.
- The missing CORS headers on challenge responses are a documented constraint, not a bug. Design single-page applications around it rather than expecting to read the header cross-origin.
If your traffic is being caught by it
- Memorise the pair: 202 means challenge, 405 means CAPTCHA. Neither means what the status code normally means.
- A valid token turns both actions into a pass-through, so the fix is almost always about acquiring and preserving the token rather than about the request itself.
- Tokens expire, and immunity times are configurable per web ACL and per rule, so a client that works for a while and then starts seeing 202 has probably aged out rather than been newly detected.