Measured in your browserWe advise on speed. We practice it.Loaded just now · real numbers from this visit, not a lab score.
Page loaded
First byte
DOM ready
First paint
Largest paint
DNS lookup
TLS handshake
Transferred
Saved by compression
Requests

A cache stores whatever the origin returns and serves it to everyone who matches the key. Cache poisoning weaponizes that generosity: induce the origin to emit something malicious using inputs the cache did not key on, and the cache faithfully distributes the attack at edge speed to every subsequent visitor.

The core mechanic: unkeyed inputs

The vulnerability is a gap between what influences the response and what the cache keys on. If the origin reflects a header (X-Forwarded-Host into absolute URLs, a forwarded scheme into redirects, an odd header into inline script) and that header is not part of the cache key, an attacker sends one crafted request, the poisoned response is stored under the innocent key, and every user until expiry receives it. The audit question is mechanical: enumerate every request input your responses reflect, and verify each is either keyed, validated or ignored.

The delivery-layer aggravators

Header normalization differences between edge and origin (one collapses duplicates, the other picks the last) create smuggling seams where the cache keys on one interpretation and the origin acts on another. Fat query handling (parameters ignored by the key but read by the application, the exact normalization our Vary article recommended, weaponized) requires that ignored parameters be genuinely ignored by origin too. And per-vendor parsing quirks are their own research field; the defense is boring consistency: strict, minimal, normalized inputs forwarded upstream.

The deployment-era postscript: modern estates add layers that reintroduce the risk in new clothes, edge functions that read headers while composing cached responses, and service workers that cache client-side with their own key logic, and both deserve the same key-or-kill review as the CDN itself. An edge function that personalizes a cached response from an unkeyed cookie has rebuilt the vulnerability inside your own code; a service worker that caches by URL while rendering by header has shipped it to every client. The principle is portable and permanent: wherever storage meets request inputs, enumerate the inputs, and make the key tell the whole truth about what shaped the bytes.

The defense stack

Key or kill: every input that can influence a cacheable response is either in the cache key or stripped/normalized before origin. Validate hosts against an allowlist and never trust forwarded-host style headers for URL construction. Keep error responses uncacheable or short-lived (poisoned errors are the lazy variant, cache-poisoned denial of service is just a stored 400). And test offensively: the public research toolkits for poisoning discovery run happily in staging, and finding your own unkeyed reflections beats reading about them in a disclosure.

In practice

Quarterly, run the reflected-input inventory against your top routes, diff edge and origin header handling for the smuggling seams, confirm error-caching policy, and add poisoning probes to your security testing cadence. The attack class is entirely preventable with key discipline, which makes any successful poisoning an audit failure before it is a security failure.

Poisoning assessments here pair the input inventory with live staging probes. The unkeyed-reflection list is the deliverable.

Get the free assessmentMore analysis