gzip_vary的核心作用是告知浏览器和中间代理同一url响应可能因accept-encoding不同而异,需按此头拆分缓存键;其on值仅条件触发,故必须配合add_header vary accept-encoding确保所有响应均携带该头。

gzip_vary 的核心作用不是直接控制浏览器缓存,而是告诉浏览器和中间代理:同一个 URL 的响应内容可能因 Accept-Encoding 请求头不同而不同(比如 gzip 压缩版 vs 未压缩版)。它不决定“要不要缓存”,而是决定“缓存时该按什么条件区分版本”。
为什么浏览器需要 Vary: Accept-Encoding
浏览器内置缓存(以及 CDN、反向代理等)默认只用 URL 和 Host 作为缓存键。如果没有 Vary 提示,它会把 /app.js 的 gzip 响应和 identity 响应当成同一份内容缓存——下次即使请求不带 Accept-Encoding,也可能返回 gzip 二进制流,导致 JS 解析失败、页面空白。
加上 Vary: Accept-Encoding 后,浏览器就知道:必须把 Accept-Encoding: gzip 和 Accept-Encoding: identity(或缺失)视为两个独立缓存项。
gzip_vary on 的真实行为
Nginx 的 gzip_vary on 是条件性生效的:只有当它实际执行了 gzip 压缩(即请求含 Accept-Encoding: gzip 且资源类型匹配 gzip_types),才会在响应头中添加 Vary: Accept-Encoding。
这意味着:
- 对不带
Accept-Encoding的请求,Nginx 不压缩,也不加 Vary 头; - 此时若该未压缩响应被缓存,后续带
Accept-Encoding: gzip的请求可能错误命中这个未压缩副本; - 结果就是“本该压缩却返回明文”,或“本不该压缩却返回 gzip”,都属于缓存污染。
正确配置:强制统一声明 Vary
解决上述缺口的唯一可靠方式,是在 location 或 server 块中显式添加:
add_header Vary Accept-Encoding;这会覆盖 Nginx 的条件逻辑,确保所有响应(无论是否压缩)都携带该头。注意:
- 不要用
add_header Vary ""或unset_header Vary,那会彻底移除 Vary,加剧风险; - 如果同时启用
gzip_static on,它绕过 gzip 模块主流程,gzip_vary on对其无效,必须靠add_header补上; - 若使用
proxy_cache,需确认proxy_cache_key包含$http_accept_encoding,否则缓存无法隔离变体。
验证是否真正起效
用 curl 检查两类典型请求:
-
curl -I -H "Accept-Encoding: gzip" https://yoursite.com/test.js→ 应返回Content-Encoding: gzip且含Vary: Accept-Encoding; -
curl -I -H "Accept-Encoding: identity" https://yoursite.com/test.js→ 应返回无Content-Encoding,但也必须含Vary: Accept-Encoding; - 两次响应的缓存标识(如
X-Cache)应不同,表明缓存系统已按编码差异分离存储。











