Alkaidcc

Alkaidcc

就算失败也没关系:)
jike
twitter
github

什么是HTTP缓存

什么是 HTTP 缓存#

我们为什么需要缓存(cache)?

我们知道,我们每次访问一个网页都需要加载资源,如果这些资源都从远端的服务器上加载,整个请求和响应时间会很长,有没有快一点的办法呢?有,可以从附近的 CDN 请求资源,减少了长距离的网络传输,响应速度自然就提升了。可是,CDN 还是离客户端有点远,有没有办法在浏览器缓存数据,尽量少的向外请求资源呢?其实这些思想都是缓存的思想,在每个节点都做缓存就可以极大的减少远端资源请求,加快响应速度。

web-cache

缓存中有几个概念

web-cache

  • Stale Content:陈旧的内容,即被缓存且过期的内容
  • Fresh Content:新鲜的内容,即被缓存且未过期的内容
  • Cache Validation:是联系服务器以检查缓存内容的有效性,并在其即将过期时得到更新的过程。
  • Cache Invalidation:是删除缓存中可用的任何陈旧内容的过程。

缓存按照位置可以分为浏览器缓存代理缓存反向代理缓存

浏览器缓存#

浏览器缓存仅限于一个用户,并且与其他缓存不相同,并且它可以存储私有的响应。

代理缓存#

与服务于单个用户的浏览器缓存不同,代理缓存可以服务于访问相同内容的数百个不同用户。往往是由 ISP 实施的。

isp-cache

反向代理缓存#

反向代理缓存一般部署在服务端,用于减轻服务器的负载。

reverse-proxy

缓存头#

每当服务器发出一些响应时,它都会伴随一些 HTTP 标头,以指导缓存与否以及如何缓存此响应

Expires#

在 HTTP/1.1 和引入缓存控制之前,有一个 Expires 标头,它只是一个时间戳,告诉缓存应该将某些内容视为新鲜的时间。比如

Expires: Mon, 13 Mar 2017 12:22:00 GMT

当然,缓存上的时钟必须与服务器上的时钟同步,否则可能无法实现所需的结果。

尽管 Expires 还在使用,但我们现在应该优先考虑Cache-Control

Pragma#

这也是来自 HTTP/1.1 的旧特性,我们可能会看到在这里和那里使用Pragma: no-cache,希望阻止响应被缓存。

Cache-Control#

缓存控制(Cache-Control)规定了内容应被缓存多长时间和以何种方式缓存。

cache-control 可以包含多个值,

private

将缓存设置为私有意味着该内容不会在任何代理中被缓存,它只会被客户端(即浏览器)缓存。

public

如果设置为公开,除了被客户端缓存外,还可以被代理机构缓存;为许多其他用户服务。

no-store

指定该内容不被任何一个缓存所缓存。

no-cache

no-cache 表示可以保持缓存,但缓存的内容在被提供之前要从服务器上重新验证(例如,使用 ETag)。也就是说,仍然有一个对服务器的请求,但是是为了验证,而不是为了下载缓存的内容。

max-age: seconds

max-age 规定了内容被缓存的秒数。例如,如果 cache-control 看起来像下面这样。

cache-control: max-age=900, private

s-maxage: seconds

s-maxage 这里 s - 前缀代表共享。这条指令专门针对共享缓存。像 max-age 一样,它也可以得到一些东西被缓存的秒数。如果存在,它将覆盖共享缓存的 max-age 和 expires 头文件。

must-revalidate

must-revalidate 有时可能发生的情况是,如果你有网络问题,而内容无法从服务器检索,浏览器可能会在没有验证的情况下提供陈旧的内容。 must-revalidate 可以避免这种情况。如果这个指令存在,意味着在任何情况下都不能提供陈旧的内容,在提供数据之前必须从服务器上重新验证。

proxy-revalidate

proxy-revalidate 与 must-revalidate 类似,但它对共享或代理缓存的规定是一样的。换句话说,proxy-revalidate 对于 must-revalidate 来说就像 s-maxage 对于 max-age 一样。

Mixing Values

我们可以以不同的方式组合这些指令以实现不同的缓存行为,但是no-cache/no-storepublic/private是互斥的。如果同时指定no-storeno-cache,则no-store将优先于no-cache

Validators#

到目前为止,我们只讨论了内容是如何被缓存的,以及多长时间的缓存内容被认为是新鲜的,但我们没有讨论客户端如何从服务器上进行验证。下面我们讨论用于此目的的头信息。

Etag

Etag 或 "Entity Tag" 是在 HTTP/1.1 规范中引入的。Etag 只是一个独特的标识符,服务器将其与一些资源联系在一起。这个 ETag 后来被客户端用来做有条件的 HTTP 请求,说明 "如果 ETag 与我的 ETag 不一样,就给我这个资源",只有在 ETag 不匹配的情况下才会下载内容

生成 ETag 的方法在 HTTP 文档中没有指定,通常使用一些抗碰撞的哈希函数来给资源的每个版本分配 ETag。可以有两种类型的 etag,即强和弱的 etag。

ETag: "j82j8232ha7sdh0q2882" - Strong Etag
ETag: W/"j82j8232ha7sdh0q2882" - Weak Etag (prefixed with `W/`)

强验证 ETag 意味着两个资源是完全相同的,它们之间根本没有区别。而一个弱的 ETag 意味着两个资源虽然严格意义上不一样,但可以被认为是一样的。例如,弱的 ETag 可能对动态内容很有用。

现在我们知道什么是 Etag 了,但浏览器是如何提出这个请求的呢? 通过向服务器提出请求,同时在If-None-Match头中发送可用的 Etag。

考虑一下这样的情况:你打开了一个网页,其中加载了一个标志图片,缓存期为 60 秒,ETag 为abc123xyz。大约 30 分钟后,你重新加载该网页,浏览器会注意到 60 秒内新鲜的标志现在已经过时了;它将触发一个请求给服务器,在 if-none-match 头中发送过时的标志图像的 ETag

If-None-Match: "abc123xyz"

然后,服务器将比较这个 ETag 和资源的当前版本的 ETag。如果两个 ETag 都匹配,服务器将发回 304 未修改的响应,这将告诉客户,它所拥有的副本仍然是好的,它将被视为新的 60 秒。如果两个标志都不匹配,即标志很可能已经改变,客户端将被发送新的标志,它将用来替换它所拥有的陈旧的标志。

Last-Modified

服务器可能包括Last-Modified标头,表明一些内容最后被修改的日期和时间。

Last-Modified: Wed, 15 Mar 2017 12:30:26 GMT

当内容过期时,客户端将向服务器发出一个条件性请求,其中包括它在名为If-Modified-Since的头中的最后修改日期,以获得更新的 Last-Modified 日期;如果它与客户端的日期相匹配,内容的Last-Modified日期将被更新,以便在另外的 n 秒内被视为新鲜。如果收到的Last-Modified日期与客户拥有的日期不一致,则从服务器重新加载内容,并替换为客户拥有的内容。

If-Modified-Since: Wed, 15 Mar 2017 12:30:26 GMT

你现在可能会问,如果缓存的内容既有Last-Modified,又有ETag,怎么办?那么,在这种情况下,两者都要使用,也就是说,当且仅当 ETag 与新检索的内容相匹配,并且 Last-Modified 日期也相匹配时,将不会有资源重新下载。如果 ETag 不匹配或者 Last-Modified 日期大于服务器上的日期,内容就必须重新下载。

加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。