Skip to main content

HTTP 缓存控制

前端缓存主要分为 HTTP 缓存浏览器本地存储

其中 HTTP 缓存是在 HTTP 请求传输时用到的缓存,主要在服务器代码上设置;而浏览器本地存储则主要由前端开发在前端 JS 上进行设置。

缓存可以说是性能优化中简单高效的一种优化方式了。一个优秀的缓存策略可以缩短网页请求资源的距离,减少延迟,并且由于缓存文件可以重复利用,还可以减少带宽,降低网络负荷。

一、什么是 HTTP 缓存

HTTP 缓存指的是: 当客户端向服务器请求资源时,会先抵达浏览器缓存,如果浏览器有“要请求资源”的副本,就可以直接从浏览器缓存中提取资源。

常见的 HTTP 缓存只能缓存 GET 请求响应的资源,对于其他类型的响应则无能为力,所以后续说的请求缓存都是指 GET 请求。

HTTP 缓存都是从第二次请求开始的。

  • 第一次请求资源时,服务器返回资源,并在 respone header 头中回传资源的缓存参数;
  • 第二次请求时,浏览器判断这些请求参数,命中强缓存就直接 200,否则就把请求参数加到 request header 头中传给服务器,看是否命中协商缓存,命中则返回 304,否则服务器会返回新的资源。

HTTP 缓存有以下好处:

  • 减少了冗余的数据传输,节省了网费;
  • 缓解了服务器的压力,大大提高了网站的性能;
  • 加快了客户端加载网页的速度。

二、浏览器的缓存机制

浏览器第一次向服务器发起请求并拿到请求结果后,会根据响应报文中 HTTP 头的缓存标识,决定是否缓存结果,是则将请求结果和缓存标识存入浏览器缓存中,过程如下:

根据缓存标识和缓存结果来决定使用强缓存、协商缓存还是直接向服务器发起请求,主要有以下三种情况:

1、不存在缓存标识和缓存结果

不存在缓存标识和缓存结果,则强制缓存失效,直接向服务器发起请求:

2、存在缓存标识和失效的结果

存在缓存标识和缓存结果,但该结果已失效,则强制缓存失效,使用协商缓存:

3、存在缓存标识和未失效的结果

存在缓存标识和缓存结果,且该结果尚未失效,则强制缓存生效,使用强缓存,直接返回该结果:

三、HTTP 缓存的分类

根据是否需要重新向服务器发起请求来分类,可分为强缓存和协商缓存;

根据是否可以被单个或者多个用户使用来分类,可分为私有缓存和共享缓存。

强缓存如果生效,不需要再和服务器发生交互,而协商缓存不管是否生效,都需要与服务端发生交互。

下面是强缓存和协商缓存的一些对比:

强缓存协商缓存
缓存存放位置本地浏览器
HTTP 状态码200304
谁来决定
  • Pragma
  • Cache-Control
  • Expires
  • ETag / If-Not-Match
  • Last-Modified / If-Modified-Since
操作是否有效
  • Ctrl+F5 强制刷新

    无效

  • F5 刷新

    无效

  • 地址栏回车

    有效

  • 页面链接跳转

    有效

  • 新开窗口

    有效

  • 前进、后退

    有效

  • Ctrl+F5 强制刷新

    无效

  • F5 刷新

    有效

  • 地址栏回车

    有效

  • 页面链接跳转

    有效

  • 新开窗口

    有效

  • 前进、后退

    有效

四、强制缓存

强缓存指的是在缓存数据未失效的情况下(即 Cache-Control 的 max-age 没有过期或 Expires 的缓存时间没有过期),会直接使用浏览器的缓存数据,不会再向服务器发送任何请求,这种方式页面的加载速度是最快的,性能也是很好的。

强缓存如果服务器端的资源修改了,页面上是拿不到的,因为它不会再向服务器发请求了。这时就需要 Ctrl + F5 强制刷新页面了。

强缓存生效时,HTTP 状态码为 200

跟强缓存相关的 header 头属性有 Pragma / Cache-Control / Expires,详情如下:

