Skip to content
Back to Blog
·10 min read·technology

Web Cache Poisoning: The Advanced Attack Teams Overlook

How unkeyed inputs like X-Forwarded-Host let attackers poison shared CDN caches, serving malicious content at scale, and how Indian dev teams find and fix it.

BR

Bachao.AI Research Team

Cybersecurity Research

Scan Your Attack Surface

Security exposure this creates

Unpatched vulnerabilities in your tech stack are the #1 entry point for breaches targeting Indian businesses. Here's what to watch.

Web cache poisoning is an attack where a malicious response gets stored in a shared cache or CDN and then served to every subsequent visitor who requests that same URL. The attacker doesn't need to compromise your server — they only need to find an "unkeyed" input, a header or parameter the cache ignores when deciding whether two requests are the same, but that your application still uses to build the response. Send one poisoned request, and the cache does the rest: it stores the malicious page and hands it to hundreds or thousands of legitimate users automatically. This makes cache poisoning one of the highest-leverage, most under-tested attack classes in modern web infrastructure — a single request can achieve what would otherwise take a full campaign of individual attacks.

What Makes Cache Poisoning Different From Standard XSS

Most Indian dev and security teams test for reflected and stored XSS, SQL injection, and IDOR — the OWASP staples. Cache poisoning sits in a blind spot because it isn't a flaw in application logic alone; it's a mismatch between how your cache key is computed and how your origin server actually behaves. A header that has zero effect on a normal request-response cycle can become a weapon the moment it influences the response body or headers while being excluded from the cache key.

🛡️
SECURITY
Cache poisoning turns a single crafted request into a persistent, self-replicating attack. Unlike reflected XSS, which requires tricking one victim into clicking a link, a poisoned cache entry attacks every user who loads that URL until the cache expires or is purged.

Understanding the Cache Key

A cache key is the set of request attributes — typically method, host, path, and a subset of query parameters and headers — that a caching layer uses to decide whether an incoming request matches a stored response. Anything not included in the key is "unkeyed": the cache treats requests differing only in that attribute as identical, even though the origin server may generate a different response for each.

Common unkeyed inputs that attackers abuse:

    1. X-Forwarded-Host — often trusted by frameworks to build absolute URLs, canonical links, or password-reset links, but rarely included in the cache key.
    2. X-Forwarded-Scheme / X-Forwarded-Proto — can flip protocol-relative resource references.
    3. X-Original-URL / X-Rewrite-URL — used by some frameworks and reverse proxies for internal routing.
    4. Accept-Language — when reflected into an error page or an unhandled-locale redirect.
    5. User-Agent fragments reflected into device-detection redirects or mobile-optimised pages.

Discovery Methodology for Indian Dev Teams

A structured methodology finds these bugs faster than random header fuzzing. The approach below mirrors what a VAPT engagement should cover for any application sitting behind a CDN, reverse proxy, or load balancer — Cloudflare, Akamai, Fastly, AWS CloudFront, or an in-house Nginx/Varnish layer.

  1. Map the caching layer. Check Cache-Control, Age, X-Cache, CF-Cache-Status, or X-Vercel-Cache response headers to confirm a cache is in play and observe hit/miss behaviour.
  2. Enumerate candidate unkeyed headers. Send a request with a distinctive header value (X-Forwarded-Host: probe-test.example) and check whether it's reflected anywhere in the response body, a redirect Location, or a resource URL.
  3. Confirm cacheability. Repeat the exact same request without the header. If the poisoned response is served on the follow-up request, the header is unkeyed and the response is cacheable — you have a working poisoning primitive.
  4. Assess impact. Determine whether the reflected value lands somewhere exploitable: a script src, an inline attribute, a redirect target, or a canonical/OG meta tag used by search engines and social crawlers.
  5. Check cache scope and duration. A poisoned entry on a shared, edge-wide cache key affects every visitor globally until TTL expiry or purge; a poisoned entry scoped to a single edge node or a short TTL has a narrower blast radius, but still matters.
  6. Document and remediate before disclosure. Cache poisoning findings should include the exact unkeyed input, a reproducible request, the affected URL pattern, and the observed cache behaviour — this is what turns a theoretical bug into an actionable fix for engineering.
