Skip to main content
Back to blog
April 28, 2026|8 min read

Web Cache Poisoning: How Attacks Work and How to Prevent Them

Web cache poisoning exploits the gap between what a cache stores and what it checks — attackers inject malicious responses that get served to thousands of users. Here's how it works and how to stop it.

ZeriFlow Team

1,403 words

Web Cache Poisoning: How Attacks Work and How to Prevent Them

Web cache poisoning is an attack technique where an adversary manipulates a caching layer into storing and serving a malicious HTTP response to all users who subsequently request the same resource. Unlike attacks that target a single user, a successfully poisoned cache entry becomes a persistent threat that affects every visitor until the cache is purged — making it one of the most scalable web vulnerabilities in existence.

Check your site's Cache-Control headers and security configuration with ZeriFlow — free, instant results.


How Web Caches Work

Web caches (CDNs, reverse proxies like Varnish or Nginx, or browser caches) store copies of HTTP responses to reduce load and improve performance. When a request comes in, the cache checks a *cache key* — typically the URL and sometimes specific headers — to decide whether to return a stored response or forward the request to the origin server.

The critical concept: not all headers that influence the response are included in the cache key. These are called unkeyed inputs.


What Are Unkeyed Headers?

An unkeyed header is a request header that the origin server reads and uses to construct its response — but which the cache ignores when deciding whether to serve a cached copy.

Classic examples: - X-Forwarded-Host - X-Forwarded-Scheme - X-Original-URL - X-Rewrite-URL - X-HTTP-Method-Override

If the origin server uses X-Forwarded-Host to construct absolute URLs in the response (for redirects, canonical tags, or JavaScript), but the cache only keys on the URL path, then an attacker can:

  1. 1Send a request with a malicious X-Forwarded-Host: evil.com
  2. 2The origin generates a response containing https://evil.com/script.js
  3. 3The cache stores this poisoned response keyed to the normal URL
  4. 4Every subsequent user requesting that URL gets the poisoned response with the malicious script

Step-by-Step: A Cache Poisoning Attack

Step 1: Identify Unkeyed Inputs

The attacker sends requests with varying header values and observes whether the response changes while the cache key stays the same. Tools like Burp Suite's Param Miner automate this process.

GET / HTTP/1.1
Host: targetsite.com
X-Forwarded-Host: evil.com

If the response contains evil.com anywhere (in a redirect, a script src, a canonical URL), the attacker has found an exploitable unkeyed input.

Step 2: Craft the Malicious Response

The attacker crafts a request that causes the origin to generate a response containing malicious content — a JavaScript injection, a redirect to a phishing page, or a poisoned resource URL.

GET / HTTP/1.1
Host: targetsite.com
X-Forwarded-Host: evil.com
Cache-Buster: unique123

Step 3: Poison the Cache

The attacker times the request to hit the cache during its refresh cycle — or uses a cache buster on the first request (to reach the origin), removes it on the second request to prime the cache.

Once cached, every user who requests the page receives the poisoned version until the cache TTL expires or is manually invalidated.


Real-World Cache Poisoning Variants

DOM-Based Cache Poisoning

If a page reads query parameters from document.location and passes them to innerHTML or eval, an attacker can inject a malicious query parameter that gets cached and delivered to all users.

Fat GET Requests

Some servers process request bodies for GET requests. If the cache ignores the body but the origin reads it to generate the response, the body becomes an unkeyed vector.

Host Header Poisoning

Web frameworks often trust the Host header to generate self-referencing links. If an intermediate proxy rewrites Host before it reaches the cache, poisoning becomes straightforward:

GET /login HTTP/1.1
Host: evil.com

Password reset emails, OAuth redirects, and absolute URLs in responses are all impacted.

Cache Parameter Cloaking

Query string parsing inconsistencies between the cache and the origin can allow attackers to inject parameters the cache ignores but the origin processes.


Cache-Control Headers: Your Primary Defense

The Cache-Control header is your most powerful tool against cache poisoning. ZeriFlow checks Cache-Control as part of its security header audit.

For sensitive or dynamic pages:

Cache-Control: no-store, no-cache, must-revalidate

For static assets you do want cached:

Cache-Control: public, max-age=31536000, immutable

Key directives: - no-store: Never cache this response, not even conditionally - no-cache: Cache it, but always revalidate with the origin before serving - private: Only cache in the user's browser, not in shared CDN/proxy caches - Vary: Tell the cache to include specific headers in the cache key


The Vary Header: Making Unkeyed Headers Keyed

If your server legitimately uses a header like Accept-Language or X-Forwarded-Host to vary the response, you can instruct the cache to include it in the key using the Vary header:

Vary: Accept-Language, X-Forwarded-Host

This forces the cache to store separate copies for each unique value of those headers, neutralizing the unkeyed input attack vector for those specific headers.


Preventing Cache Poisoning: Complete Checklist

  1. 1Audit what your origin trusts: Does your application read X-Forwarded-* headers? Does it trust the Host header for URL generation? Every such header is a potential vector.
  1. 1Configure your cache's key properly: Review your CDN or reverse proxy configuration to ensure the cache key includes all headers that influence the response.
  1. 1Set strict Cache-Control on dynamic responses: Any response that is personalized, user-specific, or security-sensitive should have Cache-Control: no-store or private.
  1. 1Validate and sanitize header values: Before using any header value in a response, validate it against a whitelist of expected values.
  1. 1Use `Vary` correctly: If a header legitimately varies your response, include it in Vary. If it doesn't, strip it at the proxy layer before it reaches your origin.
  1. 1Monitor cache hit rates and anomalies: A sudden spike in cache hits for a specific URL or unusual cache key patterns can indicate active poisoning.

ZeriFlow and Cache-Control Auditing

ZeriFlow's security scanner checks your site's Cache-Control, Pragma, and related caching headers as part of its 80+ security checks. Misconfigured caching headers are one of the most common findings — and one of the easiest to fix.

Run a free ZeriFlow scan to see how your caching headers and security configuration stack up.


FAQ

Q: Is web cache poisoning the same as cache deception?

A: No, they're opposite attacks. Cache poisoning involves getting the cache to store a malicious response that is then served to victims. Cache deception tricks the cache into storing a *victim's* sensitive response, which the attacker can then retrieve. Both exploit the same cache architecture, but with different goals and methods.

Q: Can HTTPS prevent cache poisoning?

A: No. HTTPS encrypts the transport layer, but cache poisoning happens at the application layer. The cache operates on decrypted responses. HTTPS is essential for other reasons but does not protect against cache poisoning.

Q: Do CDNs protect against web cache poisoning by default?

A: CDNs can be the vectors for cache poisoning, not the defenders. CDN configurations that fail to key on all response-influencing headers are the most common source of cache poisoning vulnerabilities. Each CDN (Cloudflare, Fastly, CloudFront) has its own default behavior — review yours carefully.

Q: How do I test my own site for cache poisoning?

A: Use Burp Suite with the Param Miner extension, which automates the discovery of unkeyed headers and parameters. James Kettle's research on cache poisoning (PortSwigger Web Security) provides a comprehensive methodology. Always test on a staging environment first.

Q: What is the difference between web cache poisoning and DNS cache poisoning?

A: DNS cache poisoning targets DNS resolvers to redirect domain lookups to malicious IPs. Web cache poisoning targets HTTP caches to serve malicious HTTP responses. Both exploit caching mechanisms but operate at different layers of the network stack.


Conclusion

Web cache poisoning is a force multiplier for attackers — a single successful exploit affects every user who visits your site until the cache is cleared. The defenses are well-understood: audit your caching headers, restrict what your origin trusts, configure Vary correctly, and apply strict Cache-Control to dynamic responses.

The first step is knowing where you stand. Automated security scanning gives you the baseline.

Scan your site with ZeriFlow now and find out whether your caching configuration is putting your users at risk.

Ready to check your site?

Run a free security scan in 30 seconds.

Related articles

Keep reading