Inspect HTTP Cache Vary Header Mismatch
Detect a missing or wrong Vary header that serves one user language or encoding to another through a shared cache.
Task
Reproduce cache poisoning by changing request headers and confirm the response Vary header correctly partitions the cache.
When to use this
Users occasionally see another locale or a Brotli payload sent to a client that only accepts gzip, because the cache key ignores headers.
Prerequisites
- A caching CDN or proxy in front of staging
- curl for header manipulation
- The cache key configuration
Steps
1. Request the same URL with two different Accept-Encoding values.
curl -sI -H 'Accept-Encoding: gzip' https://cdn.example.com/app.js | grep -i 'content-encoding\|vary'
curl -sI -H 'Accept-Encoding: br' https://cdn.example.com/app.js | grep -i 'content-encoding\|vary'Expected output: If Vary is missing, the second request may receive the gzip variant cached for the first, causing decode errors.
2. Confirm the fix partitions the cache.
for enc in gzip br identity; do
curl -sI -H "Accept-Encoding: $enc" https://cdn.example.com/app.js | grep -i vary
done | sort -uExpected output: Every response advertises Vary: Accept-Encoding so each encoding is cached separately.
Troubleshooting
Problem
Vary present but still wrong variant
Solution
Check whether the cache normalizes headers; add the header explicitly to the cache key.
Problem
Language mixed
Solution
Also vary on Accept-Language or switch to separate URLs per locale.
Limitations
- Vary on many headers hurts hit ratio; prefer URL-based variation when possible.
- Does not cover private caches in the browser.
- Requires control over the cache key.
What this recipe does
Reproduce cache poisoning by changing request headers and confirm the response Vary header correctly partitions the cache.
Users occasionally see another locale or a Brotli payload sent to a client that only accepts gzip, because the cache key ignores headers.
Prerequisites
- A caching CDN or proxy in front of staging
- curl for header manipulation
- The cache key configuration
Procedure
- Request the same URL with two different Accept-Encoding values.
curl -sI -H 'Accept-Encoding: gzip' https://cdn.example.com/app.js | grep -i 'content-encoding\|vary'
curl -sI -H 'Accept-Encoding: br' https://cdn.example.com/app.js | grep -i 'content-encoding\|vary'
Expected result: If Vary is missing, the second request may receive the gzip variant cached for the first, causing decode errors.
- Confirm the fix partitions the cache.
for enc in gzip br identity; do
curl -sI -H "Accept-Encoding: $enc" https://cdn.example.com/app.js | grep -i vary
done | sort -u
Expected result: Every response advertises Vary: Accept-Encoding so each encoding is cached separately.
How to confirm it worked
Run the commands above and confirm every expected result in order. If a step does not produce the stated output, use the troubleshooting notes before continuing. A passing run shows each Accept-Encoding and Accept-Language request returns the correct variant and the responses advertise matching Vary headers.
Troubleshooting
Vary present but still wrong variant — Check whether the cache normalizes headers; add the header explicitly to the cache key.
Language mixed — Also vary on Accept-Language or switch to separate URLs per locale.
Limitations
- Vary on many headers hurts hit ratio; prefer URL-based variation when possible.
- Does not cover private caches in the browser.
- Requires control over the cache key.