CDN边缘节点缓存规则精细配置:Vary头部与Cache-Control深度解析

本文从零开始解释CDN缓存的工作原理,重点讲解Vary头部如何让CDN区分不同请求的响应,以及Cache-Control指令如何控制浏览器和CDN的缓存行为。通过具体示例和配置建议,帮助读者理解并避免常见缓存陷阱,实现更精准的缓存策略。从基础原理讲到落地操作,同时整理容易忽略的细节和上线后的检查方法。

CDN边缘节点缓存规则精细配置:Vary头部与Cache-Control深度解析
封面图:ZuCDN · ZuCDN 原创

折腾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-LanguageUser-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只放确实会影响响应的头,宁可少放,不要多放。

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视为不同资源,分别缓存。

实战:如何配置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://你的域名

观察返回的 VaryCache-Control 是否如你所愿。再用不同的语言和User-Agent测试,看响应头是否一致?如果一致说明没区分开,检查源站是否真正根据这些头返回了不同内容。

总结

我的处理经验

Vary头部和Cache-Control是CDN缓存精细化的双刃剑。Vary确保缓存内容正确区分,避免张冠李戴;Cache-Control决定缓存的生命周期和范围。理解它们背后的原理,才能避免“缓存失效”或“缓存污染”的陷阱。记住几个原则:Vary只包含真正变化的最小集合;对于敏感数据使用no-store;利用s-maxage给CDN更长缓存的同时保留浏览器短缓存,兼顾性能与新鲜度。掌握了这些,你的网站就能在速度和正确性之间找到最佳平衡。真正做好CDN Vary,靠的不是参数堆砌,而是持续验证。

延伸阅读