On this page
What Is Web Cache Poisoning?
Web cache poisoning tricks a CDN or proxy into serving an attacker-controlled response to everyone. Learn how it works and how to prevent it.
Quick answer
- Web cache poisoning makes a cache store a malicious response and serve it to every visitor.
- It works when the cache keys a response on some inputs but the app reflects other, unkeyed inputs into the page.
- Prevent it by never reflecting unkeyed inputs into cached responses and by caching only complete, validated responses.
How does web cache poisoning work?
Caches store a response by its cache key — usually the URL and a few headers. The attacker sends a request with an extra input — a header, a query parameter, a cookie — that the application reflects into the page but that the cache doesn’t include in the key. The cache stores the attacker’s poisoned response and serves it to everyone who requests that URL. One request turns a reflected, per-user attack into a site-wide one: instead of fooling one visitor, the attacker fools every visitor through the cache.
What are the common inputs attackers use?
Anything the app reflects but the cache doesn’t key on: unkeyed query parameters, custom headers like X-Forwarded-Host or X-Forwarded-Proto (which some apps use to build URLs), cookies, and Accept headers. The classic chain is a CDN that ignores X-Forwarded-Host plus an app that uses that header to construct script or redirect URLs — the attacker injects a malicious host, the app bakes it into the page, and the cache serves the poisoned HTML to everyone.
How do I prevent cache poisoning?
First, don’t reflect untrusted inputs into pages that get cached — if a value is part of the response, it must be part of the cache key. Second, validate inputs: for X-Forwarded-Host and similar headers, allow only expected values or ignore them entirely and use your own configuration. Third, review cache configs: know exactly which headers and parameters are keyed, and add Cache-Control: no-store to responses that contain per-user or dynamically reflected data.
# CDN config example: key on the headers the app actually uses\n# Cache key: scheme + host + path + ?query\n# Do NOT key on: cookies, custom headers, Accept\n# If the app reflects X-Forwarded-Host anywhere, either key on it or reject it.Where this bites vibecoders
Vibecoded apps get CDNs and caching added as an afterthought — ‘just add Cloudflare and it’ll be faster’ — and AI assistants generate code that reflects headers into pages without thinking about what the cache keys on. The result is a vulnerability that scales: the attacker doesn’t need to trick each user, the cache does it for them. The fix is mostly discipline — validate reflected inputs, understand your cache key — which is exactly what the assistant skips.
Where AI coding assistants get this wrong
- Reflecting headers like X-Forwarded-Host into generated URLs without validating them.
- Adding caching to pages that contain per-user or dynamically reflected content.
- No understanding of what the CDN keys on, so unkeyed inputs flow straight into cached HTML.
- Using Cache-Control: public on responses that include user-specific data.
Checklist
- Validate or reject headers the app reflects (X-Forwarded-*, custom headers).
- Cache only responses with no unkeyed reflected input.
- Know your cache key: enumerate which headers and parameters are keyed.
- Set Cache-Control: no-store on any response with per-user data.
FAQ
What is the difference between cache poisoning and cache deception?
Cache poisoning makes the cache store a bad response the attacker created. Cache deception tricks the cache into storing a private response it shouldn’t — for example, requesting /account.php/nonexistent.css so the cache stores your account page and serves it to others. Both abuse caching; one poisons, the other leaks.
Can cache poisoning affect my site if I don’t use a CDN?
Yes — any cache in the path can be abused: a reverse proxy like nginx with a caching layer, a browser cache for some variants, or an in-app cache. The CDN case is the most impactful because it serves everyone, but the same rules apply to any cache.
Related topics
- What Is a CDN and Do You Need One?
- How to Set Up Cloudflare for a Small Project
- What Is SSRF (Server-Side Request Forgery)?
- What Are Security Headers (and How Do You Add Them)?
- What Is Path Traversal (Directory Traversal)?