cdn缓存错gzip内容的根本原因是缓存键未包含accept-encoding头,导致不同编码需求的请求命中同一缓存;需nginx启用gzip_vary on并确保cdn识别vary: accept-encoding字段。

为什么 CDN 会缓存错 Gzip 压缩内容?
根本原因是:CDN 节点默认只看 URL 和 Host 做缓存键(cache key),不检查 Accept-Encoding 请求头。当一个支持 gzip 的客户端先请求,Nginx 返回了压缩响应并带上 Vary: Accept-Encoding,但部分 CDN(尤其是配置简陋的边缘节点)忽略该头,直接把压缩体缓存下来;后续不支持压缩的客户端再请求同一 URL,CDN 就错误地返回 gzip 二进制流——浏览器打不开,表现为乱码或空白页。
必须开启 gzip_vary on 并确认 CDN 理解它
Nginx 默认关闭 gzip_vary,即使你开了 gzip on,也不会自动加 Vary: Accept-Encoding 响应头。必须显式启用:
gzip on; gzip_vary on; gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
注意:gzip_vary on 只在 Nginx 实际执行了压缩时才添加 Vary 头——比如请求带 Accept-Encoding: gzip 且响应 MIME 类型匹配 gzip_types;否则不加。这是正确行为,不是 bug。
CDN 层必须透传或识别 Vary: Accept-Encoding
光靠 Nginx 加头没用,CDN 必须据此区分缓存。常见情况与对策:
- Cloudflare:默认尊重
Vary,无需额外设置,但需确认域名未开启「Always Online」或「Cache Level: Cache Everything」这类强制覆盖策略 - AWS CloudFront:必须在缓存策略(Cache Policy)中勾选
Accept-Encoding为「Include when caching」,否则无视该Vary字段 - 自建或白牌 CDN:检查其缓存模块是否解析
Vary头;若只做简单哈希(如md5(url + host)),则必须升级或改用支持 Vary 的缓存逻辑 - 验证方式:用
curl -H "Accept-Encoding: gzip" -I https://yoursite.com/test.js和curl -H "Accept-Encoding:" -I https://yoursite.com/test.js分别请求,对比响应头中的Content-Encoding和Age,再检查两次响应的Vary是否一致且为Accept-Encoding
避免和 gzip_static 一起用导致 Vary 缺失
如果你启用了 gzip_static on(让 Nginx 直接发 .gz 文件),注意:gzip_static 不触发 gzip 模块主流程,因此 gzip_vary on 对它无效——Nginx 不会自动补 Vary: Accept-Encoding。
解决方法只有两个:
- 禁用
gzip_static,改用运行时压缩(适合中小流量、CPU 充足场景) - 手动补
Vary头:location ~ \.js$ { gzip_static on; add_header Vary Accept-Encoding; }但要注意:这会让所有.js响应都带Vary,哪怕没走 gzip_static(比如文件无 .gz 版本时回退到原始文件),属于“过度声明”,可能略微降低缓存效率
真正关键的不是加头本身,而是确保整个链路——从 Nginx 的条件判断、头部注入,到 CDN 对 Vary 字段的实际缓存分片逻辑——全部对齐。漏掉任意一环,gzip_vary on 就只是个摆设。











