ETag 是服务端基于资源内容生成的唯一标识符,配合客户端自动携带的 If-None-Match 实现精准协商缓存:匹配则返回 304,否则返回 200 及新 ETag;强校验要求字节一致,弱校验允许语义等价。

ETag 配合 If-None-Match 是浏览器实现协商缓存(Conditional Caching)最常用、最精准的机制之一。它不依赖时间(不像 Last-Modified),而是基于资源内容生成唯一标识,服务端通过比对标识决定是否返回 304,从而节省带宽和提升响应速度。
ETag 是什么?怎么生成?
ETag(Entity Tag)是服务端为某个资源生成的唯一字符串标识,通常反映资源内容的哈希值(如 "abc123" 或 W/"def456")。强 ETag(无前缀或带 ")要求字节级一致;弱 ETag(以 W/ 开头)允许语义等价(如 HTML 中空白差异可忽略)。
常见生成方式:
- 对响应体做 MD5 或 SHA-256 哈希(适合静态文件或小响应)
- 结合内容哈希 + 版本号或最后修改时间戳(增强一致性)
- 对数据库记录生成复合 ETag(如
"W/"user-123-v2.1"")
客户端如何发起协商请求?
当资源已缓存且响应中包含 ETag 头时,浏览器在后续请求中自动带上 If-None-Match 请求头,值为之前收到的 ETag 字符串(包括引号)。
立即学习“Java免费学习笔记(深入)”;
例如,上次响应含:
ETag: "a1b2c3d4"
则下次请求会自动携带:
If-None-Match: "a1b2c3d4"
注意:浏览器会自动处理引号匹配,无需手动加引号或转义;若缓存有多个 ETag(如使用 Vary),浏览器可能发送逗号分隔列表。
服务端如何正确验证并响应?
服务端需解析 If-None-Match,计算当前资源的 ETag,并严格比对:
- 若完全匹配(含引号与大小写),且资源未变更 → 返回 304 Not Modified,响应体为空,可带
ETag和缓存相关头(如Cache-Control) - 若不匹配或 ETag 不存在 → 正常返回 200 OK,并在响应中带上新
ETag - 若客户端发来
*(如首次请求或强制刷新),表示“只要资源存在就用”,服务端应跳过比对直接返回 200(除非资源已删除)
关键细节:
– 比对时需忽略弱/强标识的语义,但必须保留原始格式(W/"x" ≠ "x");
– 若资源支持多种表示(如 gzip / br 编码),ETag 应体现编码差异,或配合 Vary: Accept-Encoding 使用。
JavaScript 中能主动控制吗?
纯前端 JavaScript 无法直接设置或读取 ETag / If-None-Match —— 这些由浏览器自动管理。但你可以间接影响行为:
- 用
fetch(url, { cache: 'default' })保持协商缓存启用(默认行为) - 避免手动加
cache-bust参数(如?t=123),否则绕过所有缓存 - 需要强制刷新时,用
cache: 'reload'或cache: 'no-store',此时不发If-None-Match - 服务端渲染(SSR)或 CSR 场景下,确保 API 接口也返回正确 ETag(如 Express 中用
res.set('ETag', etag))
真正可控的环节在服务端逻辑:计算 ETag 要稳定、及时更新、覆盖所有影响响应的因素(内容、语言、编码、权限等)。


