⚠️
WARNING
Never test cache poisoning against production infrastructure you do not own or have explicit written authorisation to test. A poisoned cache entry on a live, shared CDN cache can affect real users instantly — this is authorised VAPT territory, not a technique to try casually on third-party sites.
graph TD A[Find unkeyed input] -->|Craft request| B[Poison request sent] B -->|Response cached| C[Cache stores response] C -->|Same URL requested| D[Victims served malicious content] D -->|Impact| E[XSS or redirect at scale] style A fill:#1e3a5f,stroke:#3B82F6,color:#e2e8f0 style B fill:#1e3a5f,stroke:#3B82F6,color:#e2e8f0 style C fill:#5f1e1e,stroke:#EF4444,color:#e2e8f0 style D fill:#5f1e1e,stroke:#EF4444,color:#e2e8f0 style E fill:#5f1e1e,stroke:#EF4444,color:#e2e8f0

Know your vulnerabilities before attackers do

Run a free VAPT scan — takes 5 minutes, no signup required.

Book Your Free Scan

Real-World Impact: Why This Scales So Fast

A cache poisoning bug's severity comes from its multiplier effect. Where a stored XSS bug usually requires the attacker to plant a payload in a database record a victim will view, a poisoned cache entry sits directly on a URL that legitimate users, search crawlers, and even monitoring bots all request identically. Documented public research (PortSwigger's web security research team, which popularised much of the modern methodology) has shown poisoning chains that achieve:

    1. Reflected XSS turned persistent — a payload that would normally require social engineering to deliver instead gets served automatically to every visitor of a popular page.
    2. Open redirect at scale — a poisoned Location header or canonical URL silently redirects an entire user base to a phishing domain.
    3. Cache deception combined with poisoning — sensitive personalised content gets cached and served to the wrong users when session-specific data leaks into a cacheable response.
    4. Denial of service via error caching — an attacker triggers and caches a 4xx/5xx error page for a high-traffic route, effectively taking it offline for all subsequent visitors until the cache clears.
14%Breaches in 2024 that began with exploitation of a vulnerability as the initial entry point, up 180% year-on-year (Verizon 2024 DBIR)
9%Year-on-year rise in the average cost of a data breach in India in 2024, reaching an all-time high (IBM Cost of a Data Breach Report 2024)

CDN Context: Every Layer Computes Keys Differently

The risk compounds because most production stacks run multiple caching layers stacked on top of each other — a CDN edge cache, a reverse proxy cache, and sometimes application-level fragment caching — and each layer can compute its cache key differently. A header excluded from the CDN's key but included at the origin (or vice versa) creates exactly the mismatch attackers look for.

LayerTypical cache key scopeCommon unkeyed risk
CDN edge (Cloudflare, Akamai, CloudFront)Host + path, sometimes query stringCustom headers like X-Forwarded-Host often excluded by default
Reverse proxy (Nginx, Varnish, HAProxy)Configurable via proxy_cache_key / VCLMisconfigured X-Original-URL or forwarded headers
Framework-level cacheRoute + selected paramsAccept-Language, device-detection headers
Browser cacheFull request including cookiesLower shared-blast-radius, but still a vector for single-user persistence
💡
TIP
Ask each caching layer explicitly: "what is in my cache key, and does my origin's response depend on anything outside it?" If the answer to the second half is yes for even one header, you have a candidate poisoning vector to test.
pie title Common Root Causes of Cache Misconfiguration "Unkeyed headers trusted by origin" : 35 "Overly broad cache TTL on dynamic routes" : 25 "Inconsistent keys across cache layers" : 20 "Missing Vary header on personalised content" : 12 "Default CDN config left unaudited" : 8

Defence: Building Cache Keys That Can't Be Poisoned

The fix is rarely "disable caching" — that destroys performance and cost benefits. The fix is disciplined cache-key design and header hygiene.

    1. Key everything the origin uses. If X-Forwarded-Host (or any header) influences the response, it must be part of the cache key, or it must be stripped/normalised before it ever reaches the origin.
    2. Strip untrusted forwarded headers at the edge. Don't let client-supplied X-Forwarded-* headers pass through unvalidated — set them explicitly at your trusted proxy layer, and reject or overwrite any client-supplied duplicates.
    3. Use explicit Vary headers correctly. If a response genuinely differs by Accept-Language or Accept-Encoding, declare it — but audit that the CDN actually honours Vary for those values, since some default CDN configurations ignore or cap it.
    4. Set conservative Cache-Control directives on dynamic or personalised routes. private, no-store for anything session- or user-specific; short, explicit max-age and s-maxage for genuinely shared content.
    5. Segregate cacheable and non-cacheable routes architecturally. Static assets, marketing pages, and public API responses can be aggressively cached; anything reflecting request-derived data (redirects, canonical URLs, locale pages) needs a tighter, audited key.
    6. Test cache behaviour as part of every release, not just once. A cache configuration that was safe six months ago can silently regress when a new header gets added to a request-handling function.
