Web cache poisoning and deception testing
How to test web cache poisoning and cache deception safely: cache keys, URL normalisation, CDN policy, Vary, private responses, detection and hardening.
- AUTHOR
- Karol Rapacz / CEO of Breachroad · OSCP · PNPT
- PUBLISHED
- 5 April 2026
- READING TIME
- 20 min read
- TOPIC
- Penetration Testing and AppSec
Web cache poisoning and web cache deception both exploit disagreement between a cache and an origin, but their outcomes differ. In poisoning, an attacker influences a response stored under a shared key, so altered content reaches later users. In deception, a victim opens a crafted path, the origin produces a private response, and an intermediary mistakenly treats it as a public resource and stores it.
The short answer: safe cache penetration testing begins by reconstructing the cache key and cacheability policy at every layer. It then uses random parameters, synthetic data and a dedicated route to examine unkeyed components, path normalisation and protection of authenticated responses. The tester never poisons a popular page or attempts to retrieve a real user’s cached entry. Every proof must be removable immediately with a targeted purge.
The topic extends beyond a CDN. Caching can exist in a browser, reverse proxy, gateway, framework, HTML fragment layer, service worker and mobile application. Two layers may use different keys and decode a URL differently, creating a vulnerability even when each configuration appears reasonable in isolation.
Cache key and cache policy are different mechanisms
A cache makes two fundamental decisions:
- whether a response may be stored, based on method, status, response fields, configuration and product rules;
- which key identifies it, commonly using scheme, host, method, path, query string and selected headers or cookies.
RFC 9111 specifies HTTP cache semantics, including private, no-store, s-maxage and Vary. A CDN can add its own rules and override origin behaviour. The Cloudflare Cache Keys documentation illustrates configuration involving query parameters, headers, cookies, host and user features.
Security depends on agreement between three functions:
| Function | Question | Disagreement risk |
|---|---|---|
| request parser | what are the host, path and parameter? | CDN and origin normalise differently |
| cache-key generator | which inputs distinguish responses? | a material header or cookie is omitted |
| cacheability policy | may this response be shared? | private content is classified as static |
A request component omitted from the key is an unkeyed input. That alone is not a vulnerability. The problem begins when it changes a response that is later shared with other users under the same key.
Web cache poisoning versus web cache deception
How cache poisoning works
In a typical condition, a proxy omits a header or parameter from the key while the application uses that input to generate a link, redirect, security header or HTML fragment. An attacker creates a response variant containing a controlled canary. If the cache stores it under the key used by ordinary requests, a security-relevant collision exists.
Impact depends on the reflection point. It may be only an incorrect link, but it can also create stored XSS, an external redirect, a changed JavaScript import, information exposure or an availability problem. The primary research Practical Web Cache Poisoning and Web Cache Entanglement shows how risk frequently emerges between cache layers and normalisation mechanisms.
How cache deception works
In deception, the origin and cache disagree about whether a path is dynamic. An application may ignore an extra segment after a valid account route and still return the account page, while a CDN recognises an apparent static extension and stores the response. Other causes include different treatment of encoded delimiters, semicolons, dot segments or percent encoding.
The attack normally requires a logged-in victim to visit a unique URL before the attacker retrieves the stored copy. This is not classic poisoning: the attacker does not create harmful content, but causes the victim’s response to persist. OWASP WSTG: Test for Path Confusion describes this category of routing and cache disagreement.
Why cache behaviour is difficult to test
One URL may traverse a browser cache, CDN point of presence, regional shield, reverse proxy and application cache. Each has a different TTL, scope, purge mechanism and diagnostic fields. A HIT does not establish which level responded. Missing Age does not prove caching is absent because products can remove diagnostics.
Additional sources of uncertainty include:
- compression variants and
Accept-Encoding; - geographic points of presence and regional origins;
- personalisation through cookies or language headers;
- stale-while-revalidate and asynchronous refresh;
- fragment caching inside the application;
- cached WAF errors and redirects;
- query strings normalised in a different order;
- differences between
HEADandGET; - a post-transformation key that differs from the URL sent upstream.
Every test therefore needs a correlation marker, repeatable sequence and several independent observations. A random cache buster is a safety control: it isolates the tester’s entry from public traffic.
Scope and rules for safe cache testing
Agree on layers, domains, environments, accounts and a purge procedure first. The CDN team should confirm whether test URLs can use a separate namespace or dedicated rule. Rules of engagement should prohibit:
- poisoning the home page, login or high-traffic assets;
- using a real user’s information;
- persistent availability testing without separate approval;
- bulk warming of points of presence;
- guessing unique URLs created by other people;
- leaving test entries behind.
The best evidence is a synthetic response in a test tenant. For poisoning, place a harmless token in a controlled field and show it appears on a request without that input but with the same isolated key. For deception, sign in as a tester, create a fake “secret” with no production value, open a unique path and retrieve it from a second tester-controlled session.
This work belongs within a wider web application penetration test, because caching frequently amplifies an existing XSS, redirect, authorisation or disclosure flaw.
Technical web cache poisoning methodology
1. Map every layer and rule
Collect the CDN configuration, cache rules, origin response fields, reverse-proxy settings, framework behaviour and service workers. For every route, record TTL, cacheable statuses, methods, query policy, cookies, Vary, bypass conditions, stale behaviour and purge controls. Separate public content from responses that depend on identity, role, language, device or tenant.
Build an expected variant table. If the response changes with language but language is neither in the key nor represented by a correct Vary, the design is defective even before exploitability is considered.
2. Reconstruct the effective cache key
Send request pairs that differ in only one component: a parameter, parameter order, header, cookie, host, port or path encoding. Give every pair a separate random namespace. Compare body hashes, status, Age, ETag, product-specific fields and origin logs.
If the second request never reaches the origin and receives the first variant, the changed component is probably absent from the key. Repeat several MISS→HIT cycles because origin retries and load balancing can imitate cache behaviour.
3. Find unkeyed inputs that change the response
Review forwarding fields, scheme and host information, alternate paths, content negotiation, cookies and parameters omitted by policy. Use neutral canary text only. Determine whether it affects a redirect, canonical URL, link, script, metadata or response field.
Do not begin with active XSS strings. First establish the dependency: input A changes field B, and B is returned to ordinary request C. That proves the mechanism and supports impact analysis without exposing users.
4. Examine normalisation and layered caches
Compare URLs before and after decoding, handling of delimiters, repeated slashes, trailing dots, case and path parameters. Work only inside the dedicated route. The security question is whether the outer and inner layers create different equivalence classes for the same address.
With two caches, distinguish four states: miss/miss, hit/miss, miss/hit and hit/hit. A weakness may appear only when an inner cache transforms a response subsequently stored by the CDN. If request boundaries can also diverge in the chain, run a separate HTTP request smuggling and desync assessment, because a poisoned connection can change the input that reaches the key generator.
5. Confirm scope and purge the entry
Determine whether an entry is local to one point of presence, regional or global without generating traffic to every node. Separate technical distribution from likely business impact in the report. Purge the test key immediately and verify in telemetry that it is no longer served.
Technical web cache deception methodology
1. Select a synthetic private response
Use an account with no real data and place a random token in its profile. Establish that the normal route has Cache-Control: private, no-store or bypasses the CDN. Exercise only unique path variants that no user will visit accidentally.
2. Compare origin routing with CDN classification
The origin should return 404 for unknown suffixes rather than tolerating arbitrary path_info. The CDN should not determine cacheability solely from an apparent extension. Compare both log sets: which path reached the application and which key stored the response?
3. Verify session isolation
After generating the response in the test session, request it without cookies or from a second tester account. If the synthetic token is served from cache without an origin fetch, confidentiality is broken. Stop, purge and do not attempt to retrieve another account’s data.
4. Test redirects and errors
Some stacks store 301, 302, 404 or WAF responses too aggressively. Determine whether an authenticated error becomes a public variant and whether Set-Cookie causes a bypass or is stripped. Session-dependent content must not be stored simply because its status resembles a static response.
Detecting cache poisoning and deception
Correlate application, edge and configuration records. High-value signals include:
- a cache HIT for a response containing
Set-Cookie, account data or authorisation context; - materially different response hashes for one cache key;
- an origin path different from the CDN’s normalised key;
- unusual purge volume or unexpected cache-rule changes;
- a public HIT immediately after an authenticated MISS;
- an unknown request field changing a body or redirect despite being absent from the key;
- a private route showing a positive TTL or
Age; - apparent static-file suffixes added to account routes.
Store response hashes and data classifications, not complete private bodies. Continuous checks can compare an expected variant model with the deployed CDN configuration. Treat cache-rule changes like application code and require review.
Hardening cache configuration
- Explicitly cache known public resources instead of attempting to exclude every private route.
- Never place responses dependent on
Authorization, sessions or user data in a shared cache without a formally designed private variant. - Set
Cache-Control: no-store, privatefor sensitive content and ensure the CDN does not override it. - Include every input that legitimately changes the response in the key; remove unnecessary unkeyed inputs.
- Use
Varydeliberately and test cache-key explosion risk. - Apply identical URL normalisation at the edge and origin, forwarding the same representation used to construct the key.
- Reject unexpected suffixes and path parameters on dynamic routes.
- Do not classify content as static by extension alone; use explicit directories or an asset manifest.
- Separate static asset hosts from authenticated applications.
- Limit caching of errors, redirects and WAF responses; do not enable it implicitly.
- Monitor and version configuration, then test MISS/HIT behaviour for every role after changes.
- Provide fast purge by URL, tag and namespace, and rehearse incident handling.
Map these controls to OWASP ASVS 5. For service interfaces, add the REST, GraphQL and OAuth pentest checklist; an API gateway cache can bypass correct backend authorisation. If the CDN fronts several cloud origins, an AWS, Azure and GCP cloud pentest should cover storage, load balancers and private endpoints.
Pre-deployment and retest checklist
- We have mapped every cache from browser to application.
- Every route has a documented cacheability rule, key, TTL, scope and purge path.
- User-dependent data never enters a shared cache.
- Every legitimate response variant belongs in the key or
Vary. - Edge and origin apply identical path and query normalisation.
- Dynamic routes reject unexpected suffixes.
- Testing uses an isolated namespace and synthetic data.
- Proof never affects a popular key or real user.
- Logs correlate request, origin fetch, key, HIT/MISS and purge.
- Cache rules are version-controlled and peer-reviewed.
- Retesting covers poisoning, deception and legitimate variants.
- Incident response can invalidate an entry globally and quickly.
Primary technical sources
- RFC 9111 — HTTP Caching
- OWASP WSTG — Test for Path Confusion
- Cloudflare Docs — Cache Keys
- PortSwigger Research — Practical Web Cache Poisoning
- PortSwigger Research — Web Cache Entanglement
Concerned about a CDN or reverse-proxy rule? An authorised assessment should reconstruct every layer’s key and policy, use synthetic information, and finish with a controlled purge and retest. That demonstrates real impact without putting customer traffic at risk.


