强缓存和协商缓存

缓存是指代理服务器或客户端磁盘内保存的资源副本。利用缓存可减少对服务器的访问,因此也就节省了通信流量和通信时间。

浏览器缓存(Brower Caching)是浏览器在本地磁盘对用户最近请求过的文档进行存储,当访问者再次访问同一页面时,浏览器就可以直接从本地磁盘加载文档。

浏览器缓存的优点有:

浏览器缓存分为强缓存和协商缓存。

浏览器在第一次请求发生后,再次请求时:

强缓存

强缓存是利用 http 的返回头中的 Expires 或者 Cache-Control 两个字段来控制的,用来表示资源的缓存时间。

Expires

该字段是 http1.0 时的规范,它的值为一个绝对时间的 GMT 格式的时间字符串,比如 Expires: Mon, 18 Oct 2066 23:59:59 GMT。这个时间代表着这个资源的失效时间,在此时间之前,即命中缓存。这种方式有一个明显的缺点,由于失效时间是一个绝对时间,所以当服务器与客户端时间偏差较大时,就会导致缓存混乱。

Cache-Control

Cache-Control 是 http1.1 时出现的 header 信息,主要是利用该字段的 max-age 值来进行判断,它是一个相对时间,例如 Cache-Control: max-age=3600,代表着资源的有效期是 3600 秒。Cache-Control 除了该字段外,还有下面几个比较常用的设置值:

Cache-Control 与 Expires 可以在服务端配置同时启用,同时启用的时候 Cache-Control 优先级高。

协商缓存

协商缓存就是由服务器来确定缓存资源是否可用,所以客户端与服务器端要通过某种标识来进行通信,从而让服务器判断请求资源是否可以缓存访问,这主要涉及到下面两组 header 字段,这两组搭档都是成对出现的,即第一次请求的响应头带上某个字段(Last-Modified 或者 ETag),则后续请求则会带上对应的请求字段(If-Modified-Since 或者 If-None-Match),若响应头没有 Last-Modified 或者 ETag 字段,则请求头也不会有对应的字段。

Last-Modify/If-Modify-Since

浏览器第一次请求一个资源的时候,服务器返回的 header 中会加上 Last-Modified,Last-Modified 是一个时间,标识该资源的最后修改时间,例如 Last-Modified: Thu, 31 Dec 2037 23:59:59 GMT。

当浏览器再次请求该资源时,request 的请求头中会包含 If-Modified-Since,该值为缓存之前返回的 Last-Modified。服务器收到 If-Modified-Since 后,根据资源的最后修改时间判断是否命中缓存。

如果命中缓存,则返回 304,并且不会返回资源内容,并且不会返回 Last-Modified。

ETag/If-None-Match

与 Last-Modified/If-Modified-Since 不同的是,Etag/If-None-Match 返回的是一个校验码。ETag 可以保证每一个资源是唯一的,资源变化都会导致 ETag 变化。服务器根据浏览器发送的 If-None-Match 值来判断是否命中缓存。

与 Last-Modified 不一样的是,当服务器返回 304 Not Modified 的响应时,由于 ETag 重新生成过,response header 中还会把这个 ETag 返回,即使这个 ETag 跟之前的没有变化。

为什么要有 ETag

你可能会觉得使用 Last-Modified 已经足以让浏览器知道本地的缓存副本是否足够新,为什么还需要 ETag 呢?HTTP 1.1 中 Etag 的出现主要是为了解决几个 Last-Modified 比较难解决的问题:

Last-Modified 与 ETag 是可以一起使用的,服务器会优先验证 ETag,一致的情况下,才会继续比对 Last-Modified,最后才决定是否返回 304。

强缓存与协商缓存的区别可以用下表来表示:

用户行为对缓存的影响

刷新后协商缓存仍然是有效的。

实际问题分析

如文章开头所述,代码更新到线上后用户浏览器不能自行更新,我们不能要求客户在系统更新后都进行一次缓存清理的操作。

到底该如何解决呢?

在资源请求的 URL 中增加一个参数,比如:js/main.js?ver=0.7.1。这个参数是一个版本号,每一次部署的时候变更一下,当这个参数变化的时候,强缓存都会失效并重新加载。这样一来,静态资源,部署以后就需要重新加载。这样就比较完美的解决了问题。

补充

补充:缓存策略的关键不是"是否缓存",而是"什么时候让浏览器重新拿一次资源"。

缓存优先级与命中条件

类型关键响应头命中条件命中后表现
强缓存Cache-Control / Expires未过期不发请求,直接用本地
协商缓存Last-Modified / ETag资源未变化发请求,返回 304

实战做法

  • HTML 文件:一般禁用强缓存,走协商缓存,保证发布后立即生效
  • 静态资源(带 hash 文件名):可以放心用一年的强缓存
  • 文件名带 hash 是配合长效缓存最常见的方案

来源整理自:我的有道云笔记