You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The goal of caching in HTTP/1.1 is to significantly improve performance by reusing a prior response message to satisfy a current request. A stored response is considered "fresh", as defined in Section 4.2, if the response can be reused without "validation" (checking with the origin server to see if the cached response remains valid for this request). A fresh response can therefore reduce both latency and network overhead each time it is reused. When a cached response is not fresh, it might still be reusable if it can be freshened by validation (Section 4.3) or if the origin is unavailable (Section 4.2.4).
启发式仅能用于没有明确新鲜度的响应,并且要求以下情况之一:
这些响应的状态码被定义为默认是可缓存的(见【RFC7231】章节 6.1);
这些响应被明确标记为可缓存的(比如,带有一个 public 响应指定)。
注:有些文章里还会看见 “启发式缓存“ 这个说法,其实就是指这个启发式新鲜度。
验证 / Validation
当请求 URI 的时候,当缓存已经存储了这个 URI 对应的一个或多个响应,但缓存却不能使用它们之中的任何一个来满足这个请求(比如,因为它们都不再新鲜,或者没有一个可以选择的(Vary 头字段),这时缓存在转发这个请求的时候,用条件请求机制来给下一个入展服务器一个机会,来选择一个有效的已储存的响应来使用。在这个过程中会更新已经存储的元数据。或者干脆就用新的响应来替换这个缓存。这个过程称为对已存储的响应进行「验证」或者「重新验证」。也就是常听说的 “协商缓存”。
说到浏览器的缓存,一下子就能想到两个词:「强缓存」和「协商缓存」。
但是这两个词比较有误导性,在 RFC 7234 中并没有出现这两个词,而是提到了「新鲜度」和「验证」。这其实就是常听到的「强缓存」和「协商缓存」。所以在后面的文章中,就尽量不使用「强缓存」和「协商缓存」了。
1. 新鲜度 / Freshness
新鲜的响应,是指没有过保鲜期的响应。同理,肯定还会有过期的响应,就是超过了保鲜期的响应。但是过期的响应并不一定意味着这个响应就是无效的,我们可以在之后对其进行「验证」,来决定是否要重新获取还是利用缓存。这个后面会再说。
如何确定响应的保鲜期呢?
要确定新鲜度,肯定就要有过期时间。过期时间可以通过两种方式来规定。一是设置
Expires
,二是设置Cache-Control: max-age
。如果源服务器希望强制让缓存每次都去源服务器上进行校验,那么只需要给一个过期的过期时间就可以了。表明这个响应是陈旧的,遵从规范的缓存将会在复用一个陈旧的已缓存的响应来满足后续请求之前,正常地验证它。
Expires
Expires
是在 HTTP 1.0 中提出的。描述的是一个绝对时间,由服务器返回。但是受限于本地时间,如果本地时间被修改了,那么缓存可能也就会失效了。Expires: Thu, 01 Dec 1994 16:00:00 GMT
Cache-Control
Cache-Control
在HTTP/1.1
中出现的,max-age
表示的是相对于请求的时间。s-maxage
会覆盖max-age
或者Expires
头,但是仅适用于共享缓存(比如各个代理),私有缓存会忽略它。Cache-Control: max-age=315360000
在 RFC7234 中提到:
当
Cache-Control
被设置为public
时,是可以响应POST
等请求的。还有一个要注意的:
no-cache
。当设置为no-cache
后,并不意味着就不缓存了,而是在发布缓存副本之前,强制要求缓存把请求提交给原始服务器进行验证(也就是俗称的协商缓存)。个人理解就是绕过了「新鲜度」而直接走到了「验证」这一步。只有当设置为
no-store
,才是真正的不走缓存,直接请求资源。其他的属性可以参考 MDN
Pragma
同
Expires
一样是 HTTP 1.0 提出的。为了向后兼容。现在看,就会向后兼容了Cache-Control
当同时有
Pragma
和Cache-Control: max-age
时,会覆盖掉max-age
。过期时间头的优先级
如果是共享缓存,那么
s-maxage
会覆盖max-age
和Expires
如果不是共享缓存,如果出现
max-age
,那么Expries
会被忽略。当同时有
Pragma
和Cache-Control: max-age
时,会覆盖掉max-age
。启发式新鲜度 / Heuristic Freshness
并不是所有的源服务器都会设置过期时间,所以在没有明确的提供一个过期时间时,缓存可以指派一个「启发式过期时间」。原理是利用一种使用其他头字段值来估算出一个看似合理的过期时间的算法。RFC 7234 规范并没有提供具体的算法,但是对它们的计算结果的最差情况实施了约束。
注:有些文章里还会看见 “启发式缓存“ 这个说法,其实就是指这个启发式新鲜度。
验证 / Validation
当请求
URI
的时候,当缓存已经存储了这个URI
对应的一个或多个响应,但缓存却不能使用它们之中的任何一个来满足这个请求(比如,因为它们都不再新鲜,或者没有一个可以选择的(Vary
头字段),这时缓存在转发这个请求的时候,用条件请求机制来给下一个入展服务器一个机会,来选择一个有效的已储存的响应来使用。在这个过程中会更新已经存储的元数据。或者干脆就用新的响应来替换这个缓存。这个过程称为对已存储的响应进行「验证」或者「重新验证」。也就是常听说的 “协商缓存”。如何验证
通过验证器进行验证
在验证阶段,缓存会发送一个或多个前提条件头字段来包含「验证器元数据」。
验证器有两个:
Last-Modified / If-Modified-Since
Last-Modified
响应头里的时间戳就是其中一个验证器。被If-Modified-Since
请求头来验证响应。ETag / If-None-Match
ETag
响应头里的实体标签是另一个验证器。被If-None-Match
请求头来验证。优先级
ETag
的优先级要高于Last-Modified
处理接收到的验证请求(状态码)
整体流程如下:
图来自参考 1 的文章:
疑问
用 node 写一个返回 html 的代码:
返回一个 html:
为什么每次刷新只有 img 走了缓存,html 没有走呢?
如果让 html 走缓存的话,需要另重新打开一个页签,才能命中 html 的缓存:
这是为什么呢?
参考:
缓存(二)——浏览器缓存机制:强缓存、协商缓存
RFC 7234
The text was updated successfully, but these errors were encountered: