Vary 决定缓存怎么分辨版本
同一个 URL 可能因为请求头不同而返回不同内容:带 Accept-Encoding 是 br 版本,带 Origin 是给某个站的跨域头。Vary 就是告诉缓存「这份响应依赖哪些请求头」,让缓存把这些头纳入缓存键。不写 Vary 的后果是缓存把某个版本当成通用版本发出去,典型表现是 A 站点拿到 B 站点的跨域响应,或者压缩版本被发给不支持的客户端。
基本信息
| 响应头名 | Vary |
|---|---|
| 作用 | 扩展缓存键,避免不同变体互相污染 |
| 规范 | RFC 9110 第 12.5.5 节,缓存键规则见 RFC 9111 |
| 高频取值 | Accept-Encoding(压缩),Origin(跨域),Accept(内容协商) |
| 特殊取值 | Vary: * 表示响应不可缓存 |
| 方向 | 只在响应里出现,客户端不需要发送 |
配置与验证
配置与验证
curl -sI https://www.example.com/ | grep -i vary # 看站点声明了哪些变体维度
curl -sI -H 'Accept-Encoding: gzip' https://www.example.com/ | grep -iE 'vary|content-encoding'
# nginx 跨域场景:回显 Origin 时必须同时声明 Vary,否则缓存会串站
# location /api/ {
# add_header Vary "Origin";
# add_header Access-Control-Allow-Origin "$http_origin" always;
# }
curl -sI -H 'Origin: https://a.example.com' https://api.example.com/x | grep -iE 'vary|access-control'取值与含义
| 写法 | 含义 | 典型场景 |
|---|---|---|
| Vary: Accept-Encoding | 按压缩算法区分版本 | 几乎所有压缩过的响应都应带 |
| Vary: Origin | 按请求来源区分版本 | 动态回显跨域来源的接口 |
| Vary: Accept | 按可接受的媒体类型区分 | 同一地址返回 JSON 与 HTML 两种表示 |
| Vary: User-Agent | 按客户端标识区分 | 移动端与桌面端返回不同页面 |
| Vary: * | 响应不可缓存 | 高度个性化且无法用头部维度描述的内容 |
典型使用场景
- 排查跨域内容串站:A 站点拿到 B 站点的跨域响应头,通常是缺少 Vary: Origin
- 排查乱码与解压失败:客户端拿到不支持的压缩版本,属于缺少 Vary: Accept-Encoding
- 多端适配场景:移动端与桌面端返回不同内容时必须声明区分维度
- 缓存命中率优化:核对 Vary 维度是否过多,避免缓存被切得过碎
- 灰度与个性化内容:确认响应是否真的可缓存,必要时改为不可缓存
动态回显请求头的接口必须同时声明 Vary,否则 CDN 会把某个来源的响应发给其它来源。跨域与压缩是最容易踩到的两处,配置时应把两者当成固定搭配。