上一篇文章 梳理 HTTP 缓存的关键路径。浏览器发起 HTTP 请求,请求报文发往浏览器内置的缓存器,缓存器经过首次验证,再决定是否向服务器发起缓存的再次验证。这条路径的完成,依赖于 HTTP 协议。

1. 首次验证相关协议

服务器在响应报文中设置 Cache-Control 或 Expire,缓存器对资源进行缓存。
再次请求同一资源时,缓存器通过检查 Cache-Control:max-age 和 Expire,决定缓存是否过期的过程称为首次验证。

将 Cache-Control 的取值拆解为三部分,如下图

三部分都是可选的。

第一部分决定是否有缓存,或者在哪里缓存,这条指令有4个取值:

  • public:可以缓存,且即可本地缓存,也可以共享缓存(例如CDN)

  • private:可以缓存,但只允许本地缓存

  • no-cache:告诉客户端,不能直接使用缓存。需要经过服务器再次验证后,才决定是否使用缓存(服务器返回 304 才可用)

  • no-store:不允许缓存

第二部分决定缓存的有效时间,以秒为单位,有两种取值

  • max-age,例如 max-age=3000,即告诉客户端和共享缓存可以缓存3000秒

  • s-maxage,例如 s-maxage=1000,即告诉共享缓存可以缓存1000秒

第三部分控制客户端向服务器发起再次验证,它有三个取值

  • must-revalidate 告诉客户端必须向服务器发起再次验证,即使本地缓存还没过期(Cache-Control: max-age 或 Expire 还在有效期内)

  • proxy-revalidate 告诉共享缓存必须向源服务器发起再验证,即使共享缓存还未过期

  • immutable 指明文档是不可更改的

2. 以上都提到共享缓存,它是什么?

共享缓存相对于本地缓存,像浏览器内置缓存这种只为当前用户提供缓存服务的称为本地缓存,而像CDN这种为一个区域的用户提供缓存服务的称为共享缓存。所以 HTTP 缓存的关键路径,在上 一篇文章 的甚础上,加一个共享缓存。它不是必要的。

假设一个网站服务于全局的用户 U,在服务器下流有N个共享缓存,服务于N个区域的用户。补充进来之后的路径如下:

3. 举些例子

服务器在响应请求的时候,设置 Cache-Control ,不同取值含义分别是

  1. Cache-Control: public max-age=3600 表示本地缓存和共享缓存的有效时间为3600秒。此处如果没有 public 效果一样,即在没有指明是 private 的情况下,都是 public

  2. Cache-Control: no-cache 表示可以缓存,但使用缓存前,必须通过 ETag 或 Last-Modified 向服务器发起再验证,如果服务器响应 304 则可用,否则不可。

  3. Cache-Control: no-store 表示不可缓存资源。

  4. Cache-Control: max-age=3600 s-maxage=7200 表示本地缓存有效期为 3600 秒,而共享缓存是 7200 秒。

4. 浏览器通过 Cache-Control 影响缓存行为

不仅服务器可以通过 Cache-Control 影响缓存器,浏览器也可以。它的取值有no-cache、no-store、max-age max-stale 等,具体的含义不在此处细说,可查《HTTP权威指南》。

因此,Cache-Control 有双向控制缓存的能力。服务器通过响应报文设置 Cache-Control
影响怎么设置缓存;而浏览器通过请求报文设置 Cache-Control 影响怎么使用缓存。Expire 只有在响应报文中有效,其作用与
Cache-Control:max-age 一样,不同的是 max-age 是相对值,Expire 是绝对值。max-age
告诉缓存可以缓存多久(例如 3600 秒), Expire 告诉缓存可以缓存到什么时候(例如 Sat, 29 Jul 2017
03:22:20 GMT )

5. 服务器再验证

如果首次验证发现缓存已经超过有效期(Cache-Control:max-age 和 Expire 已经过期),此时缓存有可能依然存在,但不能直接使用。需要向服务器发请验证,由服务器决定是否可用,这个过程称为服务器再验证。

举例说明:

首次请求文件,服务器在响应报文中设置如下缓存信息,缓存器收到报文,根据 Cache-Control 和 Expire 缓存文件,记录有效期为1天。同时记录下该文件的 ETag 和 Last-Modified。

Cache-Control:max-age=86400
ETag:"597839c9-16c3"
Expires:Sat, 29 Jul 2017 03:22:20 GMT
Last-Modified:Wed, 26 Jul 2017 06:42:17 GMT

一天后,再次请求该文件,此时如果用户并非清除缓存,并且缓存还未被缓存器清除。缓存器将会检测到该请求存在缓存,但此时缓存已经超过有效期,于是在请求报文里设置 If-None-Match 和 If-Modified-Since,取值分别为该文件的 ETag 和 Last-Modified。

服务器收到报文,检测文件的上次修改时间是否与报文中的 If-Modified-Since 一样,同时检测资源的 ETag 是否与报文中的 If-None-Match 一样。如果都一样,认为缓存器的缓存仍然可以继续使用,响应 304 的状态码,缓存器收到响应后,将本地的缓存文件响应给浏览器。

请求报文如下

If-None-Match:"597839c9-16c3"
if-Modified-Since: Wed, 26 Jul 2017 06:42:17 GMT

流程图如下

相关文章
HTTP 缓存的关键路径

如果觉得我的文章对你有用,请随意赞赏

你可能感兴趣的文章

载入中...
啃先生 啃先生

1.2k 声望

发布于专栏

啃先生

前腾讯前端开发工程师,后来有一年时间经历参与创业,目前在券商。坚持原创,分享前端开发经验,创业故事,互联网行业思考。 @深圳

72 人关注

系列文章