Web Cache Poisoning
detect, understand, remediate
Web cache poisoning manipulates caching systems by injecting malicious content through unkeyed inputs (headers, cookies), causing the poisoned response to be served to every subsequent visitor.
No credit card required. Free plan available forever.
What is web cache poisoning?
Web cache poisoning (CWE-349) is an attack technique where an adversary manipulates a web cache to serve malicious content to other users. The attack works by finding inputs that are not part of the cache key (called "unkeyed" inputs) but still influence the server's response. When the attacker injects a payload through an unkeyed input, the poisoned response is cached and subsequently served to every user who requests the same resource.
Modern web architectures rely heavily on caching layers, including CDNs, reverse proxies, and application-level caches, to improve performance and reduce server load. This makes cache poisoning a high-impact vulnerability because a single poisoned request can affect thousands or millions of users. The attack is closely related to HTTP request smuggling, as both exploit discrepancies in how intermediaries and backends process requests.
Cache poisoning attacks can deliver a range of payloads, from stored cross-site scripting that executes in every visitor's browser, to redirects that send users to phishing sites, to denial-of-service conditions where error pages are cached for popular resources. The vulnerability is especially dangerous because it persists until the cache entry expires or is purged, and the attacker does not need to maintain an active connection. Host header injection is one of the most common vectors used to achieve cache poisoning in practice.
How it works
Identify cached responses
The attacker probes the target to determine which responses are cached by examining cache-related headers (X-Cache, Age, CF-Cache-Status) and observing response time patterns for repeated requests.
Find unkeyed input
Using cache buster parameters to isolate tests, the attacker systematically injects values into HTTP headers (X-Forwarded-Host, X-Original-URL), cookies, and other inputs that are not included in the cache key.
Inject payload via unkeyed input
Once an unkeyed input that reflects in the response is found, the attacker injects a malicious payload, such as a script tag via X-Forwarded-Host that gets reflected in link or script elements on the page.
Poisoned response served to users
The server generates a response containing the attacker's payload, the cache stores it under the legitimate cache key, and every subsequent user who requests that URL receives the poisoned content.
Common causes
Caching unkeyed headers
Cache configurations that exclude HTTP headers from the cache key while the backend application reflects those headers in the response body, creating a mismatch between what is cached and what varies.
Inconsistent cache key configuration
Cache servers and CDNs that use a simplified cache key (e.g., URL path only) while the origin server generates different responses based on additional inputs like query parameters, headers, or cookies.
X-Forwarded-Host reflection
Applications that use the X-Forwarded-Host or X-Forwarded-Scheme headers to generate absolute URLs in the response (meta tags, canonical links, script sources) without including these headers in the cache key.
Fat GET requests with bodies
Some frameworks process request bodies on GET requests, and cache servers ignore the body when computing the cache key. Attackers can inject payloads in the GET body that affect the response but are invisible to the cache.
How to detect it
Automated detection
- SecPortal's external scanning analyzes cache headers across all discovered endpoints, identifying cached responses and testing for unkeyed input reflection
- Automated header injection probing tests common unkeyed headers (X-Forwarded-Host, X-Forwarded-Scheme, X-Original-URL) against cached endpoints to detect reflection
- Cache configuration auditing identifies mismatches between Vary headers and actual response variation, revealing inputs that affect responses but are excluded from the cache key
Manual testing
- Append a unique cache buster parameter to isolate test requests, then inject values into HTTP headers and observe whether the injected content appears in the cached response
- Test for X-Forwarded-Host and X-Forwarded-Scheme injection by setting these headers to attacker-controlled domains and checking if generated URLs in the response reflect the injected values
- Remove the cache buster and send the poisoned request repeatedly until a cache hit is observed, then verify from a different IP or browser that the poisoned content is served from cache
How to fix it
Restrict the cache key to include all varying inputs
Ensure every input that influences the response is included in the cache key. If the backend uses X-Forwarded-Host to generate URLs, the cache must key on that header. Use the Vary header to signal this to downstream caches.
Strip unkeyed inputs at the edge
Configure your CDN or reverse proxy to strip or normalize headers like X-Forwarded-Host, X-Original-URL, and X-Rewrite-URL before they reach the origin server, eliminating them as injection vectors.
Cache only truly static content
Limit caching to responses that are genuinely static and do not vary based on any user input. For dynamic pages, use cache-control: no-store or private directives to prevent shared caching entirely.
Add Vary headers for all dynamic inputs
When responses vary by header, cookie, or other input, include the appropriate Vary header so caches know to maintain separate entries for each variation, preventing cross-user contamination.
Implement CDN-level cache poisoning protections
Use CDN features like request header allow-listing, origin shield, and cache tag-based purging. Configure WAF rules to detect and block requests with suspicious header values targeting cache poisoning.
Compliance impact
Detect cache poisoning vectors
SecPortal identifies unkeyed inputs, cache header misconfigurations, and reflection patterns that enable cache poisoning. Start free.
No credit card required. Free plan available forever.