一文讲透HTTP缓存

2,044 阅读10分钟

概述

本文从HTTP缓存策略为入口,讲解HTTP缓存在浏览器的应用。
文章按强缓存协商缓存启发式缓存三个类别,进行深入剖析。

HTTP缓存策略

HTTP缓存分为三大策略:

  • 存储策略
  • 过期策略
  • 对比策略(也叫协商策略)

存储策略

存储策略,用于决定HTTP响应内容,是否可以缓存到客户端。

Cache-Control头的max-ageno-cacheno-storepublicprivates-maxage,使用存储策略,来指明资源文件是否可以被缓存。

过期策略

过期策略,用于决定客户端是否可以直接从本地缓存中读取文件数据,而不需要发起HTTP请求。

响应头包含Cache-Control: public的文件,虽然会被缓存,但不能明确当前文件是否在有效期内,所以需要其他字段做好“过期策略”。

强缓存的Expires字段,就是用 “过期策略” 定义缓存文件的有效期。借此,浏览器可判断是否需要发起HTTP请求。

响应头包含Cache-Control: max-age=<seconds>的文件,则包含存储策略和过期策略。

具体的策略应用,可以详细查阅下文的Cache-Control章节。

对比策略

将本地缓存文件的数据标识,发送到服务端进行验证,判断文件是否有效。这种策略,就叫对比策略,也叫协商策略。

对比策略,用于协商缓存场景,对应的字段是:

  • Last-Modified 和 If-Modified-Since
  • ETag 和 If-None-Match

例如:

ETag用于存储缓存文件的哈希值。
浏览器需要判断当前缓存文件是否有效时,需要将ETag的值放入请求头If-None-Match字段,发送到服务端。
服务端接收到请求后,对比If-None-Match中的值与最新文件的值是否一致,来决定是否使用缓存。
当两个值一致时,则返回HTTP状态码304,告知浏览器,可使用本地缓存文件。
当两个值不一致时,则返回HTTP状态码200,并携带最新的文件返回给浏览器。

具体的策略应用,可以详细查阅下文的协商缓存章节。

小结

image.png

强缓存

强缓存通过字段ExpiresCache-Control来控制本地缓存文件的有效期。
如果本地缓存有效,则浏览器不会发起HTTP请求。

在浏览器控制台NetWork中的体现为:
200 OK (from disk cache) 或者 200 OK (from memory cache)

释义

  • 200 OK (from disk cache) HTTP状态码200,缓存的文件从硬盘中读取
  • 200 OK (from memory cache) HTTP状态码200,缓存的文件从内存中读取

强缓存的字段

字段协议版本缓存类型响应头请求头
ExpiresHTTP1.0强缓存⭕️
Cache-ControlHTTP1.1强缓存⭕️⭕️

HTTP1.1字段 优先级比 HTTP1.0字段高。

Expires

Expires表示缓存的过期时间,时间代表的是服务端的时间
如果本地时间小于Expires的时间,则在有效期内。浏览器会直接读取缓存,不会发起HTTP请求。

Expires: Sun, 14 Jun 2020 02:50:57 GMT  

缺点

Expires受限于本地时间,如果本地时间修改,则可能会导致缓存失效。

Cache-Control

Cache-Control比较特殊,可以在响应头请求头中使用。它通过提供不同的值,来定义缓存策略。

Cache-Control是所有缓存定义字段中,优先级最高的。

Cache-Control字段取值含义存储策略过期策略响应头请求头
max-age缓存资源, 但是在指定时间(单位为秒)后缓存过期⭕️⭕️⭕️⭕️
no-cache相当于max-age:0,must-revalidate即资源被缓存, 但是缓存立刻过期, 同时下次访问时强制验证资源有效性⭕️⭕️⭕️⭕️
no-store请求和响应都不缓存⭕️⭕️⭕️
public资源将被客户端和代理服务器缓存⭕️⭕️
private资源仅被客户端缓存, 代理服务器不缓存⭕️⭕️
s-maxage依赖public设置, 覆盖max-age, 且只在代理服务器上有效⭕️⭕️⭕️
must-revalidation / proxy-revalidation如果缓存失效, 强制重新向服务器(或代理)发起验证(因为max-stale等字段可能改变缓存的失效时间)⭕️⭕️
max-stale指定时间内, 即使缓存过时, 资源依然有效⭕️⭕️
min-fresh缓存的资源至少要保持指定时间的新鲜期⭕️⭕️
only-if-cached仅仅返回已经缓存的资源, 不访问网络, 若无缓存则返回504⭕️
no-transform强制要求代理服务器不要对资源进行转换, 禁止代理服务器对 Content-EncodingContent-RangeContent-Type字段的修改(因此代理的gzip压缩将不被允许)⭕️⭕️

释义

  • Cache-Control:max-age=31536000 距离请求发起的时间 + 31536000秒之后,才会过期
  • Cache-Control: must-revalidate 缓存过期的任何情况下,都必须发起请求重新验证
  • Cache-Control: s-maxage=60 同max-age作用一样,距离请求发起的时间 + 60秒之后,才会过期。
    只在代理服务器中生效(比如CDN缓存),s-maxage优先级高于max-age,只对 public 缓存有效。设置了 s-maxage,没设置 public,代理服务器也可以缓存这个资源。
  • Cache-Control: no-store 所有内容都不会被缓存,即不使用强制缓存,也不使用协商缓存。
  • Cache-Control: no-cache 是否使用本地缓存都需要经过协商缓存来验证决定。使用Etag 或者Last-Modified字段来控制缓存。
  • Cache-Control:max-age=31536000,max-stale=60 距离请求发起的时间 + 31536000秒 + 60秒之后,缓存才会失效。
    max-stale表示最大容忍的过期时间,单位是秒。
  • Cache-Control:max-age=31536000, min-fresh=60距离请求发起的时间 + 31536000秒 - 60秒之后,缓存才会失效。
    min-fresh表示最小要留有N秒的新鲜度,单位是秒。

当max-age 与 max-stale 和 min-fresh 同时使用时, 它们的设置相互之间独立生效, 最为保守的缓存策略总是有效。
即哪个过期时间最早,就在这个过期时间后,发起资源请求,重新向服务端做验证。

协商缓存

当浏览器对某个资源的请求,没有命中强缓存,并在本地查找到缓存文件,则会发一个请求到服务器,验证本地缓存是否有效。
如果本地缓存文件有效,服务端响应请求,返回HTTP状态为:304(Not Modified), 不带消息主体。
如果本地缓存文件过期,服务端响应请求,返回HTTP状态为:200,并携带资源实体数据。

协商缓存的字段

协商缓存字段分为两种:

  • Last-Modified 和 If-Modified-Since
  • ETag 和 If-None-Match
字段Header类型协议版本缓存类型
Last-ModifiedResponse(响应头)HTTP1.0协商缓存
If-Modified-SinceRequest(请求头)HTTP1.0协商缓存
ETagResponse(响应头)HTTP1.1协商缓存
If-None-MatchResquest(请求头)HTTP1.1协商缓存

HTTP1.1字段 优先级比 HTTP1.0字段高。

Last-Modified 和 If-Modified-Since

Last-Modified表示本地文件的最后修改日期(精确到秒级)。

当浏览器发起资源请求时,会将文件的Last-Modified值,放入If-Modified-Since 中,发送给服务端,询问该文件在该日期后,是否有更新。

如果在本地打开并修改缓存文件,则会导致Last-Modified日期被修改

以下是例子:

  • 响应头
Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT  
  • 请求头
If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT  

缺点

  • 当本地编辑缓存文件时,缓存会失效。(虽然内容可能没改动)
  • If-Modified-Since只能检测到秒级的修改,如果是1秒内修改了N次,则无法判断文件的有效性。

注意点

  • Last-Modified是服务端响应头Response Headers中的字段。
  • If-Modified-Since 是客户端请求头Request Headers中的字段。
  • If-Modified-Since 只可以用在 GET 或 HEAD请求中
  • 当与 If-None-Match一同出现时,If-None-Match的优先级更高。If-Modified-Since会被忽略掉,除非服务器不支持 If-None-Match

ETag 和 If-None-Match

ETag像文件的指纹一样,每次内容一更改,ETag值都会发生变化。

当浏览器发起资源请求时,会将上一次文件的ETag值,放入If-None-Match 中,发送给服务端,询问该文件是否有更新。

ETag值之间的比较采用的是弱比较算法,即两个文件除了每个字节都相同外,内容一致也可以认为是相同的。例如,如果两个页面仅仅在页脚的生成时间有所不同,就可以认为二者是相同的。

举个例子:

ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"  
ETag: W/"0815"  

其中,'W/'(大小写敏感) 表示使用弱验证器

注意点

ETag是服务端响应头Response Headers中的字段。
If-None-Match 是客户端请求头Request Headers中的字段。

避免“空中碰撞”与 HTTP状态码412

空中碰撞,是指同时有多个人修改同个文件,产生竞态。
如果服务端接收每个人的保存请求,则会出现相互覆盖的状态。
所以,需要依靠ETag值,在保存前,做一些校验,以避免这种情况。

当需要修改或上传文件时,包含有If-Match头( ETag 值)的POST请求,会发送给服务端。

If-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"  

如果服务端的文件哈希值与If-Match头的值不相等,则证明文件已经修改过。
这种情况下,服务端可以返回HTTP状态码412 Precondition Failed(先决条件失败)表示客户端错误,拒绝处理请求。

启发式缓存

在资源请求的响应头中没有出现ExpiresCache-Control: max-age, 或 Cache-Control:s-maxage 字段, 并且设置了Last-Modified, 那么浏览器默认会采用一个启发式的算法

启发式缓存的算法

取响应头的Date值 - Last-Modified值的结果的10%作为缓存时间。

详细可查看Caching in HTTP 中的介绍,笔者截取部分原文如下:

If none of Expires, Cache-Control: max-age, or Cache-Control: s- maxage (see section 14.9.3) appears in the response, and the response does not include other restrictions on caching, the cache MAY compute a freshness lifetime using a heuristic. The cache MUST attach Warning 113 to any response whose age is more than 24 hours if such warning has not already been added.

Also, if the response does have a Last-Modified time, the heuristic expiration value SHOULD be no more than some fraction of the interval since that time. A typical setting of this fraction might be 10%.

The calculation to determine if a response has expired is quite simple:

  response_is_fresh = (freshness_lifetime > current_age)

巩固练习题

怎么让浏览器不缓存静态资源

  • 浏览器禁用缓存(打开控制台,勾选no-cache),用浏览器隐私模式打开页面
  • 设置请求头: Cache-Control: no-cache, no-store, must-revalidate
  • 给请求的资源带上版本号
<link rel="stylesheet" type="text/css" href="../css/style.css?version=1.8.9"/>  
  • 部分浏览器支持在HTML中禁用缓存
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate"/>  

相同作用的请求头

  • Cache-Control: no-cacheCache-Control: max-age=0
    max-age=0表示该资源已在0秒后过期,需要使用协商策略。因此,和Cache-Control: no-cache一样。

参考


坚持原创,输出有价值的文章!

同学,如果文章有帮助到你,请通过以下方式给笔者反馈:

最近笔者在整理第一本电子书书稿《前端面试手册》,有兴趣的同学可以点个✨star✨关注下