header 属性可选值优先级优缺点
Pragmano-cache:不直接使用缓存,根据新鲜度来使用缓存
  • 响应头不支持这个属性
  • 在 HTTP/1.1 中已被废弃
Cache-Control
  • no-cache:不直接使用缓存,根据新鲜度来使用缓存
  • no-store:不直接使用缓存,每次都是请求下载新资源
  • max-age:缓存时长
  • public / private(默认):是否只能被单个用户使用
  • must-revalidate:每次访问需要缓存校验
  • 请求头和响应头都支持这个属性
  • 不适用与 HTTP/1.0
  • 在缓存未失效前,获取不到修改后的资源
ExpiresGMT 时间
  • 服务器和客户端的时间不一致会出问题
  • 在缓存未失效前,获取不到修改后的资源

注意:强缓存情况下,只要缓存还没过期,就会直接从缓存中取数据,就算服务器端有数据变化,也不会从服务器端获取了,这样就无法获取到修改后的数据。

解决办法:在修改后的资源加上随机数,确保不会从缓存中取。例如:

  • http://www.leophen.cn/kim/common.css?v=22324432
  • http://www.leophen.cn/kim/common.2312331.css

五、协商缓存

协商缓存指的是强缓存失效后,浏览器携带缓存标识向服务器发送请求,由服务器根据缓存标识决定是否使用缓存的过程。

这里携带的缓存标识指的是 Etag 和 Last-Modified,服务器校验后如果资源没有修改,则返回 304 状态码,浏览器直接使用缓存。

如果资源有更新则返回 200 状态码,服务器就会返回更新后的资源并且将缓存信息一起返回。

跟协商缓存相关的 header 头属性有 ETag / If-Not-Match 、Last-Modified / If-Modified-Since,请求头和响应头需要成对出现,详情如下:

header 属性可选值优先级优缺点
ETag / If-Not-Match校验值
  • 默认使用 hash 算法,在分布式环境下可能会出现不同服务器生成的 ETag 值不一致
  • 精确的判断资源有无被修改,可识别一秒内的修改次数
  • 计算 ETag 需要性能消耗
Last-Modified / If-Modified-SinceGMT 时间
  • 只要资源修改,无论内容有无变化,都会将资源返回客户端
  • 以时刻为标识,无法获取一秒内的修改变化
  • 某些服务器不能准确获取最后的修改时间

注意:应尽量减少 304 的请求,因为协商缓存每次都会与后台服务器进行交互,所以性能上不是很好。从性能上来看尽量多使用强缓存。

六、如何使用 HTTP 缓存

1、html 页面缓存的设置

html 页面缓存的设置主要是嵌入 <meta> 标签。

  • html 设置缓存如下:
<!-- 仅 IE 才识别的标签 -->
<meta http-equiv="Expires" content="Mon, 20 Aug 2018 23:00:00 GMT" />
<!-- 主流浏览器识别的标签 -->
<meta http-equiv="Cache-Control" content="max-age=7200" />

2、静态资源的缓存

  • 静态资源的缓存一般是在 web 服务器上配置的。

3、不想使用缓存的方式

  • Ctrl + F5 强制刷新,会直接向服务器提取数据。
  • F5 刷新,默认加上 Cache-Control:max-age=0,走协商缓存。
  • 在 IE 下不想使用缓存的做法:工具 -> Internet 选项 -> 常规 -> 浏览历史记录 设置选择“从不”,然后保存。最后点击“删除”把 Internet 临时文件都删掉(IE 缓存的文件就是 Internet 临时文件)
  • html 页面禁用缓存的设置如下:
<!-- 仅 IE 才识别的标签,不一定会在请求字段加上 Pragma,但会让当前页面每次都发新请求 -->
<meta http-equiv="pragma" content="no-cache">
<!-- 仅 IE 才识别的标签,仅作为知会 IE 缓存时间的标记,不能在请求或响应报文中找到 Expires 字段 -->
<meta http-equiv="expires" content="0">
<!-- 主流浏览器识别的标签 -->
<meta http-equiv="cache-control" content="no-cache">