Vary Header
title: "Vary Header — Content Negotiation and Cache Variants" description: "The Vary response header tells caches to store multiple versions of a response based on request headers like Accept, Accept-Encoding, and Accept-Language." date: 2026-06-28 lastmod: 2026-06-28 weight: 16 tags: [apis, caching] }
The Vary header instructs caches to key stored responses by specified request headers, ensuring different clients receive the correct content variant.
What You'll Learn
- How Vary works with content negotiation
- Common Vary header values
- Cache key generation with Vary
Why It Matters
Without Vary, a cache might serve a gzipped response to a client that doesn't support gzip, or an English response to a French client.
Code Examples
# Vary header for content negotiation
@app.route('/api/users')
def get_users():
response = jsonify(get_users_data())
# Vary by Accept-Encoding (compression)
response.headers['Vary'] = 'Accept-Encoding'
response.headers['Cache-Control'] = 'public, max-age=3600'
return response
# Vary by multiple headers
@app.route('/api/user/profile')
def user_profile():
response = jsonify(profile_data())
# Vary by language and encoding
response.headers['Vary'] = 'Accept-Language, Accept-Encoding'
response.headers['Cache-Control'] = 'public, max-age=3600'
return response
# Vary by Authorization (different cached per user)
@app.route('/api/dashboard')
def dashboard():
response = jsonify(generate_dashboard())
# Each user gets their own cache entry
response.headers['Vary'] = 'Authorization'
response.headers['Cache-Control'] = 'private, max-age=60'
return response
// Express with Vary header
app.get('/api/articles', (req, res) => {
const lang = req.headers['accept-language']?.split(',')[0] || 'en';
const articles = getArticles(lang);
res.set({
'Vary': 'Accept-Language, Accept-Encoding',
'Cache-Control': 'public, max-age=3600'
});
res.json(articles);
});
# Example Vary response
HTTP/1.1 200 OK
Content-Type: application/json
Content-Encoding: gzip
Vary: Accept-Encoding, Accept-Language
Cache-Control: public, max-age=3600
# Cache stores separate entries for:
# /api/users + Accept-Encoding: gzip + Accept-Language: en
# /api/users + Accept-Encoding: gzip + Accept-Language: fr
# /api/users + Accept-Encoding: identity + Accept-Language: en
Common Mistakes
1. Missing Vary: Accept-Encoding
CDNs and proxies may serve wrong compression variant.
2. Overly Broad Vary Headers
Varying by * or many headers fragments the cache.
3. Not Including Authorization for Private Data
Without Vary: Authorization, one user's data may leak to another.
4. Vary on User-Agent
User-Agent has too many values, fragmenting cache. Use device detection on the server.
5. Vary Without Corresponding Logic
If you Vary by a header, your server must actually respect it.
Practice Questions
- What problem does Vary solve?
- What header values should you Vary by for compression?
- Why is Vary: User-Agent problematic?
- How does Vary affect cache hit ratio?
- Can you Vary by custom headers?
Answers:
- Serving correct content variants to different clients.
- Accept-Encoding.
- Too many unique User-Agent values drastically reduce cache hits.
- More Vary headers = fewer cache hits (cache fragmentation).
- Yes:
Vary: X-API-Versionfor versioned APIs.
Challenge: Implement a multi-language API with proper Vary headers. Show how the cache stores separate entries for each language.
FAQ
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro