Http 缓存:论某一资源被缓存和使用缓存的条件
某一请求使用缓存的基本步骤有两个,一个是先将响应的资源缓存下来,另一个是在发起请求时满足使用缓存的条件。下面先来看看什么样的响应会被缓存下来。
什么样的响应会被缓存
这里引用 RFC7234 原文做简要说明(原文:page6,第三章节)
A cache MUST NOT store a response to any request, unless:
- The request method is understood by the cache and defined as being
cacheable, and - the response status code is understood by the cache, and
- the "no-store" cache directive (see Section 5.2) does not appear
in request or response header fields, and - the "private" response directive (see Section 5.2.2.6) does not
appear in the response, if the cache is shared, and - the Authorization header field (see Section 4.2 of [RFC7235]) does
not appear in the request, if the cache is shared, unless the
response explicitly allows it (see Section 3.2), and - the response either:
- contains an Expires header field (see Section 5.3), or
- contains a max-age response directive (see Section 5.2.2.8), or
- contains a s-maxage response directive (see Section 5.2.2.9)
and the cache is shared, or - contains a Cache Control Extension (see Section 5.2.3) that
allows it to be cached, or - has a status code that is defined as cacheable by default (see
Section 4.2.2), or - contains a public response directive (see Section 5.2.2.5).
大概解释一下:
一个响应满足以下条件,即可被缓存:
- 请求的方法必须能被缓存理解,而且必须是可以被缓存的方法。例如 GET、HEAD 方法可以被缓存,POST 和 PATCH 如果设置了合适的 Header(Content-Location)也可以被缓存,但 PUT 和 DELETE 方法不可被缓存。
- 响应码可以被缓存理解,以下这些响应码是可以被缓存的:200、203、204、206、300、301、404、405、410、414、501。
- 请求头和响应头中都没有指定 "no-store" 头部
- 对于共享缓存来说(代理服务器),在响应头中没有指定为 "private"
- 对于共享缓存来说(代理服务器),请求中没有 "Authorization" 头部
- 响应头中含有 "Expires"、"max-age"、"s-maxage"、"public",或通过 "Cache Control Extension" 明确指明要缓存,或返回的响应码指明要缓存时
如何命中缓存
同样引用 RFC7234 原文做简要说明(原文:page8,第四章节)
When presented with a request, a cache MUST NOT reuse a stored
response, unless:
- The presented effective request URI (Section 5.5 of [RFC7230]) and
that of the stored response match, and - the request method associated with the stored response allows it
to be used for the presented request, and - selecting header fields nominated by the stored response (if any)
match those presented (see Section 4.1), and - the presented request does not contain the no-cache pragma
(Section 5.4), nor the no-cache cache directive (Section 5.2.1),
unless the stored response is successfully validated
(Section 4.3), and - the stored response does not contain the no-cache cache directive
(Section 5.2.2.2), unless it is successfully validated
(Section 4.3), and - the stored response is either:
- fresh (see Section 4.2), or
- allowed to be served stale (see Section 4.2.4), or
- successfully validated (see Section 4.3).
大概解释一下:
发起请求时,满足一下条件即可使用缓存:
- URI 匹配,如果一个 URI 有多个缓存,则使用时间最近的
- 缓存的响应允许我们当前请求的 METHOD 使用缓存
- 头部匹配,指缓存的响应中,Vary 指定的头部必须与本次请求的头部匹配
- 提交的请求不包含 "no-cache" 头部(Pragma 和 Cache-Control 都不可包含)
- 缓存的响应不包含 "no-cache" 头部(Pragma 和 Cache-Control 都不可包含)
- 缓存未过期,或允许使用过期缓存(max-stale),或针对过期缓存已经到源服务器成功验证(源服务器响应 304)
Varying Response
这里对命中缓存时提到的头部匹配做一个介绍。Vary
是 Http 响应头的一个 Header,他决定了一个请求在命中缓存时的 Header 匹配规则。当缓存的响应中指定了 Vary 时,新的请求必须满足 Vary 指定的所有 Header 规则才可使用缓存。例如:
上图中的这个 js 资源 Response 中的 Vary 指明,想要使用我这个缓存,必须验证 Accept-Encoding
头的值,再看 Request Header 中,请求的 Accept-Encoding
头的值为 gzip, deflate
。所以,新的请求想要命中这个缓存,也必须带这个头且值能够匹配才行。
再来看一个来自 Mozilla 官网的例子。
Client1 向代理服务器 Cache 发起/doc 的请求,Cache 发现没有缓存,向源服务器 Server 请求,Server 将结果响应给 Cache,并带有一个 Vary 头部,指定要校验 Content-Encoding
头部,值为 gzip 的请求才能使用这份共享缓存。然后再响应给 Client1。
Client2 向代理服务器 Cache 发起/doc 请求,并带有 Accept-Encoding
头,值为 br。Cache 服务器跟本地缓存校对,发现 Vary 校验不通过,因此不能使用共享缓存,再次向源服务器发起请求,此时源服务器会返回响应,并将请求的 br 加到限制条件中。再返回给 Client2。
Client3 向代理服务器 Cache 发起/doc 请求,并带有 Accept-Encoding
头,值为 br。Cache 服务器跟本地缓存校对,发现 Vary 中指定的值包含 br,校验通过,直接返回共享缓存,不再请求源服务器。
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于