6

HTTP 协议的缓存是通过 6 个报文头完成的,通过两层协商使 web 资源能够不那么频繁地在服务器与客户端之间传递,从而使服务器不必多次处理相同的请求,节约了流量,提高浏览速度。
以从客户端到服务器的顺序,第一层协商为 Cache-Control 与 Expires;第二层协商为 Last-Modified 与 Etag 。
本章不涉及与 HTTP 相关性不大的缓存代理(如 CDN)和客户端缓存(如 manifest)。

相关的报文头

Cache-Control

请求/响应报文头,缓存控制字段,也就是用于控制资源生命周期,是 http/1.1 引入的属性。它支持多值,值用逗号分隔。作用方不仅限于客户端,有些指令还将作用在中间的缓存服务器上。

例子:

Cache-Control: private, max-age=0, no-transform 

Cache-Control 的指令梳理

代号:客户端(C)、缓存代理(P)、服务器(S)。
注:中括号表示可选项,尖括号表示参数。

属性名发起方作用方说明
publicSC/P任何一方都可以缓存该资源。
private[=<header>]SC/P只允许客户端缓存,不允许缓存代理缓存。可选指定针对某些头部。
no-cache[=<header>]C/SC/P缓存代理不缓存;客户端缓存该资源,但每次都要询问是否更新,可以等价 max-age=0 。当服务器发起时可选指定针对某些头部。​
no-storeC/SC/P任何一方都不缓存该资源。
max-age=<second>C/SC/P设置缓存存储的最大周期,也就是说在这个秒数内不发起新请求。当客户端发送此指令给缓存代理时,代理能满足要求则直接返回给客户端,不必再次访问服务器。
no-transformC/SP缓存代理不可更改媒体类型,这样做可防止代理执行压缩图片等类似操作。
s-maxage=<seconds>SP缓存代理可缓存的最长时间。
must-revalidateSC/P可缓存但一旦资源过期必须再向源服务器进行确认,如果无法访问服务器则向客户端报 504 Gateway Timeout 。优先级高于 max-stale。
proxy-revalidateSP与 must-revalidate 作用相同,但它仅适用于缓存代理。
max-stale[=<seconds>]CP客户端要求缓存代理该时间内(默认不限时间)的资源无论缓存有没有过期都返回给客户端。
min-fresh=<seconds>CP客户端要求缓存代理返回至少还未过指定时间的缓存资源,可以理解成限定了资源的最小生命期,在这生命期内才算有效。
only-if-cachedCP客户端要求缓存代理只返回有效的缓存,不需要向服务器对有效性进行确认,如果没有缓存则报 504 Gateway Timeout 。

补充说明

在 HTTP/1.1 中 Cache-Control 是优先级最高的缓存相关头部,部分决定是否缓存的指令(public、private、no-store)会起到直接决定的作用,也就是说如果决定不缓存了,那就不会再进行下一步关于是否过期的判断。在 HTTP/1.0 中 Cache-Control 会被忽略,降级为对 Expires 过期时间的判断。

Expires

响应报文头,代表资源过期时间,在过期之前缓存会一直保存,并且不会向服务器发起请求。由服务器返回提供,是 HTTP/1.0 的属性,在 HTTP/1.1 环境并且与 Cache-Control 共存的情况下,优先级要低。
Expires 的功能基本与 Cache-Control 的 max-age 指令相似,但它是指定一个过期时间点,而 Cache-Control 的 max-age 是指定了过期前的秒数。

例子:

Expires: Wed, 04 Jul 2020 08:26:05 GMT

Last-Modified

响应报文头,资源最终修改时间,由服务器告诉客户端。

例子:

Last-Modified: Wed, 23 May 2020 09:59:55 GMT

If-Modified-Since

请求报文头,与 Last-Modified 相对应,浏览器把服务器最后一次给的 Last-Modified 返回。服务器将以此进行对比,判断资源是否需要更新,如果请求的资源都没有过更新,则返回状态码 304 Not Modified 的响应。

例子:

If-Modified-Since: Thu, 15 Apr 2004 00:00:00 GMT

Etag

响应报文头,ETag 是 HTTP/1.1 标准开始引入的,对 Last-Modified 的补充。它是一种可将资源以字符串形式做唯一性标识的方式。服务器会为每份资源分配对应的 ETag 值。当资源更新时,ETag 值也需要更新。

ETag 的强弱之分

  • 强 ETag:不论实体发生多么细微的变化都会改变其值。
  • 弱 ETag:只用于提示资源是否相同。只有资源发生了根本改变,产 生差异时才会改变 ETag 值。这时,会在字段值最开始处附加 W/。

例子:

ETag: "usagi-1234" 
ETag: W/"usagi-1234"

为什么需要 ETag

  • 一些周期性修改的文件,修改时间变了但内容没变,此时不希望重新获取;
  • 一些文件修改非常频繁,比如1秒内修改了多次,Last-Modified 只能精确到秒;
  • 一些服务器不能得到文件修改的精确时间。

额外注意

  • ETag 没有规定生成的算法,每个服务器生成都可能不一样;
  • 分布式系统里多台计算机间文件的 Last-Modified 必须一致,以免负载均衡到不同机器导致对比失败,因此分布式系统要统一 ETag 算法。

If-None-Match

请求报文头,是一种客户端向服务器提条件的方法,它与报文头 If- Match 作用相反。一般客户端把服务器最后一次给的 ETag 值通过 If-None-Match 返回,服务器将以此进行对比,判断资源是否需要更新。

例子:

If-None-Match:58b66ccbe349d0d931df877c00d8101d037243dc

协商流程

以下假定资源已经获取过一次,并且运行在HTTP/1.1环境下,现在进行二次访问。

流程图如下:
HTTP 缓存

说明:

  • 客户端是有可能因为缓存原因不向服务器发起任何请求的,图中 200 From Cache 就是这种情况。
  • 服务器根据回传的 If-Modified-Since 与 Last-Modified 比对,如果不同则说明这个文件修改过,需要更新。但在这种判断精度是秒,如果是一秒内的改动,就需要进一步对比回传的 If-None-Match 与 ETag 的值。
  • 服务器返回 304 Not Modified 的意思就是不需要重新获取新资源,直接使用本地缓存即可。

缓存多久合适

生存时间(TTL)指令告诉浏览器应该缓存某个资源多久,也就是 Cache-Control 或 Expires 的值。
找到给定资源的最佳TTL值并没有完美的科学方法。

指导原则:

  • 纯静态内容,例如图片或带版本的数据,可以在客户端永久缓存;
  • CSS/JS 和个性化资源,缓存时间大约是会话(交互)平均时间的两倍;

其他类型资源取决于新数据对旧数据的容忍极限。

浏览器操作对 HTTP 缓存的影响

用户操作Expires/Cache-ControlLast-Modified/Etag
地址栏回车有效有效
页面链接跳转有效有效
新开窗口有效有效
前进、后退有效有效
F5刷新无效有效
Ctrl+F5刷新无效无效

缓存改进方案

md5/hash 缓存

通过不缓存 html,为静态文件添加 MD5 或者 hash 标识,解决浏览器无法跳过缓存过期时间主动感知文件变化的问题。

CDN缓存(代理缓存)

CDN 是构建在网络之上的内容分发网络,依靠部署在各地的边缘服务器,通过中心平台的负载均衡、内容分发、调度等功能模块,使用户就近获取所需内容,降低网络拥塞,提高用户访问响应速度和命中率。


calimanco
1.4k 声望766 粉丝

老朽对真理的追求从北爱尔兰到契丹无人不知无人不晓。