HTTP 条件请求是什么?ETag 和 Last-Modified 有什么区别?
简化版
HTTP 条件请求是客户端带上条件头,让服务器判断资源是否变化,再决定返回完整内容还是 304 Not Modified。常见组合是 If-None-Match 配合 ETag,If-Modified-Since 配合 Last-Modified。ETag 是资源版本标识,可以比时间更精确;Last-Modified 基于修改时间,精度通常到秒,容易受时间粒度和文件更新时间影响。强缓存不发请求,协商缓存会发条件请求。
详细版
条件请求主要用于协商缓存和并发更新保护。
| 响应头 | 请求头 | 常见用途 |
|---|---|---|
| ETag | If-None-Match | 判断资源版本是否一致 |
| Last-Modified | If-Modified-Since | 判断资源修改时间是否晚于客户端缓存 |
| ETag | If-Match | 更新前确认版本未变化 |
| Last-Modified | If-Unmodified-Since | 更新前确认资源未被修改 |
浏览器缓存资源过期后,会带上条件请求头询问服务器。如果资源没变,服务器返回 304,响应体为空或很小,浏览器复用本地缓存;如果资源变了,服务器返回 200 和新内容。优先级上通常 ETag 高于 Last-Modified。
完整版教学
一、条件请求解决什么问题
HTTP 缓存分两类:强缓存和协商缓存。强缓存命中时浏览器不请求服务器;强缓存过期后,并不一定要重新下载完整资源,而是可以发条件请求。
浏览器缓存过期
|
v
带 If-None-Match / If-Modified-Since 请求服务器
|
+-- 资源未变化 -> 304 -> 继续用本地缓存
|
+-- 资源已变化 -> 200 -> 下载新内容
条件请求的价值是减少传输体积,同时保证客户端拿到的资源没有过期。
二、ETag 与 If-None-Match
ETag 是服务器给资源生成的版本标识。客户端第一次请求:
HTTP/1.1 200 OK
ETag: "v1-abc"
Cache-Control: max-age=60
缓存过期后,客户端再次请求:
GET /app.js HTTP/1.1
If-None-Match: "v1-abc"
如果服务器发现当前资源仍然对应 "v1-abc",返回:
HTTP/1.1 304 Not Modified
ETag: "v1-abc"
浏览器就继续使用本地缓存的 app.js。
三、Last-Modified 与 If-Modified-Since
Last-Modified 表示资源最后修改时间。客户端缓存后,会在后续请求中带上:
If-Modified-Since: Wed, 21 Oct 2026 07:28:00 GMT
服务器比较资源最后修改时间。如果没有更新,返回 304;如果更新时间更晚,返回 200 和新资源。
它的缺点也明显:
- 时间精度通常到秒,一秒内多次修改可能判断不准;
- 资源内容未变但修改时间变了,可能误判为需要重新下载;
- 分布式服务器时间不一致会导致判断混乱。
四、ETag 为什么通常更可靠
ETag 可以根据内容 hash、版本号、构建产物指纹或数据库版本生成,因此比单纯时间戳更灵活。
| 对比项 | ETag | Last-Modified |
|---|---|---|
| 判断依据 | 资源版本标识 | 修改时间 |
| 精度 | 可由服务器控制 | 通常秒级 |
| 分布式一致性 | 需要统一生成规则 | 依赖机器时间 |
| 适合场景 | 精确协商缓存 | 静态文件简单缓存 |
面试里可以说:
ETag更精确,Last-Modified更简单;两者同时存在时,服务端通常优先使用ETag。
五、条件请求不只用于缓存
条件请求还可以避免并发更新覆盖。比如客户端修改文章前先拿到:
ETag: "article-v3"
提交更新时带:
PUT /article/1 HTTP/1.1
If-Match: "article-v3"
如果服务器当前版本已经不是 article-v3,说明有人先改了,服务器可以返回 412 Precondition Failed,避免“后提交覆盖先提交”。
这和数据库乐观锁很像:ETag 相当于版本号,If-Match 相当于更新条件。
六、304 响应到底有没有响应体
304 Not Modified 的语义是资源未修改,客户端应使用已有缓存。它通常不带消息体,核心价值是节省响应体传输。
但 304 响应仍然可以携带与缓存相关的头,例如:
Cache-ControlETagExpiresLast-Modified
客户端会用这些头更新本地缓存元信息。不要把 304 理解成错误,它是缓存协商成功的结果。
七、常见误区与追问
- 误区:协商缓存命中时不会发请求。 协商缓存需要向服务器发条件请求,只是资源未变化时返回 304。
- 误区:ETag 和 Last-Modified 完全等价。 ETag 是版本标识,Last-Modified 是时间标识,精度和可靠性不同。
- 误区:304 会返回完整资源内容。 304 通常没有响应体,浏览器使用本地缓存。
- 误区:ETag 一定是文件内容 hash。 ETag 的生成策略由服务器决定,可以是 hash、版本号或其他标识。
- 追问:ETag 和 Last-Modified 同时存在谁优先? 通常优先使用 ETag,因为它能更精确表达资源版本。
- 追问:If-Match 有什么用? 它可用于并发更新保护,只有资源版本匹配时才允许修改。
八、加强记忆
条件请求就是“先问服务器资源有没有变”。ETag -> If-None-Match 看版本,Last-Modified -> If-Modified-Since 看时间;没变返回 304,变了返回 200 和新内容。强缓存是不请求,协商缓存是发条件请求。回答时再补一句:If-Match 可以做乐观并发控制,说明你不只会背浏览器缓存,还理解 HTTP 条件语义。