Pricing - Pricing & Costs
CloudFront Free Tier 2026: What Is Free and When You Pay
The Amazon CloudFront free tier gives every AWS account 1 TB of data transfer out, 10 million HTTP and HTTPS requests ...
Cache poisoning happens when a request input that changes the response is left out of the cache key, so an attacker's variant is stored once and served to everyone who asks for the same URL. For example, a page with a 300-second TTL that receives 500 requests per second at one edge serves the poisoned copy 150,000 times before it expires, and the attacker can re-arm it every five minutes. This playbook takes about 90 minutes per zone. You inventory unkeyed inputs, probe them safely, and apply one of three rules to each: key it, strip it, or reject it.
Web cache poisoning works because RFC 9111 defines a shared cache's primary key as the request method and target URI, while origins routinely build responses from inputs outside that key, such as the X-Forwarded-Host header. As of 2026, one crafted request carrying an unkeyed header that alters the body, a redirect target or the status code is enough to poison a cached response for its full TTL, typically 300 to 3,600 seconds on HTML.
Every poisoning bug has the same shape. The CDN decides "these two requests are the same" using the cache key. The origin decides "this is what the response looks like" using a wider set of inputs. Any input in the second set but not the first is a lever an attacker can pull.
Four classes cover nearly every reported case:
Web cache deception is a different bug: the attacker tricks the cache into storing a victim's private response under a static-looking path. It deserves its own audit, and the rules below do not fix it.
Blast radius per edge equals request rate on the URL times the TTL. A product page at 50 requests per second with a 600-second TTL yields 30,000 poisoned responses per edge per window. A homepage at 2,000 requests per second with a 60-second TTL yields 120,000 responses. A short TTL does not save the homepage, because the attacker simply re-sends the request after each expiry. The real controls are the key and the inputs the origin trusts, not the TTL.
| Input type | Rule | Hit ratio cost | Example |
|---|---|---|---|
| Client-supplied proxy headers the origin should never trust | Strip at edge, overwrite with edge-set values | None | X-Forwarded-Host, X-Original-URL, X-Rewrite-URL |
| Inputs that legitimately vary output with few values | Add to cache key, normalized to an allowlist | Divides traffic per key by the number of variants | Accept-Encoding mapped to br, zstd, gzip or identity |
| Method or body overrides | Strip, or reject with a non-cached 400 | None | X-HTTP-Method-Override, GET bodies |
| Dropped tracking parameters the origin reflects | Strip before forwarding, not just from the key | None | utm_source echoed into a canonical link |
| High-cardinality inputs that vary output | Mark the route uncacheable or move variation client-side | Route leaves the cache entirely | Raw User-Agent, session cookies |
Stripping is the default rule because it closes the vector at zero hit ratio cost; keying is reserved for inputs with a small, normalized set of legitimate values.
The key rule is where trade-offs bite. Keying Accept-Language raw, with dozens of distinct strings in the wild, can split one cached response into 40 or more variants and multiply origin fetches per TTL by the same factor. Normalize to the locales you actually serve, typically 3 to 10, before adding anything to the key. Relying on Vary alone is weaker: CDN support for arbitrary Vary values is uneven, and a Vary on User-Agent fragments the cache to near zero.
If you deliver heavy static traffic through BlazingCDN, custom configurations and cache rules are available and each zone is set up together with an engineer, so the strip and key rules from this matrix can be written into the zone at onboarding, with purge through the API for step 7. The BlazingCDN features covering cache rules and purge list what is included. BlazingCDN is a delivery layer, not a WAF, so keep your existing security stack for request filtering.
Re-run the canary probe from step 3 for every input in your inventory, against at least the 20 highest-traffic cacheable routes. Expected result: the clean follow-up request never contains the canary, regardless of cache status. For error caching, send a request with a header past the origin limit to a unique path, then a clean request; the clean request must return 200 from origin, not a cached 400.
Then watch two metrics for 24 hours. Cache hit ratio per route should stay within 1 to 2 points of its baseline after stripping; a larger drop means you keyed something with too many values. Origin request rate should not climb by more than the variant count you deliberately introduced.
Strip rules are low risk to roll back but should rarely need it. If one breaks a feature, the origin was depending on a client-supplied header, which is the vulnerability itself, so fix the origin to read an edge-set header instead. Key changes are the risky ones: ship them one route group at a time, and revert by removing the key component and purging that group.
| Symptom | Cause | Fix |
|---|---|---|
| Redirect loops after stripping X-Forwarded-Proto | Origin can no longer tell the edge used HTTPS | Have the edge set the header itself, overwriting any client value |
| Canary still appears after the fix | An intermediate cache (origin shield, framework cache) has its own key | Audit every cache tier, then purge each one |
| Hit ratio drops 10 points or more | A raw, high-cardinality header was added to the key | Normalize to an allowlist before keying |
| Intermittent site-wide 400s | Negative caching of client errors | Disable caching for 400 and 5xx, cap 404 at 10 to 30 seconds |
Most post-fix surprises come from a second cache tier with its own key, so audit the shield and any framework cache with the same rigor as the edge.
Add a scheduled canary probe to CI that runs the step 3 check against new routes on every deploy, since framework upgrades frequently change trusted-proxy defaults. Log the full set of forwarded headers for a 1 percent sample of origin requests and alert when a new header appears that is not on your inventory. Finally, revisit TTLs: once inputs are controlled, longer HTML TTLs become safe and recover the hit ratio you may have traded away.
An unkeyed header is a request header the origin uses to build a response but the cache does not include in its cache key. Because the cache treats requests with different values as identical, a response generated from an attacker's value is stored and served to every user requesting the same URL until the TTL expires or the path is purged.
Vary helps only partially and often hurts performance. It tells caches to key on listed headers, but CDN support for arbitrary Vary values is inconsistent, and varying on high-cardinality headers such as User-Agent fragments the cache badly. Stripping untrusted headers at the edge, and keying only normalized, low-cardinality inputs, is more reliable than depending on Vary.
Yes, cache-poisoned denial of service happens when an error response gets cached for a popular URL. An attacker sends a request the origin rejects, for example an oversized header or a method override, and if the CDN stores that 400 or 405, every user receives the error until expiry. Never cache 400 or 5xx responses.
Test against unique cache-buster paths so no real user shares the poisoned cache key. Send one request with a canary value in the suspect header, then a clean request to the same unique path. If the clean response contains the canary and shows a cache hit, that input is poisonable and needs to be stripped or keyed.
Pick the 20 highest-traffic cacheable routes from your CDN logs and run the two-request canary probe against each, covering X-Forwarded-Host, X-Forwarded-Scheme, X-Original-URL, method override headers and every query parameter your key drops. Record which inputs reflect, which get stored, and which status codes your edge caches today. That one table tells you whether you need strip rules, key normalization or error caching changes, and it takes about 90 minutes per zone whether or not you change providers.
For high-volume static delivery, the BlazingCDN Anycast CDN with custom cache rules zones are configured with an engineer during roughly one hour of onboarding, so the key and strip rules from your audit can be in place from day one.
Heavy traffic.
Light bill.
The CDN for video and large traffic
Pricing - Pricing & Costs
The Amazon CloudFront free tier gives every AWS account 1 TB of data transfer out, 10 million HTTP and HTTPS requests ...
Learn
To set up a CDN for static files, you need five things working together: a cacheable origin, a dedicated hostname ...