开启 gzip_vary on 不足以确保传统 CDN 正确分发压缩资源,因其仅在实际压缩时添加 Vary: Accept-Encoding;必须配合 add_header Vary Accept-Encoding 强制所有响应携带该头,才能使 CDN 按 Accept-Encoding 拆分缓存键。
开启 gzip_vary on 是让 Nginx 在启用 gzip 压缩时自动添加 Vary: Accept-Encoding 响应头的基础操作,但仅靠这一项不足以确保传统 CDN 节点正确识别和分发压缩资源——关键在于“所有响应都必须带这个头”,而 Nginx 默认行为只在实际压缩时才加,未压缩响应会漏掉,导致缓存混淆。
Nginx 的 gzip_vary on 是条件触发的:只有当请求带 Accept-Encoding: gzip 且响应内容匹配 gzip_types、长度超过 gzip_min_length 时,才会插入 Vary: Accept-Encoding。如果某个请求没声明支持压缩(比如爬虫、旧工具、监控探针),Nginx 不压缩,也不加 Vary 头。CDN 就会把这次响应当作“通用版本”缓存,后续有支持 gzip 的用户来访问,可能拿到未压缩内容,造成 JS 执行失败或页面空白。
要在所有 HTTP 响应中强制带上 Vary: Accept-Encoding,无论是否压缩,需在配置中明确添加:
gzip on;gzip_vary on;add_header Vary Accept-Encoding;注意:add_header 必须写在 gzip on 之后,且不能被 proxy_hide_header Vary 或类似指令覆盖。它会覆盖 Nginx 的条件性行为,确保每个响应都有统一标识,CDN 才能按 Accept-Encoding 值拆分缓存键。
用两条 curl 命令对比测试同一资源:
curl -H "Accept-Encoding: gzip" -I https://example.com/main.js → 应返回 Content-Encoding: gzip 和 Vary: Accept-Encoding
curl -H "Accept-Encoding:" -I https://example.com/main.js → 应返回无 Content-Encoding,但 Vary: Accept-Encoding 仍存在若两次响应的 Vary 头不一致,或第二次缺失,说明配置未生效;若 CDN 返回的 Age 值相同,大概率是它没解析 Vary,仍在用 URL 单一缓存键。
光 Nginx 加头没用,CDN 层必须配合解析:
若 CDN 不支持或未启用 Vary 解析,再严格的 Nginx 配置也无效——此时应考虑预压缩 + gzip_static on,并手动为 .gz 文件响应补 Vary 头。