必须启用 gzip_vary on 并配合 add_header vary accept-encoding,确保所有响应(含未压缩)都携带该头,同时合理配置 gzip_types、禁用 proxy_hide_header vary,并注意 gzip_static 与 vary 的兼容性。

要在 Nginx 中让上游缓存(如 CDN、反向代理或浏览器)正确识别并区分压缩与未压缩响应,关键不是“配置 Gzip Vary 头”,而是确保 Vary: Accept-Encoding 响应头被稳定、准确地发送出去——因为上游缓存正是靠这个头来决定是否把 gzip 版和明文版当作两个独立缓存项。
必须启用 gzip_vary on,并配合显式 add_header
仅开 gzip_vary on 不够。它只在 Nginx 实际执行了 gzip 压缩时才加 Vary 头;而未压缩响应(比如旧爬虫请求、无 Accept-Encoding 的探针)就不会带这个头,导致上游缓存误以为这是“通用版本”,后续可能错发给支持 gzip 的客户端。
- 在
http或server块中启用基础压缩: gzip on;-
gzip_vary on;(保证压缩响应自动带 Vary) - 再加一行:
add_header Vary Accept-Encoding;(强制所有响应都带该头,堵住未压缩响应的缺口) - 确保没有
proxy_hide_header Vary这类指令,否则头会被删掉
确认 gzip_types 和压缩范围合理
上游缓存依赖 Vary 区分变体,但前提是 Nginx 确实对目标资源做了压缩决策。如果 MIME 类型不在 gzip_types 列表里,即使请求带 Accept-Encoding: gzip,Nginx 也不会压缩,也就不会触发 gzip_vary on 的逻辑(哪怕你写了 add_header,至少能保 Vary 存在)。
- 推荐明确列出文本类类型:
gzip_types text/html text/css application/javascript application/json text/xml application/xml; - 避免对图片、视频、字体等二进制资源启用 gzip——它们本身已压缩,再压可能变大,还干扰 Vary 的语义一致性
- 不建议用
gzip_types *,易引发不可控行为
验证上游缓存是否真正按 Accept-Encoding 拆分缓存键
光发 Vary 头没用,上游缓存必须解析并尊重它。不同平台处理方式不同:
-
Cloudflare:默认识别
Vary: Accept-Encoding,但要关掉「Cache Everything」或「Always Online」这类覆盖策略 -
AWS CloudFront:必须在缓存策略中手动勾选
Accept-Encoding为 “Include when caching” -
自建或白牌 CDN:若缓存模块只哈希
URL + Host,不解析 Vary,那加头也无效,需升级缓存逻辑 - 验证方法:用
curl -I -H "Accept-Encoding: gzip" URL和curl -I -H "Accept-Encoding:" URL分别请求,检查两次响应是否都有Vary: Accept-Encoding,且X-Cache(或类似字段)显示为不同缓存状态
警惕 gzip_static 与 Vary 的冲突
如果你启用了 gzip_static on(直接返回预压缩的 .gz 文件),注意:gzip_vary on 对它完全无效——Nginx 不走 gzip 模块主流程,不会自动加 Vary 头。
- 解决方案一:禁用
gzip_static,改用运行时压缩(适合中小流量、CPU 充足场景) - 解决方案二:保留
gzip_static,但必须手动补上add_header Vary Accept-Encoding;(它对静态 .gz 文件同样生效) - 切勿同时开启
gzip_static和动态 gzip,容易造成响应不一致











