浏览器中的 HTTP 缓存使用策略

浏览器中的 HTTP 缓存使用策略

Jun 12, 2021
前端, 浏览器, 系统设计, 计算机网络

浏览器使用缓存数据有非常多的好处,很多情况下静态资源会比 js 代码更大,同时修改的频率又非常低,通过对静态资源使用缓存,可以有效减少重复请求,从而节省网络带宽、降低服务器的请求压力。

浏览器的缓存数据一般指的是通过 Get 方法向服务器发送请求获得的数据,其他方法不会缓存。浏览器如何使用这些缓存数据,有不一样的策略,一方面服务器指定了这些策略,同时浏览器在默认情况下也会有自己的做法,下面就开始详细介绍这些缓存策略。

缓存策略 #

服务端可以通过设置响应的 HTTP Response Header 来告诉浏览器资源的缓存策略,浏览器每次发起请求,都会先在浏览器缓存中查找该请求的结果以及缓存标识来决定是否请求服务器,而每次请求服务器都会将该结果和缓存标识存入浏览器缓存中。

浏览器对策略的使用可以归为强制缓存策略和协商缓存策略,其中强制指的就是浏览器先在本地查询缓存结果,协商指的是与服务器进行协商,浏览器以强制缓存优先,就是说强制缓存不生效的情况下,浏览器才会采用协商缓存。

强制缓存 #

强制缓存生效

浏览器会优先使用强制缓存,使用强制缓存策略的场景主要有三种:

  1. 查询资源时发现本地没有缓存数据,于是请求服务器获取数据,即强制缓存失效;
  2. 本地有缓存结果,但是根据缓存标识发现资源已经过期,于是再次请求服务器获取数据,即强制缓存失效;
  3. 本地有缓存结果,并且资源的缓存标识生效中,此时浏览器不会请求服务器,直接返回该结果,即强制缓存生效。

控制强制缓存的字段分别是 Expires 和 Cache-Control,其中 Cache-Control 优先级比 Expires 高,下面详细说一下。

Expires #

Expires 是 HTTP/1.0 控制网页缓存的字段,被用于在 HTTP 的响应头中,其值为服务器返回该请求结果缓存的到期时间,即再次发起该请求时,如果客户端的时间小于 Expires 的值时,直接使用缓存结果。

HTTP/1.1 200 OK
Expires: Wed, 21 Oct 2015 07:28:00 GMT

到了 HTTP/1.1,Expires 已经被 Cache-Control 替代,主要原因在于 Expires 控制缓存的原理是使用客户端的时间与服务端返回的时间做对比,一方面两者的时间有误差,还有就是 Expires 不能做更多维度的失效控制。

Cache-Control #

在 HTTP/1.1 中,Cache-Control 是最重要的规则,主要用于控制网页缓存,Cache-Control 是通用消息头字段,可以被用在请求和响应头中,详细的枚举值可以参考 MDN 文档,主要有:

  • public:所有内容都将被缓存(客户端和代理服务器都可缓存)
  • private:所有内容只有客户端可以缓存,Cache-Control 的默认取值
  • no-cache:客户端缓存内容,但是是否使用缓存则需要经过协商缓存来验证决定
  • no-store:所有内容都不会被缓存,即不使用强制缓存,也不使用协商缓存
  • max-age=600:缓存内容将在 600 秒后失效

协商缓存 #

协商缓存 Header

强制缓存失效后,就会使用协商缓存策略:浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存,所以这个过程是在与服务器进行“协商”,使用协商缓存主要有以下两种情况:

  1. 协商缓存生效,服务器一般返回 304 Not Modified;
  2. 协商缓存失效,服务器一般返回 200 OK。

涉及协商缓存的 Header 标识有:Last-Modified / If-Modified-SinceETag / If-None-Match,其中后者比前者的优先级高,同时存在时只有后者生效。

Last-Modified/If-Modified-Since #

Last-Modified 是一个响应头,值为服务器响应请求时,返回该资源文件在服务器最后被修改的时间。

HTTP/1.1 200 OK
Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT

If-Modified-Since 则是客户端再次发起该请求时,携带上次请求返回的 Last-Modified 值,通过此字段值告诉服务器该资源上次请求返回的最后被修改时间。服务器收到该请求,发现请求头含有 If-Modified-Since 字段,则会根据 If-Modified-Since 的字段值与该资源在服务器的最后被修改时间做对比,若服务器的资源最后被修改时间大于 If-Modified-Since 的字段值,则重新返回资源,状态码为 200;否则则返回 304,代表资源无更新,可继续使用缓存文件。

GET /posts/http-caching-in-browser/xxx.png HTTP/1.1
If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT

ETag/If-None-Match #

Etag 是 HTTP 响应头,表示资源的特定版本,大多情况下可以是一个文件的哈希值,如果给定 URL 中的资源更改,则一定要生成新的 Etag 值。 其中 W/ 是用来区分 Etag 之间的比较是强比较算法还是弱比较算法。

  • 强比较算法:只有在每一个字节都相同的情况下,才可以认为两个文件是相同的。
  • 弱比较算法:两个文件除了每个字节都相同外,内容一致也可以认为是相同的。
HTTP/1.1 200 OK
ETag: W/"<etag_value>"
ETag: "<etag_value>"

If-None-Match 是一个条件式请求首部,当客户端再次发起该请求时,携带上次请求返回的唯一标识 Etag 值,通过此字段值告诉服务器该资源上次请求返回的唯一标识值。服务器收到该请求后,发现该请求头中含有 If-None-Match,则会根据 If-None-Match 的字段值与该资源在服务器的 Etag 值做对比,一致则返回 304,代表资源无更新,继续使用缓存文件;不一致则重新返回资源文件,状态码为 200。

GET /posts/http-caching-in-browser/xxx.png HTTP/1.1
If-None-Match: <etag_value>
If-None-Match: <etag_value>, <etag_value>, …
If-None-Match: *

参考 #

https://mp.weixin.qq.com/s/d2zeGhUptGUGJpB5xHQbOA

本文共 1858 字,上次修改于 Oct 19, 2022,以 CC 署名-非商业性使用-禁止演绎 4.0 国际 协议进行许可。

相关文章

» 说说实际工作中 GraphQL 的使用体验

» 前端的节流和防抖

» 了解下 Protobuf 相关概念

» 实现限流的几种方案

» Go 语言的 MPG 并发调度模型