HTTP缓存 - pingdongyi/blog-2 GitHub Wiki

缓存的概念

缓存有客户端浏览器缓存(HTTP缓存)、服务器端缓存(memcache缓存)、代理服务器的缓存(webserver缓存)和页面缓存(smarty或varnish缓存)等。

HTTP缓存是利用HTTP协议中的请求头及响应头进行设置,把web服务器的响应有条件的缓存的客户端浏览器中,以便在满足条件的情况下直接从本地缓存中返回请求内容,而不用再对服务器发起请求并返回数据。

缓存的好处

  1. 减少了请求及数据的传输所用的带宽
  2. 减少了服务器的负担
  3. 加快了客户端浏览器加载页面的速度,大大提高了网站的性能

HTTP缓存的应用

通过服务器提供正确适当的HTTP头指令来指引浏览器什么时候进行缓存及缓存多长时间。

使用ETag验证缓存

HTTP缓存是通过ETag来判断是否使用缓存的,即ETag是浏览器缓存的条件或依据。

服务器通过ETag头传递验证令牌,并通过验证令牌进行资源更新检查,如果资源未更新,则不会传输任何数据。

假设在首次获取资源 120 秒之后,浏览器又对该资源发起了新请求。首先,浏览器会检查本地缓存并找到之前的响应,不幸的是,这个响应现在已经’过期’,无法在使用。此时,浏览器也可以直接发出新请求,获取新的完整响应,但是这样做效率较低,因为如果资源未被更改过,我们就没有理由再去下载与缓存中已有的完全相同的字节。

这就是 ETag 头中指定的验证令牌所要解决的问题:服务器会生成并返回一个随机令牌,通常是文件内容的哈希值或者某个其他指纹码。客户端不必了解指纹码是如何生成的,只需要在下一个请求中将其发送给服务器,如果指纹码仍然一致,说明资源未被修改,我们就可以跳过下载。并返回304 Not Modified响应,告诉浏览器缓存的资源没有更新,可以继续使用,这样也节约了时间和带宽。

我们唯一要做的就是确保服务器提供必要的 ETag 令牌:查看服务器文档中是否有必要的配置标志流行服务器配置样例

# CSS and Javascript
location ~* \.(?:css|js)$ {
  expires 1y;
  access_log off;
  add_header Cache-Control "public";
}

使用ETag的好处:

  1. 某些服务器不能精确得到文件的最后修改时间, 这样就无法通过最后修改时间来判断文件是否更新了。

  2. 某些文件的修改非常频繁,在秒以下的时间内进行修改. Last-Modified只能精确到秒。

  3. 一些文件的最后修改时间改变了,但是内容并未改变。 我们不希望客户端认为这个文件修改了。

使用Cache-Control来控制缓存

每个资源都可以通过 Cache-Control HTTP 头来定义自己的缓存策略

Cache-Control指令控制谁在什么条件下可以缓存响应以及可以缓存多久

最好的请求是不必与服务器进行通信的请求:通过响应的本地副本,我们可以避免所有的网络延迟以及数据传输的数据成本。为此,HTTP 规范允许服务器返回 不同的Cache-Control指令,控制浏览器或者其他中继缓存如何缓存某个响应以及缓存多长时间。

在HTTP/1.1规范中用Cache-Control指令取代了之前的响应策略头,如Expires,现在所有的浏览器都已支持Cache-Control头。如果Cache-Control和Expires头都存在,则优先使用Cache-Control头。

Cache-Control指令头

请求报文

  • no-cache

    在重新向服务器验证之前,不要返回文档的缓存副本,即需要请求服务器进行验证,但不一定需要返回文档内容

  • no-store

    不要返回文档的缓存副本。不要保存服务器的响应,即不使用缓存内容

  • max-age

    缓存中的文档不能超过指定的使用期

  • max-stale

    文档允许过期,但不能超过指令中指定的过期值

  • min-fresh

    文档的使用期不能小于这个指定的时间与它的当前时间之和。换句话说,响应必须至少在指定的这段时间之内保持新鲜,即可以过期,但可以在过期后指定的一段时间内

  • no-transform

    文档在发送之前不允许被转换

  • only-if-cached

    只有当文档在缓存中才发送,不要联系原始服务器

响应报文

  • public

    响应可以被任何服务器缓存,大多数情况下,public不是必须的,因为明确的缓存信息(例如max-age)已表示响应可以被缓存

  • private

    响应可以被缓存,但只能被单个端访问,只为单个用户缓存,因此,不允许任何中继缓存对其时进行缓存。例如用户浏览器可以缓存包含用户私人信息的HTML网页,但是CDN不能缓存

  • no-cache

    表示必须先与服务器确认返回的响应是否被更改,然后才能使用该响应来满足后续对同一个网址的请求。因此,如果存在合适的验证令牌 (ETag),no-cache 会发起往返通信来验证缓存的响应,如果资源未被更改,可以避免下载

  • no-store

    响应不允许被缓存,直接禁止浏览器和所有中继缓存存储返回的任何版本的响应。例如一个包含个人隐私数据或银行数据的响应,每次用户请求该资源时都会向服务器发送一个请求,每次都会下载完整的响应

  • must-revalidate

    响应在提供给客户端之前必须重新向服务器验证

  • proxy-revalidate

    共享的缓存在提供给客户端之前必须重新向原始服务器验证。私有的缓存可以忽略这条指令

  • max-age

    指定文档可以被缓存的时间以及新鲜度的最长时间

  • s-max-age

    指定文档作为共享缓存时的最长使用时间。私有的缓存可以忽略本指令

最优Cache-Control 策略

定义缓存策略的技巧与方法

不存在最佳的缓存策略,根据您的通信模式、提供的数据类型以及应用特定的数据更新要求来定义,必须定义和配置每个资源最适合的设置以及整体的’缓存层级’。

要注意以下几点:

  1. 使用一致的网址

    不同网址会缓存重复内容,网址区分大小写

  2. 确保服务器提供验证令牌 (ETag)

    通过验证令牌,如果服务器上的资源未被更改,就不必传输相同的字节

  3. 确定中继缓存可以缓存哪些资源

    对所有用户的响应完全相同的资源很适合由 CDN 或其他中继缓存进行缓存

  4. 确定每个资源的最优缓存周期

    不同的资源可能有不同的更新要求。审查并确定每个资源适合的 max-age

  5. 确定网站的最佳缓存层级

    对 HTML 文档组合使用包含内容指纹码的资源网址以及短时间或 no-cache 的生命周期,可以控制客户端获取更新的速度

  6. 搅动最小化

    有些资源的更新比其他资源频繁。如果资源的特定部分(例如 JavaScript 函数或一组 CSS 样式)会经常更新,应考虑将其代码作为单独的文件提供。这样,每次获取更新时,剩余内容(例如不会频繁更新的库代码)可以从缓存中获取,确保下载的内容量最少

注意

  • CTRL+F5强制刷新浏览器,使浏览器不使用缓存
  • 按F5刷新浏览器, 浏览器会去Web服务器验证缓存
  • 在地址栏输入网址访问,浏览器会"直接使用有效的缓存", 而不去服务器验证缓存,即缓存命中
  • Cache-Control: public 指可以公有缓存, 可以是数千名用户共享的
  • Cache-Control: private 指只支持私有缓存, 私有缓存是单个用户专用的
⚠️ **GitHub.com Fallback** ⚠️