浏览器依据 Vary: Accept-Encoding 实现多版本缓存,将请求头值纳入缓存键;Nginx 需启用 gzip_vary on 或强制 add_header Vary Accept-Encoding always,并配合 proxy_cache_key 包含 $http_accept_encoding。

浏览器缓存要正确识别和复用压缩资源,关键不是“自己决定怎么缓”,而是信任并遵守服务端返回的 Vary: Accept-Encoding 头。这个头的作用是告诉浏览器:“同一 URL 的响应内容可能因 Accept-Encoding 不同而不同,请把该请求头的值也纳入缓存匹配条件”。没有它,浏览器可能把 gzip 版本缓存下来,再错发给不支持解压的环境(比如某些旧 WebView 或禁用压缩的调试工具)。
浏览器如何依据 Vary 实现多版本缓存
当浏览器收到一个带 Vary: Accept-Encoding 的响应时,它不会只用 URL 做缓存键,还会记录当时请求中 Accept-Encoding 的具体值(如 gzip、br 或空)。后续再次请求同一 URL 时:
- 如果新请求的
Accept-Encoding和之前一致,就直接复用对应缓存条目 - 如果不一致(比如之前是
gzip,这次是identity),就视为全新请求,不命中缓存 - 如果响应没带
Vary: Accept-Encoding,浏览器默认只认 URL,所有变体都挤在同一个缓存槽里
Nginx 必须确保 Vary 头真实反映压缩行为
gzip_vary on 是最稳妥的启用方式,因为它由 Nginx 内部逻辑控制:只有实际执行了 gzip 压缩时,才添加 Vary: Accept-Encoding;未压缩响应则不加。这样能避免缓存系统误以为“所有响应都依赖 Accept-Encoding”。
但要注意一种隐患:某些客户端(如旧爬虫、curl 测试)根本不发 Accept-Encoding,Nginx 不压缩、也不加 Vary,此时未压缩响应若被 CDN 或中间代理缓存,就可能污染整个资源的缓存变体。解决方法是在 location 块中强制统一添加:
这会覆盖 Nginx 的条件逻辑,确保每个响应(无论是否压缩)都携带该头,彻底消除缓存歧义。
配合 proxy_cache 使用时的关键点
如果你启用了 proxy_cache,仅靠 gzip_vary on 还不够。因为 Nginx 默认缓存键($scheme$proxy_host$request_uri)不含请求头,必须显式扩展:
- 在 cache 配置块中设置
proxy_cache_key "$scheme$proxy_host$request_uri $http_accept_encoding"; - 确保
gzip_types覆盖了你要压缩的 MIME 类型(如application/javascript) - 验证时用 curl 分别请求
-H "Accept-Encoding: gzip"和-H "Accept-Encoding: identity",检查响应头是否都含Vary: Accept-Encoding,且X-Cache标识为不同条目
不需要额外配置浏览器,但需避免干扰行为
现代浏览器原生支持 Vary 语义,无需 JS 或 meta 标签干预。但要注意:
- 不要在前端代码里手动修改
Accept-Encoding请求头(如 fetch 中硬设),这会破坏与服务端协商的一致性 - 避免在开发时频繁禁用浏览器压缩功能(如 Chrome DevTools 的 Network → Disable cache + Disable cache toggle),否则可能绕过 Vary 匹配逻辑
- 如果使用 Service Worker,确保其 fetch 逻辑尊重原始响应的 Vary 和 Cache-Control,不要无差别缓存所有 GET 请求


















