折腾CDN Vary时,我发现最麻烦的往往不是安装,而是配置。当你打开一个网页,浏览器向服务器发送请求,然后服务器返回HTML、CSS、图片等资源。如果每次访问都要从源站加载,不仅速度慢,源站也扛不住压力。CDN(内容分发网络)就是在全球各地放了很多缓存节点,把资源缓存起来,用户就近获取,速度更快。但缓存不是简单的“存一份,所有人用”——不同的用户可能请求同一个URL却需要不同的内容,比如移动端和PC端、不同语言的版本。如果没有精细的配置,缓存就会出问题:要么把不该缓存的内容缓存了(比如用户私密数据),要么该缓存的内容没缓存(比如版本号一致但语言不同)。今天就带你深入理解两个控制缓存的核心工具:Vary头部和Cache-Control。
为什么缓存会因为“看似相同”的请求而出错?——CDN Vary
配置前的检查
想象一个电商网站的商品详情页,URL是 /product/123。用户A用Chrome浏览器访问,页面返回默认中文版;用户B用手机Safari访问,页面返回英文版。这两个请求的URL完全相同,但服务器可能根据 User-Agent(浏览器标识)或 Accept-Language(语言偏好)返回不同的内容。如果CDN只根据URL缓存,那么先请求的用户A拿到的中文版被缓存后,用户B再请求时就会得到中文版(而本应返回英文版),这叫缓存污染。
Vary头部:告诉CDN“缓存时要看哪些请求头”
Vary是服务器在响应中设置的一个HTTP头,它的值告诉中间缓存(包括CDN节点):“这个响应依赖于哪些请求头,请根据这些请求头的不同组合来区分缓存”。
Vary的工作原理
当服务器返回以下响应头时:
Vary: Accept-Language, User-Agent
CDN就会把请求中的 Accept-Language 和 User-Agent 的值作为缓存键的一部分。也就是说,对于同一个URL,如果请求头的值不同,CDN会认为这是不同的资源,分别缓存。用户A请求时携带 Accept-Language: zh-CN,CDN缓存一份中文版;用户B请求时携带 Accept-Language: en,CDN从源站获取英文版并单独缓存。这样就避免了张冠李戴。
常见的Vary值
- Accept-Encoding:用于区分gzip、br等压缩格式。几乎所有CDN都会自动处理,通常无需手动设置。
- Accept-Language:用于区分语言版本。如果网站有多语言,必须设置。
- User-Agent:用于区分桌面端和移动端。但注意,User-Agent成千上万,会导致缓存碎片化,命中率下降。推荐用
Vary: Accept或基于设备类型检测后统一重定向。 - Cookie:用于区分登录状态。但极不推荐!因为Cookie值太多,缓存几乎失效。更好的做法是:把动态内容(如购物车)通过API获取,静态资源统一缓存。
Vary的坑:不要什么都Vary
有些新手在服务器上设置 Vary: *,意思是一切请求头都影响响应。这会导致CDN永远无法命中缓存——因为几乎每个请求的HTTP头都不一样。结果是CDN每次都回源,完全失去了加速的意义。所以Vary只放确实会影响响应的头,宁可少放,不要多放。
补充参考:此处可内链到“CDN Vary故障排查实例”。
关联教程:此处可内链到“CDN Vary部署与验证”内容。
Cache-Control:精确控制缓存的生命周期
如果说Vary解决的是“缓存哪些东西要区分”,那么Cache-Control解决的是“缓存多久、谁可以缓存”。它是一个请求/响应头,包含多个指令。
关键指令一览
- public:表示响应可以被任何中间缓存(CDN、代理等)缓存。适用于静态资源。
- private:表示响应只能被浏览器缓存(私有缓存),CDN不得缓存。适用于包含用户个人信息的页面,如“我的订单”。
- no-cache:字面意思并非不缓存,而是“每次使用缓存前必须向源站验证是否过期”。适用于需要动态验证的页面。
- no-store:真正的禁止缓存,任何中间节点都不得存储响应。适用于敏感数据如支付、令牌。
- max-age=seconds:设置缓存的最大存活时间(从请求时刻算起)。例如
max-age=3600表示缓存1小时。 - s-maxage=seconds:专门针对共享缓存(如CDN)的max-age,优先级高于max-age。用于对CDN和浏览器设置不同的缓存时间。
- stale-while-revalidate=seconds:允许在后台异步验证新版本的同时,继续使用过期的缓存。能大幅提升感知性能。
浏览器缓存 vs CDN缓存
浏览器缓存是私有缓存,CDN缓存是共享缓存。同一个URL,浏览器希望尽快展示,所以通常允许较短的max-age;CDN希望尽可能回源少,所以希望较长的max-age。通过 s-maxage 可以区分:
Cache-Control: public, max-age=60, s-maxage=3600
这条指令的意思是:浏览器缓存1分钟,CDN缓存1小时。这样浏览器可以在1分钟内不请求CDN,而CDN在一小时内只回源一次,减少了源站压力。如果浏览器超过1分钟,它会向CDN发起请求,如果CDN还未过期,则直接返回缓存,浏览器相当于间接获得了CDN的缓存。
配合Vary的最佳实践
假设你的多语言网站需要CDN缓存,但要根据 Accept-Language 区分。服务器响应:
Vary: Accept-Language
Cache-Control: public, max-age=300, s-maxage=86400
这样CDN会根据不同语言缓存,每个语言版本保存1天;浏览器缓存5分钟。用户切换语言后,请求会携带不同的 Accept-Language,CDN视为不同资源,分别缓存。
延伸阅读:此处可内链到“CDN Vary配置案例”相关文章。
实战:如何配置Vary和Cache-Control与CDN Vary
你可以在源站服务器(Nginx、Apache、应用框架)中设置响应头。以Nginx为例:
location /static/ {
add_header Cache-Control "public, max-age=31536000, immutable";
add_header Vary Accept-Encoding;
}
location /api/ {
add_header Cache-Control "no-store";
}
location / {
add_header Cache-Control "public, max-age=60, s-maxage=600";
add_header Vary Accept-Language, User-Agent;
}
注意:如果使用了CDN(如腾讯云CDN、CloudFront),你还需要在CDN后台开启“遵循源站缓存头”或“设置缓存规则”。有些CDN默认会覆盖源站的Cache-Control,需要手动配置成“遵循源站”才能生效。
测试你的配置是否正确
用curl查看响应头:
curl -I -H "Accept-Language: zh-CN" -H "User-Agent: Mozilla/5.0" https://你的域名
观察返回的 Vary 和 Cache-Control 是否如你所愿。再用不同的语言和User-Agent测试,看响应头是否一致?如果一致说明没区分开,检查源站是否真正根据这些头返回了不同内容。
想继续深入:此处可内链到“CDN Vary优化清单”文章。
相关阅读:此处可内链到“CDN Vary常见问题”专题。
总结
我的处理经验
Vary头部和Cache-Control是CDN缓存精细化的双刃剑。Vary确保缓存内容正确区分,避免张冠李戴;Cache-Control决定缓存的生命周期和范围。理解它们背后的原理,才能避免“缓存失效”或“缓存污染”的陷阱。记住几个原则:Vary只包含真正变化的最小集合;对于敏感数据使用no-store;利用s-maxage给CDN更长缓存的同时保留浏览器短缓存,兼顾性能与新鲜度。掌握了这些,你的网站就能在速度和正确性之间找到最佳平衡。真正做好CDN Vary,靠的不是参数堆砌,而是持续验证。
延伸阅读
