抹桥的博客
语言
主题色
250

HTTP 缓存

目录

先说结论

HTTP 缓存要分开看两个问题:响应还能直接复用多久,以及需要验证时如何确认有没有变化。响应指令 no-cache 允许存储,但再次复用前要成功向源站验证;no-store 才要求缓存不要存储。下面保留 2016 年的面试和截图背景,机制说明在 2026-10-15 按 RFC 9111 复核,没有新增浏览器或 CDN 实测。

下午意外的接到了阿里钉钉前端的面试,很是欣喜。 面试内容从做的项目,到使用的技术 Angular、React、Express 等,让我感觉到自己的理解还是不够深刻。 比如我说我喜欢 React,大项目中需要配合 Redux 来用处理数据,然后就问到 Redux 到底解决了哪些问题,我没能清楚的答上来。但是整体上,面试的前面部分,自我感觉还是很不错的。 但是到最后,面试官说问一个比较基础的问题:如何在 HTTP 协议中控制缓存? 我懵了,平时工作中根本接触不到。只能凭自己感觉说,从文件名的更改以及在 HTTP 的 header 中设置相应参数来设置,但具体怎样设置,设置的内容是什么,我完全答不上来。感觉肯定是要挂在这里。不过俗话说的好,人不能在同一个地方跌倒两次,谨以此篇记录 HTTP 缓存的相应内容。

概览

HTTP 控制缓存主要有一下几种方式:

  1. Expires
  2. Cache-Control
  3. Last-Modified/If-Modified-Since
  4. Etag/If-None-Match

接下来,就一个一个来了解。

Expires

Expires 给出绝对过期时间,现代 HTTP 中仍然有效。新鲜且允许复用的响应通常可直接使用,但刷新、请求指令等仍可能触发网络请求。有适用的 Cache-Control 新鲜度指令时按后者处理,不能简单理解为四种头的统一优先级。历史截图不代表当前博客配置。

2016 年博客 JS 响应头,标出了 Expires 和 Cache-Control 上图是我博客中一个 js 文件的 HTTP header,可以看到其中红框 1 Expires 设置为当前 Date 的 4 个小时后,也就是说正常情况这个文件在 Expires 这个时间点以前的请求都会直接使用本地缓存,而不用从服务端重新获取。

Cache-control

Cache-control 与 Expires 作用基本相同,都是标志出当前资源的有效期,以此来控制浏览器是使用本地缓存还是从服务器重新获取资源。不过,不同的地方在于,Cache-control 能够控制的更加细致,当 header 中同时存在 Expires 时,Cache-control 的优先级要更高一些。

下面主要说明响应指令,不能和请求指令混为一谈。

指令含义
public允许共享缓存存储本来可能受限制的响应,仍受其他指令约束
private不允许共享缓存存储,浏览器私有缓存仍可存储
no-cache可以存储,但复用前必须成功向源站验证
no-store缓存不得存储该响应
max-age=N响应的新鲜度寿命为 N 秒;年龄还要结合 Date、Age 等计算
s-maxage=N为共享缓存指定新鲜度,覆盖其 max-age 或 Expires
must-revalidate过期后不能在未成功验证的情况下复用

请求的 min-fresh 要求至少还剩指定秒数的新鲜度;max-stale 表达可接受的过期程度,仍受响应限制约束。

Cache-Control 的 public 与四小时 max-age 示例 如图,红框 2 上面的 Cache-Control 这一栏,值为 public,max-age=14400. 就是说它的有效期是 14400s,也就是 4h,和上面的 Expires 的过期时间是一样的,但是它不像 Expires 那样必须给一个类似 Mon,07 Mar 2016 17:14:33 GMT 这样具体的时间,只需要给定一个最大寿命的时间就可以了。这个应该是比较常用的方法。

Last-Modified/If-Modified-Since

服务器可用 Last-Modified 给出表示的修改时间。客户端需要验证已有副本时,可发送 If-Modified-Since;对通常的 GET/HEAD,如果自该时间起没有修改,服务端可返回 304,客户端复用已有正文;有变化则通常返回 200 和新正文。

验证器不要求必须同时设置 Cache-Control,也不是只有 max-age 到期才会验证:no-cache、请求指令、用户刷新等也可能要求验证。没有显式新鲜度信息时,还可能按 HTTP 规则使用启发式新鲜度。

Etag/If-None-Match

ETag 是服务端为选定表示提供的验证标签,不一定是文件哈希。客户端可把已有标签放进 If-None-Match。对 GET/HEAD,匹配时通常返回 304,不匹配时通常返回 200 与新正文。它也不要求必须配合 max-age。

历史缓存判断图,需要区分新鲜度与条件验证 图片来自 图中涉及两个不同阶段:新鲜度判断和条件验证。当条件请求同时有 If-None-Match 和 If-Modified-Since 时,先处理 If-None-Match。浏览器私有缓存和 CDN 共享缓存要分别考虑,个性化响应不能直接照搬 public 的示例。本文不涵盖 Service Worker Cache API、应用缓存和浏览器历史导航缓存的全部行为。

参考文章: 1.浏览器缓存机制 2.浏览器缓存相关的 HTTP 头介绍:Expires,Cache-Control,Last-Modified,ETag

3.HTTP 的请求头标签 If-Modified-Since

规范与现行文档

HTTP 缓存
https://blog.kisnows.com/2016/03/08/http-cache/
作者
Kisnows
发布于
2016-03-08
许可协议
CC BY-NC-ND 4.0