Web cache poisoning is an offensive technique that turns a legitimate cache into a payload distribution mechanism. Instead of attacking a single victim directly, the attacker gets a caching layer (a CDN, a reverse proxy such as Varnish or Nginx, or the application cache itself) to store a malicious response and then serve it to every user who requests that same URL. A single well placed payload can reach thousands of requests, and that is exactly where its severity comes from. In this article we look at it from the pentester's point of view: how the exploitable inputs are discovered, how the impact is confirmed and how it is chained with other vulnerabilities.
Let us make one thing clear from the start: we are talking about application layer (HTTP) caching, not DNS cache poisoning or ARP cache poisoning. They share part of the name but live in different layers, and we break that down further below.
What web cache poisoning is
A web cache keeps copies of responses so it can serve them quickly without bothering the origin server again. To decide whether two requests are "the same" and can share a response, the cache computes a cache key. The attack consists of finding a component of the request that:
- Influences the content of the response the origin generates.
- Is not part of the cache key, so the cache ignores it when indexing.
That component is called an unkeyed input. If the attacker sends a request with a malicious unkeyed input, the origin produces a poisoned response, the cache stores it under the "clean" key of the URL and, from that moment on, serves it to victims who never even sent that header.
It is a variant of the configuration and injection flaws we review in the 5 most common web vulnerabilities, but with a reach multiplier: the cache distributes the payload on our behalf.
The cache key and unkeyed inputs
What a cache actually compares
By default, most caches build the key from the HTTP method, the host and the path with its query string. In other words, GET https://shop.com/product?id=5 is usually a complete key. What is interesting for the attacker is everything that stays outside that key and still reaches the origin:
- Headers such as
X-Forwarded-Host,X-Forwarded-Scheme,X-Forwarded-Proto,X-Host,X-Original-URLorX-Forwarded-Server. - Custom headers introduced by the framework or the load balancer.
- Query parameters the cache decides to exclude from the key (for example
utm_sourceand other tracking parameters, or parameters the application considers irrelevant).
Unkeyed inputs: the attack surface
The Vary header lets the origin state which headers should be part of the key (typically Accept-Encoding or Accept-Language). Anything that is not in Vary and not in the default key is a candidate unkeyed input. The first step of a web cache poisoning assessment is therefore to map which inputs affect the response without affecting the key.
Vectors from an offensive perspective
X-Forwarded-Host and reflected proxy headers
The classic case, documented by James Kettle in his research "Practical Web Cache Poisoning" (PortSwigger, 2018), is an application that builds absolute URLs from X-Forwarded-Host. Many frameworks trust that header to generate canonical links, import resources or form script paths:
GET / HTTP/1.1
Host: victim.com
X-Forwarded-Host: attacker.com
If the response reflects the value without validating it, the origin can return something like:
<script src="https://attacker.com/static/analytics.js"></script>
Once cached, that response makes every visitor load attacker controlled JavaScript: a de facto stored XSS served by the cache itself. The variant with X-Forwarded-Scheme: nothttps or X-Forwarded-Proto can force redirects (301 or 302) toward the attacker, or redirect loops that degrade the service.
Cache key normalization flaws
The cache and the origin do not always interpret a URL the same way. If the cache normalizes the key (decoding %2F, ignoring case, trimming the fragment or stripping certain characters) differently from how the origin processes the path, the attacker can poison a key that the cache considers equivalent to the victim's URL. Kettle expanded on these scenarios in "Web Cache Entanglement" (2020), where he shows that parsing inconsistencies between layers are an endless source of colliding keys.
Fat GET and unkeyed parameters
A fat GET is a GET request that carries parameters in the body. Some frameworks process them even when the method is GET, while the cache only indexes the query string. The result: two requests with the same key but a different body, one of them poisoned. The same principle applies to query parameters excluded from the key: if the cache drops lang when computing the key but the origin uses it to render content, that parameter becomes a vector. These abuses of parameter semantics share a philosophy with business logic flaws: the system does exactly what you ask of it, but nobody anticipated that combination.
Chaining the poisoning: XSS, open redirect and DoS
Web cache poisoning is rarely the final impact: it is the amplifier. The most common chains in an assessment are:
- Reflected XSS to mass XSS. A parameter or header that was reflected in the response and that, on its own, required the victim to click a crafted link, is now served to every user of the cached URL. Cross-site scripting no longer needs per victim interaction.
- Mass open redirect. If the cached response redirects based on a reflected header, all the traffic for that path ends up on the attacker's domain, useful for phishing or OAuth token theft.
- Resource import. Poisoning the
srcof a script, a CSSimportor a<base href>allows loading external code with the trust of the legitimate domain. - Cache-Poisoned DoS (CPDoS). Described by Nguyen and Lo Iacono in 2019, it consists of sending a malformed header (oversized, with metacharacters or with
X-HTTP-Method-Override) that makes the origin return a400or404, and the cache stores that error, denying service to legitimate users.
GET / HTTP/1.1
Host: victim.com
X-Metachar-Header: %00
In a pentest, combining this with SSRF or with OWASP Top 10 2025 issues usually raises the severity from "curious reflection" to "compromise of every user on the path".
Detection with Burp Suite and Param Miner
The standard workflow for hunting web cache poisoning combines manual reconnaissance and automation with Burp Suite:
- Identify the cache. Look in the responses for headers such as
Age,X-Cache: hit/miss,CF-Cache-Status,X-Served-Byor response times that drop sharply on the second request. - Use a cache buster. Add a unique parameter (
?cb=837462) to the URL during testing. It is a golden rule: it isolates your key so you do not poison real production users while you investigate. - Discover unkeyed inputs with Param Miner. The PortSwigger extension offers "Guess headers" and "Guess GET/POST params": it sends hundreds of known headers and parameters with a built in cache buster and reports which ones influence the response without being part of the key.
- Confirm persistence. Send the poisoned request, then repeat it without the malicious header (same key) and check that the poisoned response is served from the cache.
This methodical approach is the same one we apply in any web pentest, mapping the finding, measuring its blast radius and documenting the exact key that ends up poisoned.
Web cache poisoning is not DNS or ARP cache poisoning
It is a common confusion because of the shared name, so it is worth separating clearly:
- DNS cache poisoning. Corrupts the cache of a DNS resolver so a domain points to an attacker IP (the Kaminsky attack is the canonical example). It operates on name resolution, not on HTTP.
- ARP cache poisoning. A layer 2 attack inside a LAN: it forges the ARP table to sit as an intermediary (man in the middle) between two hosts. It is a sniffing and spoofing vector on the local network.
- Web cache poisoning. Acts at layer 7 (application), on HTTP caches such as CDNs, reverse proxies or the browser cache. It touches neither DNS nor the link layer.
The practical difference for the auditor is the toolset: here there is no ettercap or resolver spoofing, but rather Burp Suite, HTTP header analysis and a fine understanding of how each CDN computes its key.
How an organization defends against it
From the defensive side, the countermeasures are straightforward once the mechanism is understood:
- Treat
X-Forwarded-Hostand everyX-Forwarded-*header as untrusted input: never reflect them in the response without strict validation. - Include in the cache key every input that affects the response, or strip it at the edge before it reaches the origin.
- Configure
Varycorrectly and disable caching of dynamic or user specific responses. - Normalize the key consistently between cache and origin to avoid parsing collisions.
Frequently asked questions
How does web cache poisoning differ from web cache deception?
They are cousins, but opposites. Poisoning injects a malicious response into the cache to serve it to others. Web cache deception tricks the cache into storing a victim's private response (for example, their profile page) under a URL the attacker can retrieve later. One distributes payloads, the other steals sensitive data.
What exactly is an unkeyed input?
It is any component of the request that changes the origin response but that the cache does not include when computing its key. Headers such as X-Forwarded-Host, query parameters excluded from the key or the body of a fat GET are typical examples. They are the main attack surface of web cache poisoning.
Is Param Miner enough to detect every case?
Param Miner automates the discovery of unkeyed headers and parameters that are reflected in the response, which covers most common cases. It does not replace the manual analysis of key normalization flaws or of parsing inconsistencies between cache and origin, where you need to reason about each layer separately.
Does a WAF stop cache poisoning?
Only partially. A WAF can block obvious XSS payloads, but it does not solve the underlying problem: that an untrusted input reaches the origin and stays outside the cache key. The real fix lives in the cache configuration and in not reflecting proxy headers.
At Secra we audit HTTP caches, CDNs and reverse proxies within our web and mobile audit service, with a focus on unkeyed inputs, key normalization and chaining with XSS, open redirect and CPDoS. Every finding arrives with reproduction, the exact poisoned key, a severity justified by business impact and a prioritized remediation plan. If you want a proposal for your application, reach out through contact.
About the author
Secra Solutions team
Ethical hackers with OSCP, OSEP, OSWE, CRTO, CRTL and CARTE certifications, 7+ years of experience in offensive cybersecurity, and authors of CVE-2025-40652 and CVE-2023-3512.

