必须同时启用gzip_vary on和add_header Vary Accept-Encoding,否则CDN可能将gzip与未压缩响应混为同一缓存键,导致不支持压缩的客户端收到gzip内容而页面空白或JS报错;因gzip_vary仅在实际压缩时添加Vary头,而add_header可强制所有响应携带该头,确保CDN按Accept-Encoding拆分缓存。

开启 gzip_vary on 本身不会让 CDN 正确缓存压缩资源,它只是“有条件地”加一个响应头;真正起作用的是让所有响应都带上 Vary: Accept-Encoding,否则 CDN 很可能把 gzip 和未压缩内容混为一缓存,导致用户看到空白页或 JS 报错。
gzip_vary on 的真实行为是“按需添加”,不是“强制添加”
它只在 Nginx 实际执行了 gzip 压缩时才插入 Vary: Accept-Encoding。比如:请求带 Accept-Encoding: gzip、响应 MIME 类型在 gzip_types 列表中、且长度超过 gzip_min_length,才会加这个头。其他情况(如爬虫不带该头、监控探针、旧浏览器)——Nginx 不压缩,也不加 Vary,响应就变成“无标识的通用版本”,CDN 会把它当成默认缓存,后续有支持 gzip 的用户来访问,可能拿到未压缩内容,造成解析失败。
CDN 缓存错乱的根本原因是缓存键没区分编码变体
- 多数传统 CDN 默认只用 URL + Host 构建缓存键,完全忽略请求头
- 没有
Vary: Accept-Encoding,CDN 就不知道“同一个 URL 可能返回 gzip 或明文” - 结果就是:gzip 响应被缓存后,直接返回给不支持解压的客户端,浏览器打不开,页面白屏
必须配合 add_header 强制统一声明 Vary
光开 gzip_vary on 不够,得在 server 或 location 块里加上:
gzip on;<br>gzip_vary on;<br>add_header Vary Accept-Encoding;
注意三点:
-
add_header要写在gzip on之后,否则可能被覆盖 - 不能有
proxy_hide_header Vary这类指令,否则头会被删掉 - 该配置会让每个响应(无论是否压缩)都带
Vary: Accept-Encoding,CDN 才能据此拆分缓存键
不同 CDN 对 Vary 的支持程度差异很大
-
Cloudflare:默认识别
Vary: Accept-Encoding,但要关掉「Always Online」或「Cache Everything」这类强制策略 -
AWS CloudFront:必须在缓存策略中手动勾选
Accept-Encoding为“Include when caching” - 自建或白牌 CDN:若缓存逻辑只哈希 URL+Host,不解析 Vary 头,那加了也没用,得升级缓存模块


















