gzip_vary的作用是让缓存系统识别同一url因accept-encoding不同而返回不同响应,需将该请求头纳入缓存键;nginx中启用gzip_vary on可自动在实际压缩时添加vary: accept-encoding头,精准避免缓存污染。

gzip_vary 的作用不是“实现缓存版本控制”,而是让缓存系统(浏览器、CDN、反向代理)知道:同一个 URL 可能返回不同内容——取决于客户端是否支持 gzip 压缩。它通过添加 Vary: Accept-Encoding 响应头,告诉缓存“请把 Accept-Encoding 的值也当作缓存键的一部分”。这样就避免了把压缩版错发给不支持解压的旧客户端,或把未压缩版错发给本可接收压缩内容的现代浏览器。
为什么需要 Vary: Accept-Encoding
如果不加这个头,缓存(比如 CDN 或公司内部代理)可能只按 URL 缓存一份响应。假设第一个请求带 Accept-Encoding: gzip,服务器返回 gzip 压缩内容并被缓存;紧接着一个不带该头的老设备来请求同一 URL,缓存却直接返回压缩后的二进制数据——老设备无法解压,页面就乱码或空白。
- 缓存系统默认只认 URL 作为唯一标识
- Vary 告诉它:“别只看 URL,还要看 Accept-Encoding 的值”
- 于是
GET /app.js+Accept-Encoding: gzip和GET /app.js+Accept-Encoding: identity被视为两个独立缓存项
nginx 中开启 gzip_vary 的正确写法
只需在 http 或 server 块中启用,并确保 gzip 已打开:
http {
gzip on;
gzip_vary on; # 关键:自动加 Vary: Accept-Encoding
gzip_types text/css application/javascript text/html;
}
注意:gzip_vary on 是默认行为,但显式写出更清晰。它不依赖 add_header 手动设置,而是由 ngx_http_gzip_module 自动注入,且仅在实际执行了 gzip 压缩时才生效(即响应确实被压缩了,才会发这个 Vary 头)。
和手动 add_header Vary 的区别
有人会写 add_header Vary Accept-Encoding;,这看似等效,但有隐患:
- 它无条件添加,哪怕这次响应根本没压缩(比如图片、已预压缩的 .gz 文件),也会发 Vary 头
- 这会导致缓存系统为每个资源都多存一份,浪费空间,还可能干扰缓存策略
- 而
gzip_vary on是智能的:只在真正压缩响应时才加 Vary,精准匹配语义
CDN 或多层代理下的注意事项
如果你用 CDN(如 Cloudflare、阿里云全站加速),需确认它尊重 Vary 头:
- 主流 CDN 默认识别并遵循 Vary: Accept-Encoding
- 某些轻量级反向代理(如部分自建 nginx 缓存层)可能忽略 Vary,导致缓存污染
- 可通过 curl 测试验证:
curl -I -H "Accept-Encoding: gzip" https://yoursite.com/test.js和curl -I -H "Accept-Encoding:" https://yoursite.com/test.js,观察 Content-Encoding 和 Vary 是否一致出现











