浏览器两次请求同一张图片:强缓存、协商缓存是怎么接力的
1. 同一张图片,第二次请求到底发不发?
浏览器第一次请求 banner.jpg,服务器返回 200 和图片内容。第二次再请求同一张图,有三种可能:
- 不发请求,直接读本地缓存;
- 发请求,服务器说“没变过,用你手里那份”,返回 304,不传图片;
- 发请求,服务器说“变了”,返回 200 和新图片。
哪种发生,取决于两道关卡:先过强缓存,再过协商缓存。两道关卡用的响应头不一样,分工也不一样。
2. 第一道关卡:强缓存,过期前不发请求
第一次请求的响应头里有:
HTTP/1.0 200 OK
Cache-Control: max-age=60
ETag: "3a429cdec9174cd1c3bfd032517076c1"
Last-Modified: Wed, 11 Nov 2026 03:30:00 GMT
Cache-Control: max-age=60 的意思是:这份响应在 60 秒内是新鲜的。60 秒内浏览器再请求同一张图,根本不发请求,直接读缓存,状态显示 200 from cache。服务器完全不知道这次请求发生过。
强缓存只认时间,不认内容。60 秒内服务器上的图片就算换了,浏览器也不会知道——这是强缓存的代价,换来的是零请求、零流量。
3. 第二道关卡:协商缓存,过期后问一句“变了没”
60 秒过去,强缓存过期。浏览器这次要发请求,但先在请求头里带上上次记下的标识,问服务器图片变了没有:
curl -i -H 'If-None-Match: "3a429cdec9174cd1c3bfd032517076c1"' http://127.0.0.1:8899/banner.jpg
服务器拿请求里的 If-None-Match 和当前文件的 ETag 一比对,没变:
HTTP/1.0 304 Not Modified
ETag: "3a429cdec9174cd1c3bfd032517076c1"
304 没有响应体,浏览器复用本地缓存。省了图片传输,只花了一次往返问一句。
如果图片更新了,ETag 对不上,服务器返回 200 和新图片:
curl -i -H 'If-None-Match: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"' http://127.0.0.1:8899/banner.jpg
HTTP/1.0 200 OK
Cache-Control: max-age=60
ETag: "3a429cdec9174cd1c3bfd032517076c1"
Last-Modified: Wed, 11 Nov 2026 03:30:00 GMT
浏览器拿到新图片,同时记下新的 ETag 和新的 max-age=60,下一轮从强缓存重新开始。
4. ETag 和 Last-Modified:谁优先,谁兜底
协商缓存有两个标识,优先级不同:
- ETag / If-None-Match 优先。ETag 是文件内容的哈希摘要,内容变了它一定变,内容没变它一定不变,判断最准。上面三次请求走的就是这条路。
- Last-Modified / If-Modified-Since 兜底。没有 ETag 时,浏览器用上次的
Last-Modified时间问服务器“这个时间之后改过吗”。它的精度只有秒,1 秒内改两次是认不出来的;而且文件被重新保存、内容没变,修改时间变了也会误判为“变了”。
所以有 ETag 就用 ETag,Last-Modified 只当后备。两个标识同时存在时,浏览器先发 If-None-Match。
5. 完整流程:banner.jpg 的三次请求
把上面的实测串起来,max-age=60 的一张图,浏览器是这样走的:
- 第一次请求:没有缓存,发请求,服务器返回 200、图片、
Cache-Control: max-age=60、ETag、Last-Modified。浏览器存下图片和这三个头。 - 60 秒内第二次请求:强缓存未过期,不发请求,200 from cache。
- 60 秒后第三次请求:强缓存过期,发请求带
If-None-Match;图片没变,服务器返回 304,浏览器复用本地图片;图片变了,服务器返回 200 和新图片,浏览器更新缓存,下一轮重新计时。
强缓存决定“问不问”,协商缓存决定“传不传”。max-age 过期不等于重新下载——304 那次只问了一句,图片一个字节都没传。
6. 容易踩的三个坑
- max-age 设得太长,图片更新了用户看不到。强缓存期内浏览器不发请求,服务器换了图也白搭。解法是给静态资源文件名加哈希,内容一变 URL 就变,旧缓存自然失效。
- 只配了 Last-Modified,没配 ETag。1 秒内改两次文件、或者文件被原样重新保存,都会误判。内容型资源优先配 ETag。
- 看到 304 以为是缓存失效。304 恰恰是缓存命中:服务器确认本地那份还能用。真正重新下载的是 200。
- 点赞
- 收藏
- 关注作者
评论(0)