# 浏览器缓存

我们都知道,缓存的存在是为了节约通信成本、减少服务器负担、提高网页的响应速度,但也导致了资源文件过时的问题,所以这方面也有相应的策略,保证缓存的作用尽可能地得到发挥。

浏览器缓存分为强缓存与协商缓存。简单而言,强缓存是没有过期的缓存,而协商缓存是过期了、但经过鉴定还能够继续使用的缓存。


# 强缓存

HTTP 响应报文的头中可以使用 ExpiresCache-Control 两个字段来标识资源的缓存时间:

  • Expires 属于 HTTP/1.0 的规范,使用绝对时间的 GMT 格式字符串表示缓存失效时间。
  • Cache-Control 则来自 HTTP/1.1,使用 max-age 值或者特殊值来设置缓存策略。

Expires 最大的缺点就是因为其是一个绝对时间,当服务器与客户端的时间偏差较大时,会导致缓存变得混乱。


Cache-Control 则采用相对时间,解决了这个问题。例如:

Cache-Control: max-age=3600

代表着资源的有效期是3600秒,接收到资源后的3600秒内无需重新访问服务器获取该资源,直接复用该缓存即可。

除了设置 max-age,还有以下几个常用的设置值:

  • no-cache:不使用强缓存,但可以进行缓存协商。
  • no-store:禁止游览器缓存数据,彻底不使用缓存。
  • public:可以被所有的用户缓存,包括终端用户和 CDN 等中间代理服务器。
  • private:只能被终端用户的浏览器缓存,不允许 CDN 等中继缓存服务器对其缓存。

为了兼容性,Cache-ControlExpires 可以在服务端配置同时启用,但同时启用的时候 Cache-Control 的优先级更高。


# 协商缓存

所谓协商,就是浏览器对于是否要使用该资源的缓存,都会先和服务器先进行沟通。

在 HTTP/1.0 中,第一次响应返回的头中会带有 Last-Modified 字段,用来表示资源的最后一次修改日期。

如在客户端的缓存过期后,客户端向服务端请求该文件时就会带上 If-Modified-Since 字段,其值为之前所收到的 Last-Modified 值。

如果服务端将其与文件进行对比后发现没有修改,则只返回 304 响应码而不携带资源,说明该资源仍然能够继续使用,从而达到节约带宽的效果。

但因为以下的原因,在 HTTP/1.1 中,这两个字段又有了新的版本:

  • 有些文件会进行周期性的更改,但是他的内容并不改变,仅仅是改变了文件的最后修改时间
  • 如果文件的修改频率很高,1秒钟内修改了多次,那么原本的表示法无法精确到1秒以内
  • 某些服务器不能精确的得到文件的最后修改时间

于是对应地,出现了 ETagIf-None-Match 字段。


ETag 是文件的唯一校验码,如果文件有进行修改,那么 ETag 就会发生变动。如果客户端发来的 If-None-Match 值与服务端文件的 ETag 比对不一致,则说明缓存已过期,需要重新发送文件。

HTTP 规范中并没有规定 ETag 应该用什么算法来生成,比较理想的是用哈希,但是因为这样非常消耗服务器资源,所以许多服务器一般是使用 Last-Modified + Content-Length 的十六进制表示连在一起来表示 ETag


某次请求得到的示例:

部分请求头的字段:

:authority: ceynri.cn
:method: GET
cache-control: max-age=0
if-modified-since: Mon, 13 Apr 2020 06:23:12 GMT
if-none-match: W/"5e940550-3a60"

部分响应头的字段:

date: Tue, 14 Apr 2020 15:08:25 GMT
etag: W/"5e940550-3a60"
last-modified: Mon, 13 Apr 2020 06:23:12 GMT
status: 304

用户使用 F5 刷新页面时,会无视强缓存进行协商缓存的流程;如果使用 Ctrl + F5 强制刷新页面,则强制重新获取资源。


上次更新: 2020/4/15 16:14:01