Web Cache Poisoning: Detection, Attack Techniques, and Defense Guide

Retool's new app builder is where AI-generated code ships safely
Building apps with AI is easy. Getting them to production safely is another story.
Web caches exist to improve performance: they store responses to frequently requested URLs and serve subsequent requests from cache rather than the origin server. The security assumption is that all requests with the same cache key should receive the same response. Web cache poisoning breaks this assumption by exploiting headers or parameters that the origin server uses to generate the response but that the cache does not include in the cache key.
The research by James Kettle at PortSwigger documented this vulnerability class extensively in 2018 and 2019, demonstrating that it affected major internet services at scale. The impact of cache poisoning is multiplicative: a single malicious request can poison a cache entry that is subsequently served to thousands of users, delivering XSS payloads, open redirects, or malicious script imports with no per-victim interaction required.
Cache poisoning attack variants
Cache poisoning manifests in several distinct forms depending on which unkeyed input the attacker exploits and how the origin server uses that input to generate responses. Understanding each variant helps security teams prioritize what to test and which cache configurations to audit first. The four main vectors are Host header injection via X-Forwarded-Host, parameter cloaking through stripped query strings, request smuggling-based poisoning, and fat GET body injection. Each requires a slightly different detection approach and a targeted remediation at either the cache key configuration or the origin server request handling layer.
X-Forwarded-Host injection: poisoning via URL construction
The most impactful cache poisoning variant. Many web frameworks (Django, Rails, Spring, Laravel) use the X-Forwarded-Host header (set by proxies) to construct absolute URLs in responses: redirect headers, canonical URL meta tags, script src attributes, and API endpoints in JavaScript. If a CDN strips X-Forwarded-Host from the cache key but forwards it to the origin, an attacker can supply X-Forwarded-Host: attacker.com, receive a response with <script src='https://attacker.com/payload.js'>, and have that response cached. The attacker then hosts a malicious payload.js that executes in the context of the victim site's origin, enabling full XSS against cached page visitors.
Parameter cloaking: poisoning via URL parameter confusion
Some CDNs and caching layers normalize URLs by removing certain query parameters (tracking parameters, analytics tags) from the cache key. If the origin server processes these parameters before the CDN strips them, a poisoning opportunity exists. Example: if the CDN strips ?utm_source from the cache key, but the origin reflects utm_source in the response (in a meta tag or analytics script), an attacker can send a request with a malicious utm_source value, poison the cache, and subsequent clean requests receive the reflected malicious value. Test by adding parameters to common URL structures: UTM parameters, AMP parameters, and pagination parameters are frequent targets.
Request smuggling-based cache poisoning
HTTP request smuggling can be combined with cache poisoning to poison specific cache entries. A smuggled request can deliver a response from a non-existent path to the next user's request for a legitimate path. If that response is cached, all subsequent requests for the legitimate path receive the smuggled response. This requires both request smuggling and a caching layer, but the combination amplifies the impact significantly: a single smuggling request can poison a cache entry that affects thousands of subsequent users. Detection and remediation requires addressing both the smuggling vulnerability (HTTP/2 or header normalization) and the cache configuration.
Fat GET poisoning: body injection in GET requests
Some servers process request bodies in GET requests even though RFC semantics say GET requests should not have bodies. If a caching layer caches based on the URL alone (ignoring the request body for GET requests) but the origin processes the GET body, an attacker can inject content into the response via the GET body without affecting the cache key. The cache stores a poisoned response indexed by the URL only. This is less common than header-based poisoning but has been found in Nginx, Apache, and custom server implementations that process GET request bodies.
Detection methodology
Detecting cache poisoning requires both automated header discovery and careful manual verification to confirm that a poisoned response is actually served from cache to subsequent requests. The testing process must be conducted with cache-busting parameters to avoid poisoning live cache entries that would affect real users during the assessment. Burp Suite's Param Miner extension automates the tedious work of discovering which headers and parameters are unkeyed, while manual verification confirms that a discovered unkeyed input actually makes it into the cached response. The full detection workflow combines Param Miner scanning, manual header injection, and a second cache-hit confirmation request.
Safe testing procedure with cache busting
Append a unique random string as a query parameter to every test request (e.g., ?cb=RANDOM123). This ensures your test requests hit the origin (cache miss) and your poisoned responses are cached under a unique cache key that real users are unlikely to request. After confirming the vulnerability with your isolated test key, do not confirm the full attack against live cache entries during testing, because that would serve the malicious response to real users. Report the vulnerability with evidence from isolated test requests. Only demonstrate impact on production cache entries with explicit written permission from the application owner.
Param Miner: automated unkeyed header and parameter discovery
Install Burp Suite's Param Miner extension. Right-click any request in Burp Proxy and select 'Guess headers' and 'Guess params'. Param Miner sends hundreds of requests with different header and parameter combinations, detects whether any cause a difference in the response, and identifies which are excluded from the cache key (by checking whether subsequent requests with the same URL but without the header receive the modified response). This automates the most tedious part of cache poisoning testing: discovering which unkeyed inputs influence the response.
Manual X-Forwarded-Host testing
Send a request with X-Forwarded-Host set to a unique value you control: GET /page HTTP/1.1\nHost: target.com\nX-Forwarded-Host: test-12345.attacker.com. Search the response body for test-12345.attacker.com. If it appears in any URL (href, src, action, meta content, JavaScript variable), the origin is using X-Forwarded-Host for URL construction. Then send the same request without X-Forwarded-Host and check whether the response still contains the test domain (cache hit with poisoned entry). If yes, the endpoint is cacheable and vulnerable to cache poisoning via X-Forwarded-Host injection.
Briefings like this, every morning before 9am.
Threat intel, active CVEs, and campaign alerts, distilled for practitioners. 50,000+ subscribers. No noise.
Remediation: cache key configuration and origin hardening
Remediating cache poisoning requires changes at two layers simultaneously: the caching infrastructure must be configured to include all response-influencing headers in its cache key, and the origin server must be hardened to validate proxy headers before using them in URL construction. Fixing only the cache layer leaves a window of risk if cache configuration drifts. Fixing only the origin layer is more robust but requires verifying that every framework and library function that constructs URLs does so from validated inputs. The most durable fix applies both controls together and adds automated tests that confirm poisoning attempts fail after each deployment.
Include all response-influencing headers in the cache key
Audit which request headers and parameters the origin server uses to construct response content, and verify each is included in the cache key. In Cloudflare: use Cache Rules with 'Vary by header' to include custom headers. In AWS CloudFront: configure Origin Request Policies and Cache Policies to include the relevant headers in the cache key. In Nginx proxy_cache: use the proxy_cache_key directive to include all varying headers. In Varnish: add hash_data() calls in vcl_hash for each header. The principle: if the origin server reads a request attribute to produce the response, that attribute must be in the cache key.
Validate proxy headers on the origin server
Do not use unvalidated proxy headers (X-Forwarded-Host, X-Forwarded-For, X-Original-URL) directly in URL construction without validating them against an allowlist of permitted values. In Django: set USE_X_FORWARDED_HOST = True only if you trust the proxy; set ALLOWED_HOSTS to the specific permitted domains, and Django will raise SuspiciousOperation if an X-Forwarded-Host value is not in ALLOWED_HOSTS. In Rails: configure config.action_dispatch.trusted_proxies to specific IP ranges. In Spring Boot: use server.forward-headers-strategy=FRAMEWORK with a trusted proxy configuration. Rejecting unexpected host values prevents the origin from generating responses with attacker-controlled URLs regardless of cache key configuration.
Set Vary header on origin responses
The HTTP Vary response header tells caches which request headers must match for a cached response to be served. If the origin uses X-Accept-Language to return localized content, it should include Vary: X-Accept-Language in the response. The caching layer then treats requests with different X-Accept-Language values as different cache entries. This is the standard HTTP mechanism for handling response variation: use it for any header the origin legitimately uses to modify responses. Note that some CDNs ignore or limit Vary header processing, so verify CDN-specific behavior rather than assuming RFC compliance.
The bottom line
Web cache poisoning is a multiplier: one attack, many victims. The vulnerability exists in the gap between what headers a cache uses for its cache key and what headers the origin uses to construct its response. Detection requires Param Miner to discover unkeyed inputs and careful cache-busted testing to confirm exploitability. Remediation requires two simultaneous fixes: configure the cache to include all response-influencing headers in its cache key, and configure the origin to validate proxy headers before using them to construct response content. Start by auditing which request headers your origin server reads and which are included in your CDN's cache key, then close every gap between those two sets.
Frequently asked questions
What is web cache poisoning and how does it work?
Web cache poisoning exploits the difference between a cache's key (the set of request attributes used to identify a cached response) and the attributes the origin server uses to generate the response. If the origin server uses a request header (like X-Forwarded-Host) to construct URLs in the response, but the cache does not include that header in the cache key, an attacker can send a crafted request with a malicious X-Forwarded-Host value, receive a response containing the attacker's URL (e.g., a JavaScript import from a malicious domain), and have that response stored as the cached entry for all normal requests to that URL.
What is the difference between web cache poisoning and web cache deception?
Cache poisoning causes the cache to serve a malicious response to other users. Cache deception causes the cache to store a sensitive response (like a user's account page) by tricking the cache into treating it as a cacheable static resource. In cache deception, the victim is the user whose data is cached and served to the attacker; in cache poisoning, the attacker injects malicious content and every other user is the victim. Both exploit the gap between what the cache considers a unique resource and what the origin server considers cacheable.
How do I find unkeyed headers for cache poisoning?
Use Burp Suite's Param Miner extension, which automatically discovers unkeyed headers by sending requests with custom headers and measuring whether they influence the response. Run Param Miner's 'Guess headers' feature against the target and look for headers that change the response content without appearing in the cache key (indicated by the response being cached with the same key across requests with and without the header). Common unkeyed headers include X-Forwarded-Host, X-Forwarded-For, X-Original-URL, X-Rewrite-URL, X-Host, X-Forwarded-Scheme, and custom CDN headers. Verify findings by poisoning the cache with a safe payload and confirming a second request (without the header) receives the poisoned response.
What is Host header injection in cache poisoning?
Many web frameworks use the HTTP Host header to construct absolute URLs in responses, such as redirect locations and links in HTML. Some CDNs and proxies support X-Forwarded-Host to override the Host header for proxy scenarios. If the origin server uses X-Forwarded-Host for URL construction but the CDN does not include X-Forwarded-Host in the cache key, an attacker can send a request with X-Forwarded-Host: attacker.com, receive a response with absolute URLs pointing to attacker.com (e.g., a script tag importing attacker.com/payload.js), and poison the cache. Subsequent requests without the header receive the cached response that imports the attacker's script.
How do I detect if my application is vulnerable to cache poisoning?
Test each endpoint: send a request with a custom header set to a unique value (X-Forwarded-Host: cache-poison-test-12345.com). Check whether the header value appears in the response (URL construction, meta tags, script src attributes). If yes, check whether a second request without the header returns the same response (cache hit with the injected value). Also test URL parameters: add unknown query parameters (?cachebust=uniquevalue) and check if they appear in the response. Unknown parameters that appear in responses but are excluded from the cache key enable parameter-based cache poisoning. Use Burp Suite Pro or the HTTP Request Smuggler/Param Miner extensions for automated testing.
What cache configuration changes prevent cache poisoning?
Include all response-influencing headers in the cache key. In Nginx's proxy cache: use proxy_cache_key '$scheme$request_method$host$request_uri$http_x_forwarded_host' to include X-Forwarded-Host if the origin uses it. In Varnish VCL: add all influencing headers to the hash (hash_data(req.http.X-Forwarded-Host)) in vcl_hash. In CDNs: configure custom cache key rules via the CDN dashboard (CloudFront behaviors, Cloudflare Cache Rules) to vary the cache on the specific headers your origin uses. Alternatively, configure the origin server to not use unvalidated proxy headers for URL construction, and validate Host/X-Forwarded-Host against an allowlist of permitted values.
Can HTTPS prevent web cache poisoning?
HTTPS does not prevent web cache poisoning. The attack exploits the caching layer's key construction logic, not the transport security. The attacker's malicious request is delivered over HTTPS, the cache stores the poisoned response, and subsequent HTTPS requests receive the poisoned payload. HTTPS protects data in transit but does not affect how caches key or store responses. The fix is at the caching configuration and origin server level: either include all response-influencing headers in the cache key, or prevent the origin from using unvalidated proxy headers to construct response content.
Sources & references
Free resources
Critical CVE Reference Card 2025–2026
25 actively exploited vulnerabilities with CVSS scores, exploit status, and patch availability. Print it, pin it, share it with your SOC team.
Ransomware Incident Response Playbook
Step-by-step 24-hour IR checklist covering detection, containment, eradication, and recovery. Built for SOC teams, IR leads, and CISOs.
Get threat intel before your inbox does.
50,000+ security professionals read Decryption Digest for early warnings on zero-days, ransomware, and nation-state campaigns. Free, daily, no spam.
Unsubscribe anytime. We never sell your data.

Founder & Cybersecurity Evangelist, Decryption Digest
Cybersecurity professional with expertise in threat intelligence, vulnerability research, and enterprise security. Covers zero-days, ransomware, and nation-state operations for 50,000+ security professionals every morning.
Win a $2,495 Black Hat pass.
Full-access to Black Hat USA 2026 in Las Vegas. Subscribe free to enter.
