Web Cache Poisoning: How to Find High-Impact Cache Bugs
Web cache poisoning turns a single crafted request into an attack on every visitor. If a caching layer stores a response influenced by an input it does not include in the cache key (an “unkeyed” input), you can poison the cached copy so unsuspecting users get your payload.
This is different from web cache deception (tricking a cache into storing private data). Here you are corrupting a shared, public cache entry.
The core idea: keyed vs unkeyed inputs
A cache decides what to store using a cache key — usually method, host, and path, sometimes a few headers. Anything the response depends on but the key ignores is unkeyed and dangerous:
- Custom headers like
X-Forwarded-Host,X-Forwarded-Scheme,X-Host - Headers reflected into HTML, redirects, or script sources
- Query parameters excluded from the key
- Cookies that influence output but not caching
If an unkeyed input changes the response, and that response gets cached, you can poison it.
Detection workflow
- Add a harmless cache buster to keep experiments isolated, for example
?bugflarecb=1. - Send suspicious headers (like
X-Forwarded-Host: bugflare-poc.example) and watch whether the value is reflected in the body, a redirect, or a resource URL. - Inspect caching signals:
Cache-Control,Age,X-Cache: hit/miss, and CDN-specific headers. - If your unkeyed input is reflected and the response is cacheable, confirm a second plain request (same buster) returns the poisoned copy.
Keep every test on your own cache-buster value so you never serve a poisoned response to real users.
Proving impact responsibly
Strong impact examples:
- Reflected value becomes executable (cached XSS served to others on your buster path)
- Poisoned redirect points to an attacker-controlled location
- Resource import (script/style) swapped to a malicious host
Prove it on an isolated key, capture the cached response, and stop. Do not poison a production home page or a real high-traffic path.
Why programs care
One poisoned entry can hit thousands of users with no interaction. That mass, no-click quality often pushes severity to high or critical when the reflected input reaches a dangerous sink.
Common pitfalls
- Reflection that is present but never cached (check
X-Cache) - A value keyed by the cache (changing it just makes a new entry, not a poison)
- Self-only effects that never reach another request
Report structure
Explain the unkeyed input, the reflection sink, the caching evidence, and the payload delivered to a second request. Recommend adding the input to the cache key, stripping untrusted forwarding headers at the edge, and refusing to cache responses that vary on user-controlled headers.
Defensive checklist
- Include every response-affecting input in the cache key, or normalize it away.
- Strip or validate
X-Forwarded-*and similar headers at the edge. - Never reflect request headers into HTML, redirects, or resource URLs.
- Set precise
Cache-ControlandVaryvalues. - Test caches with header fuzzing in staging.
Web cache poisoning rewards hunters who understand the boundary between the app and the CDN. Map the cache key, find the unkeyed input, and prove one clean poisoned hit.
Original Bugflare guide informed by PortSwigger web cache poisoning research.