← Recipes

阅读中文版本

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.

~30 minhttpcachingdebugging

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 -u

Expected 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

  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 result: If Vary is missing, the second request may receive the gzip variant cached for the first, causing decode errors.

  1. 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.