The Vary Header — Why Your CDN Serves the Wrong Cached Response
Updated April 2026
Reading this? Verify your fix in real-time. Audit your cache headers → EdgeFix
You set up Brotli compression. Your CDN caches the first request. The second user, whose browser does not support Brotli, gets served the Brotli-compressed version and cannot decompress it. This is a Vary header problem.
When you need Vary and what to set
| Situation | Add this Vary value | Why |
|---|---|---|
| Multiple compression formats (gzip, Brotli, zstd) | Accept-Encoding | Different encodings = different response bodies |
| CORS with explicit origins | Origin | ACAO header value differs per origin |
| Content negotiation (JSON vs HTML) | Accept | Same URL, different content type |
| Internationalisation | Accept-Language | Different language = different response |
| Mobile vs desktop content | User-Agent | Warning: very broad, hurts cache efficiency |
The most common one — compression
# Nginx — you probably have this already (gzip adds it automatically) # But verify it is there: gzip_vary on; # adds Vary: Accept-Encoding for gzip responses # For Brotli: brotli_types text/html text/css application/javascript; # Also add: add_header Vary "Accept-Encoding" always;
CORS + Vary: Origin
When you return a specific origin in Access-Control-Allow-Origin instead of *, you must add Vary: Origin. Otherwise the CDN caches the response for one origin and serves it to requests from a different origin — which will have the wrong ACAO value.
# Nginx — CORS config with Vary
add_header Access-Control-Allow-Origin "https://yourapp.com" always;
add_header Vary "Origin" always; # required when using explicit origins
# Express
app.use((req, res, next) => { res.vary("Origin"); // add Vary: Origin before setting ACAO next();
});
Multiple Vary values
# Correct — comma-separated list
Vary: Accept-Encoding, Origin
# In Nginx — multiple add_header lines for Vary stack incorrectly
# Use a single directive:
add_header Vary "Accept-Encoding, Origin" always;
# Vercel (vercel.json)
{ "key": "Vary", "value": "Accept-Encoding, Origin" }
Check current Vary header
curl -I https://yoursite.com/ | grep -i vary # Should show: vary: Accept-Encoding # If you use CORS with specific origins, also: vary: Origin
What not to vary on
# Never use: Vary: * # disables caching entirely Vary: Cookie # prevents any CDN caching (every request unique) Vary: Authorization # same as Cookie — kills CDN efficiency # For authenticated content: # Do not use Vary: Authorization # Use Cache-Control: private, no-store instead
Use EdgeFix to audit your live site's Vary, Cache-Control, Age, and X-Cache headers together — it shows exactly what the CDN is caching and what it should not be.
Audit your cache headers → EdgeFix