必须显式添加add_header Vary Accept-Encoding,因为gzip_vary on仅在压缩时添加该头,未压缩响应会缺失Vary导致缓存混淆;需配合gzip on和add_header确保所有响应统一声明变体依据。

要让客户端浏览器准确理解服务器是否对当前响应做了 gzip 压缩,关键不是“动态通知压缩详情”,而是**统一、可靠地声明缓存变体依据**——即强制所有响应都携带 Vary: Accept-Encoding 头。Nginx 默认的 gzip_vary on 并不足够,它只在实际执行压缩时才加这个头;未压缩的响应(比如来自旧爬虫或禁用压缩的请求)会漏掉该头,导致缓存系统混淆,把未压缩内容错发给需要 gzip 的浏览器。
为什么必须显式添加 Vary 头
浏览器和 CDN 依赖 Vary: Accept-Encoding 来区分“压缩版”和“未压缩版”两个缓存条目。如果某次请求没带 Accept-Encoding: gzip,Nginx 不压缩、也不加 Vary 头,缓存系统就会把这个响应当作通用版本存储。后续带 gzip 请求进来时,可能拿到未压缩的 JS/CSS,直接报 Unexpected token 或页面空白。
- 现象本质是缓存键(cache key)未隔离,而非压缩本身失效
- 即使只服务现代浏览器,iOS WebView、安全扫描器、监控探针等也可能发空或
identity编码头,触发未压缩路径 -
gzip_vary on是条件性行为,不能覆盖所有响应场景
正确配置方式(Nginx)
在 http、server 或 location 块中,确保以下三要素同时存在且顺序合理:
-
gzip on;—— 启用压缩功能 -
gzip_vary on;—— 允许 Nginx 在压缩时自动加 Vary 头 -
add_header Vary Accept-Encoding;—— 强制所有响应(无论是否压缩)都带上该头
注意:add_header 必须写在 gzip on 之后,且不能被 proxy_hide_header Vary 覆盖,否则等于主动移除关键标识。
验证是否真正生效
用 curl 检查两类典型请求的响应头:
-
curl -I -H "Accept-Encoding: gzip" https://yoursite.com/app.js→ 应返回Content-Encoding: gzip和Vary: Accept-Encoding -
curl -I -H "Accept-Encoding: identity" https://yoursite.com/app.js→ 应返回Vary: Accept-Encoding,但无Content-Encoding
两次响应的缓存标识(如 CDN 的 cache key)必须不同,否则说明 Vary 未起作用。
配套要点不能少
单靠 Vary 头还不够,需同步检查其他环节:
-
gzip_types要覆盖常用文本类型:如text/css application/javascript application/json text/xml,避免漏压关键资源 -
gzip_min_length 1024防止小文件压缩开销反超收益 - 若使用
proxy_cache,确认proxy_cache_key包含$http_accept_encoding,否则缓存无法按编码隔离 - 禁用 IE6 等老旧客户端压缩:
gzip_disable "msie6";,规避已知解压异常


















