Skip to content

HTTP 缓存策略

对于一些具有重复性的 HTTP 请求,比如每次请求得到的数据都一样的,我们可以把这对 「请求-响应」的数据都缓存在本地,那么下次就直接读取本地的数据,不必在通过网络获取 服务器的响应了,这样的话 HTTP/1.1 的性能肯定肉眼可⻅的提升。

HTTP 主要有 2 种缓存方式:强缓存和协商缓存

强缓存

主要是判断缓存有没有过期,由浏览器来决定是否使用缓存。

强缓存的命中条件:响应头中是否包含这两个字段

  • Cache-Control 相对时间

  • Expires 绝对时间

Cache-Control 优先级高于 Expires

流程如下:

  1. 浏览器第一次访问资源时,服务器在响应头加上 Cache-Control,设置过期时间

  2. 浏览器再次访问资源时,根据请求发出的时间(Date 字段),和 Cache-Control 进行比对,如果未过期,使用缓存,否则重新发起请求

  3. 浏览器缓存过期的情况下重新请求,会根据响应头再次更新 Cache-Control

协商缓存

通过服务端告知客户端是否可以使用缓存的 方式被称为协商缓存。

协商缓存通过两种情况实现:

  1. 请求头 If-Modified-Since + 响应头 Last-Modified,服务端判断如果 Last-Modified 大于 If-Modified-Since,返回最新资源 + 状态码 200,否则返回状态码 304 通知浏览器使用缓存

  2. 请求头 If-None-Match + 响应头 ETag,服务器判断 If-None-Match 资源是否有改变,如果改过,返回最新资源+ETag+状态码 200,否则返回状态码 304 通知浏览器使用缓存

第 2 种方式(ETag)优先级更高。

第 1 种方式是基于时间实现的,第 2 种方式是基于唯一标识实现的,相对来说后者更准确一些,它避免了因时间篡改导致的不可靠问题,主要体现在 3 个方面:

  1. 在没有修改文件内容的情况下,文件最后的修改时间也会改变,导致浏览器认为文件被改过了

  2. 文件的改动可能是毫秒级的,而 If-Modified-Since 是秒级的,导致不精确的问题

  3. 有些服务器不能精确获取到文件最后的修改事件

一个坑点

我面试字节的时候被面试官挖了个坑,如果一个响应同时包含 Date 和 Last-Modified 字段,哪个优先级更高?一时没答出来。

后来查了 HTTP 协议规范才了解到:

如果一个响应同时包含了 Date 和 Last-Modified,浏览器会优先使用 Last-Modified 验证请求的有效性,而 Date 字段只能表示响应的创建时间,不能准确地反映内容的变化。

所以应该还是按上述命中协商缓存的流程来,这里只是面试官挖了个坑干扰了一下。