浏览器 HTTP 缓存


缓存的只是相应的URL资源,querystring 不同会产生不同的请求。所以可以在资源后面加版本号的查询参数来控制版本。 // hash 不会带到请求中
浏览器第一次访问服务器资源,响应头中设置一个缓存过期时间,一个文件修改时间,一个根据资源内容计算出来的实体标记 Entity Tag,简称 Etag。
浏览器将资源的请求缓存到本地。

再次请求将进行缓存过期判断,若没有过期,控制台 200 OK(from cache),若已过期,则向服务器发送请求,此时请求中会带上文件修改时间,和 Etag。
// max-age 设置缓存存储的最大周期,超过这个时间缓存被认为过期(单位秒)
// max-age 是 CDN 给浏览器用的,而 s-maxage 是源站给 CDN 用的

服务器进行更新判断,根据浏览器传过来的文件修改时间,判断自上一次请求之后,文件是不是没有被修改过;根据 Etag,判断文件内容自上一次请求之后,有没有发生变化,若两者有任意一个没有通过,则服务器会受理此次请求。若没有修改过,服务器返回 304 Not Modified,此时的情况叫协议缓存,浏览器和服务器之间有一次请求交互。
// 响应头里没有 Last-Modified 和 ETag,浏览器没法生成 If-Modified-SinceIf-None-Match 请求头,所以没法发送条件请求,只能发个普通的非条件请求。

cache-control 用来做缓存过期判断,还有 PragmaExpires,这两个为遗留字段,同时存在被 cache-control 覆盖。
  1. no-cache : 表示必须先与服务器确认返回的响应是否被更改,然后才能使用该响应来满足后续对同一个网址的请求。因此,如果存在合适的验证令牌 (ETag),no-cache 会发起往返通信来验证缓存的响应,如果资源未被更改,可以避免下载。
  2. public : 如果响应被标记为public,即使有关联的 HTTP 认证,甚至响应状态码无法正常缓存,响应也可以被缓存。
  3. private : 浏览器可以缓存private响应,但是通常只为单个用户缓存,因此,不允许任何代理服务器对其进行缓存 。比如,用户浏览器可以缓存包含用户私人信息的 HTML 网页,但是 CDN 不能缓存。
  4. no-store : 禁止缓存任何响应,也就是说每次用户请求资源时,都会向服务器发送一个请求,每次都会下载完整的响应。
  5. max-age : 用来设置资源被缓存的最长时间(单位是秒)
    // 缓存文件(max-age) 更新时注意版本乱套, 因为用户不一定会缓存一个版本的所有文件。

    如果 max-age=0 出现在请求(如:开发者工具中禁用缓存)中,则代表浏览器要求服务器,此次请求必须重新返回最新文件;如果 max-age=0 出现在响应中,则代表服务器要求浏览器你在使用本地缓存的时候,必须先和服务器进行一遍通信,将 Etag、 If-Not-Modified 等字段传递给服务器以便验证当前浏览器端使用的文件是否是最新的(如果浏览器用的是最新的文件,http 状态码返回 304,服务器告诉浏览器使用本地缓存即可;否则返回 200,浏览器得自己把文件重新下载一遍)。

if-match / if-none-match 用来做资源更新判断(etag)
if-modified-since 用来做资源更新判断(date)

ETag 用来设置根据资源内容生成的实体标签
Last-Modified 用来设置资源最后修改时间 // Last-Modified ETag是可以一起使用的,服务器会优先验证ETag
  • Last-Modified 标注的最后修改只能精确到秒级
  • 有时内容定期生成,内容并没有任何变化,但 Last-Modified 却改变了;
  • 有可能存在服务器没有准确获取文件修改时间,或者与代理服务器时间不一致等情形
Age 这个字段用来告诉客户端,这个 response 是在多久前被创建的,单位为秒,缓存服务器返回资源的时候必须创建此字段

缓存配置的一些注意事项

  • 只有 get 请求会被缓存,post 请求不会
  • Etag 在资源分布在多台机器上时,对于同一个资源,不同服务器生成的 Etag 可能不相同,此时就会导致304协议缓存失效,客户端还是直接从 server 取资源。可以自己修改服务器端 etag 的生成方式,根据资源内容生成同样的etag。
  • 系统上线,更新资源时,可以在资源uri后边附上资源修改时间、svn版本号、文件md5 等信息,这样可以避免用户下载到缓存的旧的文件
  • 观察 Chrome 的表现发现:
    • 通过链接或者地址栏访问,会先判断缓存是否过期,再判断缓资源是否更新;
    • F5 刷新,会跳过缓存过期判断,直接请求服务器,判断资源是否更新(请求头中添加过期字段max-age=0 并附带 If-Modified-Since(来自以前的响应),If-None-Match(来自以前响应的etag值))。
    • Ctrl+F5 请求头中添加 no-cache,跳过缓存过期判断和资源是否更新

总结
浏览器第一次请求:

浏览器再次请求时: