核心是让vary与缓存键严格对齐:先确认请求头是否真实影响响应内容,再通过map归一化易变头(如user-agent),用标准化头(如x-device-type)同步设置vary和cache_key,多层代理中仅后端接入层可设vary。

核心不是“去掉 Vary”,而是让 Vary 和缓存键严格对齐、只反映真实影响响应的请求差异。Vary 冲突本质是语义错配:头声明了某字段会影响内容,但缓存系统没按它分片;或反过来,缓存键区分了字段,却没在 Vary 中声明,导致中间代理误复用。
确认 Vary 是否真有必要
先判断后端是否真的根据某个头返回不同内容:
- 如果 API 对所有 Origin 都返回相同响应(比如固定设 Access-Control-Allow-Origin: *),就不该加 Vary: Origin——加了会让每个跨域请求建独立缓存,命中率暴跌
- 如果 HTML 页面做了服务端设备适配(如 PC / 移动返回不同 DOM),才需 Vary: X-Device-Type;若只是前端 JS 判断,就不该 Vary User-Agent
- 静态资源(JS/CSS/图片)通常不依赖请求头,Vary 应为空或仅 Accept-Encoding
用 map 归一化易变请求头
直接 Vary 完整 User-Agent 或带时间戳的自定义头,等于主动制造缓存碎片。正确做法是在 Nginx 内部做归类:
- 在 http 块定义 map,把千变万化的 UA 映射为有限枚举:mobile / tablet / desktop
- 用 proxy_set_header 注入标准化头:proxy_set_header X-Device-Type $device_type;
- 缓存键和 Vary 都基于这个稳定值:proxy_cache_key "$uri$device_type";,add_header Vary "X-Device-Type";
确保 Vary 与缓存键完全一致
两者必须一一对应,否则缓存行为不可控:
- 若决定 Vary Origin,缓存键里就必须含 $http_origin;但空 Origin 会导致分裂,建议用 map 统一为 "null"
- 若后端已返回 Vary 头,Nginx 默认透传;为防遗漏,可显式设置:add_header Vary "Origin" always;
- 检查是否误配了 proxy_ignore_headers Vary——这会丢弃后端 Vary,但 Nginx 缓存仍按原始逻辑计算 key,造成语义脱节
多层代理时禁止中继污染
级联 Nginx 中,Vary 只能由最靠近后端的一层生成,其他层只透传:
- 边缘层(接 CDN):禁用 add_header Vary,只开 proxy_pass_request_headers on;
- 网关层(鉴权路由):proxy_hide_header Vary; 清除上游误加的头,避免叠加
- 后端接入层:唯一允许 add_header Vary 的位置,且必须与本层 proxy_cache_key 中使用的变量严格匹配











