理解浏览器缓存机制

1,319 阅读4分钟
原文链接: newbyvector.github.io

理解浏览器缓存机制

介绍

浏览器缓存可以提高网页打开速度、减少 Http 请求,在提高用户体验、重复利用资源、减少网络带宽上发挥着重要的作用。特别是现在用户对网站响应速度要求特别高的情况下,高效地利用浏览器缓存机制可以使网站性能优化事半功倍。
下面对浏览器缓存机制做一些分析。

概念

HTTP缓存可以分为强缓存协商缓存:
  1. 强缓存:强缓存命中不会发送请求到服务器端,直接从本地缓存中获取资源,状态码 200 ( from cache )
  2. 协商缓存:协商缓存会发送请求到服务器,服务器通过请求头部字段来验证资源是否命中协商缓存,如果命中,则返回状态码 304 ( not modified ),通知浏览器从缓存中获取资源。

示意图

分析

根据响应头部的字段确定浏览器缓存策略

响应头中与缓存策略相关的字段包括 Cache-Control, Pragma, Expires, 下面分析这三个字段。

Cache-Control

Cache-Control 是 Http/1.1 新增的字段,是控制浏览器缓存的主要字段。它的主要内容如下:
  1. no-cache:资源可以被缓存,但立刻过期,下次访问必须验证资源有效性
  2. max-age:缓存资源,在指定时间后过期(单位为秒)
  3. no-store:资源不会被缓存
  4. public:资源可以被浏览器和代理服务器缓存
  5. private: 资源只能被浏览器缓存

Pragma

Pragma 是 Http/1.0 的头部字段,只有一个值 no-cache, 功能和 Cache-Control:no-cache 一样。

Expires

Expires 是缓存到期时间,以服务器时间为参考,优先级比 Cache-Control: max-age 低。 在一些场景中必须避免浏览器缓存,推荐的做法是设置请求头:Cache-Control: no-cache, no-store, must-revalidate

强缓存命中条件

我们会发现,最优的做法是让一些公开资源命中强缓存,这是响应最快的。那么命中强缓存的条件又是什么呢?

  1. 请求头部不包括 Pragma 字段
  2. 响应头部 Cache-Control 中不包括 no-cache、no-store
  3. 响应头部 max-age 或者 Expires 大于请求日期

如果我们在很短的一段时间内多次访问同一个资源,并且响应头部却没有 max-age 或者 Expires 信息,是不是就不会命中强缓存了呢?不是的,浏览器会做出优化,默认采用一个启发式算法,取响应头的 (Date - Last-Modified) * 0.1 作为缓存有效时间,只要是在这段时间内请求这个资源,即使没有缓存过期字段,也会命中强缓存。

服务器端校验资源是否修改机制

Last-Modified/If-Modified-Since

服务器响应资源的时候返回一个头部字段 Last-Modified,代表该资源最后修改时间,当浏览器再次向服务器请求该资源时,会传送 If-Modified-Since 信息,值就是上次服务器响应的最后修改时间, 服务器将这个请求时间与本地资源实际最后修改时间做对比,如果文件没有被修改,则返回状态码 304,通知浏览器从缓存中读取资源文件。

ETag/If-None-Match

ETag 是一个响应首部字段,它是根据资源内容生成的一段hash字符串,标识资源的状态,由服务端产生。当浏览器再次向服务器请求该资源时,会传送 If-None-Match 字段,服务器收到请求后,拿 If-None-Match 字段的值与资源的实际 ETage 值进行比较,若相同,则命中协商缓存,返回状态码 304ETag 优先级比 Last-Modified 高,同时存在时会以ETag 为准。

那已经有了 Last-Modified 机制,为什么要引入 ETage 机制呢?

  1. 某些服务器不能精确得到资源的最后修改时间,这样就无法通过最后修改时间判断资源是否更新
  2. 如果资源修改非常频繁,在秒以下的时间内进行修改,而 Last-Modified 只能精确到秒
  3. 一些资源的最后修改时间改变了,但是内容没改变,使用 ETag 就能判别出资源内容是否被修改

浏览器缓存的使用

使用缓存的过程其实就是根据场景设置 Cache-Control 的值的过程: