If-None-Match 是什么
If-None-Match 是条件请求头:当资源的当前 ETag 与客户端给出的值不匹配时,请求条件才成立。它在读场景里用来做缓存校验——内容没变就返回 304,省掉重复传输;在写场景里可以表达「资源不存在才创建」,防止覆盖已有对象。它与 If-Modified-Since 同时出现时,规范要求以 ETag 的判定优先。
基本信息
| 方向 | 请求头 |
|---|---|
| 取值 | 一个或多个 ETag,或用通配符 "*" |
| 读场景命中 | 内容未变,返回 304 Not Modified |
| 写场景不成立 | 资源已存在,返回 412 Precondition Failed |
| 通配语义 | "*" 表示「资源不存在时条件成立」 |
| 规范 | RFC 9110 §13.1.2 |
典型使用场景
- 静态资源缓存校验:浏览器带上次收到的 ETag,命中则回 304 只传头
- 接口轮询:内容未变时不传正文,显著降低带宽
- 幂等创建:用 If-None-Match: * 保证「只在不存在时写入」
- 配合 CDN:回源校验由源站决定是否 304,减少回源流量
- 避免覆盖:上传前先检查对象是否已存在
缓存校验与条件创建
缓存校验与条件创建
# 读场景:内容未变返回 304
curl -si -H 'If-None-Match: "v17"' https://static.example.com/app.js | head -1
# HTTP/1.1 304 Not Modified
# 写场景:只有资源不存在时才创建,已存在则 412
curl -si -X PUT https://api.example.com/keys/k1 \
-H 'If-None-Match: *' -d 'value'
# 先取 ETag 再校验
ETAG=$(curl -sI https://static.example.com/app.js | awk 'tolower($1)=="etag:"{print $2}' | tr -d '\r')
curl -si -H "If-None-Match: $ETAG" https://static.example.com/app.js | head -1用法与结果
| 用法 | 条件成立时 | 条件不成立时 |
|---|---|---|
| GET + If-None-Match: ETag | 回 200 与完整内容 | 回 304,不传正文 |
| GET + If-None-Match: * | 资源不存在时回 404 | 资源存在时回 200 |
| PUT + If-None-Match: * | 资源不存在,执行创建 | 已存在,回 412 拒绝覆盖 |
| PUT + If-None-Match: ETag | 版本不同,执行更新 | 版本相同,回 412 |
两者同时出现时 If-None-Match 优先,If-Modified-Since 被忽略:这是规范明确规定的,也是「为什么我改了时间戳但缓存还是旧的」这类疑问的答案。缓存设计上建议以 ETag 为主。
304 响应不携带正文,但应带上必要的缓存头(如 Cache-Control、ETag),否则客户端可能在下一次请求时缺少判断依据,缓存策略出现抖动。
把 If-None-Match 用在写接口时要确认服务端的并发语义:两个请求同时带 If-None-Match 通配值到达时,只有真正做了原子判定的实现才能挡住第二个,先查后写在并发下依然会重复创建。