ETag 是什么
ETag 是响应内容的版本标识,一个不透明的字符串,由服务端生成。客户端把它带回来做条件请求,服务端比对后决定返回完整内容还是「未修改」,因此它是协商缓存的核心;同时它也被用来做并发控制,写入前先比对版本,避免覆盖别人刚提交的修改。它分强校验与弱校验两类:强校验要求字节级完全一致,弱校验只要求语义等价,前缀不同,能用的场景也不同。
基本信息
| 名称 | ETag(RFC 9110 §8.8.3) |
|---|---|
| 作用 | 标识响应表示的版本,用于缓存校验与并发控制 |
| 请求侧配套 | 条件获取与条件写入两类请求头 |
| 强校验 | 要求内容完全一致,可用于写入冲突检测 |
| 弱校验 | 只要求语义等价,不适合做写入冲突检测 |
典型使用场景
- 静态资源二次访问:内容没变就只回一个未修改,省下传输
- 接口数据校验:变化不频繁的列表接口降低带宽消耗
- 并发编辑保护:提交前比对版本,避免后写覆盖先写
- 发布校验:确认新版本资源是否真的已经上线
- 多节点一致性检查:比对各节点同一资源的版本标识
条件获取与并发写入
条件获取与并发写入
curl -sI https://static.example.com/app.js | grep -i etag # 先看服务端给出的版本标识
curl -si https://static.example.com/app.js -H 'If-None-Match: v17' # 条件获取,命中返回未修改
curl -i -X PUT https://api.example.com/items/1 -H 'If-Match: v17' -H 'Content-Type: application/json' -d '{}' # 条件写入,版本不符则拒绝生成方式的影响
| 生成方式 | 特点与风险 |
|---|---|
| 基于内容摘要 | 内容相同标识就相同,跨节点稳定,最适合集群 |
| 基于修改时间与长度 | 实现简单,但重建文件后可能变化 |
| 基于节点本地特征 | 多节点部署时同一资源标识来回变化,缓存持续失效 |
排查要点
- 缓存总是失效时先看标识是否每次请求都在变,这比看缓存策略更直接
- 并发写入被拒绝时确认是版本不匹配,而不是权限或参数问题
- 集群环境要求所有节点使用一致的生成算法,否则负载均衡会把用户带到不同的标识上
用弱校验做并发写入控制会导致冲突检测失效:语义等价但内容不同的版本也会匹配通过,最终覆盖掉别人的修改。写入路径必须使用强校验标识。
反向代理与后端各自生成标识时,后写的一方会覆盖前者,客户端最终拿到的可能不是你预期的那一层。遇到标识与内容对不上时,先确认是哪一层在生成。