🎯Key Takeaway
Cache poisoning isn't a bug in your application code alone — it's a mismatch between what your cache treats as "the same request" and what your origin actually uses to build a response. Fixing it means auditing every caching layer's key, not just patching the application.

Independent testing catches this class of issue reliably because it requires deliberately probing headers that internal QA and functional testing never touch — nobody manually tests X-Forwarded-Host variations during a feature release. This is exactly the kind of advanced, infrastructure-aware testing a proper free VAPT scan is designed to surface, alongside the broader application and network layer coverage. For organisations also mapping this into a data-protection posture — since a poisoned cache serving malicious content to Indian users touches DPDP Act breach-notification obligations — see our DPDP compliance guide for how infrastructure-level findings feed into your incident-response plan. Bachao.AI's VAPT methodology specifically includes unkeyed-input and cache-layer testing as part of its automated and manual review passes, and where CERT-In-empanelled validation is required for regulatory purposes, this is delivered with a CERT-In empanelled partner.

A Practical Audit Checklist

Before your next release, or as a standing quarterly check, run through this sequence against every route sitting behind a shared cache:

  1. List every header your application reads from the incoming request (not just the ones you intentionally documented).
  2. Cross-reference that list against your CDN/proxy's configured cache key.
  3. Flag any header that's read by the origin but absent from the key — treat each as an untested poisoning candidate.
  4. Confirm Cache-Control: private or no-store is set on every route serving session-specific or personalised data.
  5. Re-run the check after any change to routing, middleware, or reverse-proxy configuration — this is a regression class, not a one-time fix.
ℹ️
INFO
Organisations building on major CDNs should also review platform-specific guidance directly — Cloudflare, AWS CloudFront, and Akamai all publish cache-key configuration documentation, and CERT-In periodically issues advisories referencing web infrastructure misconfigurations. See CERT-In's advisory portal for current guidance relevant to Indian organisations.

Dhisattva AI Pvt Ltd builds this testing methodology around exactly these infrastructure-layer gaps — the vulnerabilities that sit between application code and the network path a request travels, which conventional scanners and manual QA both tend to miss. As more Indian SMBs move behind CDNs and reverse proxies for performance and DDoS protection, cache poisoning moves from "advanced attacker technique" to "standard item on the pentest scope."

Frequently Asked Questions

What is web cache poisoning in simple terms?
It's an attack where a malicious response gets stored by a shared cache or CDN and then automatically served to every subsequent visitor requesting the same URL. The attacker exploits a header or input the cache ignores but the origin server still uses, poisoning the cached copy with a single crafted request.
How is cache poisoning different from a normal XSS attack?
Standard XSS requires tricking an individual victim into visiting a malicious link or interacting with injected content. Cache poisoning turns that same payload into something served automatically to every user who loads the affected URL, without any further action by the attacker, until the cache entry expires or is purged.
What is an unkeyed input in cache poisoning terms?
It's any request attribute — most commonly a header like X-Forwarded-Host — that influences how the origin server builds its response, but is not included in the cache key the caching layer uses to decide whether two requests are "the same." This mismatch is the root cause of most cache poisoning bugs.
Can a CDN like Cloudflare or CloudFront be poisoned even if the origin server is secure?
Yes. Poisoning exploits the interaction between the cache's key configuration and the origin's response logic, not a flaw purely in the origin code. A perfectly secure origin can still be poisoned if the CDN caches a response built from an unkeyed, attacker-controlled header.
How do Indian SMBs test for cache poisoning without breaking production?
This should be done as authorised testing against staging or a scoped production window, following a documented methodology — mapping the cache layer, enumerating candidate unkeyed headers, confirming reflection and cacheability, then assessing real impact — ideally as part of a broader VAPT engagement rather than ad hoc header fuzzing.
What's the single most important fix for cache poisoning risk?
Ensure every header or input your origin server actually uses to build a response is either included in the cache key or stripped/normalised before it reaches the origin. This single discipline — keying everything the response depends on — closes the vast majority of real-world cache poisoning vectors.
BR

Bachao.AI Research Team

Cybersecurity Research

AI-powered security research and threat intelligence from the Bachao.AI team. Covering the latest vulnerabilities, CVEs, and cybersecurity developments affecting Indian businesses.

Get cybersecurity insights for Indian SMBs

Weekly vulnerability alerts, DPDP compliance tips, and security guides. No spam — unsubscribe anytime.

We respect your privacy. Your email is never shared.

Find out if you're exposed to this class of threat

Free automated scan — risk score in under 2 hours. No credit card required.

Scan Your Attack Surface
Find your vulnerabilitiesStart free scan →