headers

在之前的文章浏览器缓存理论中,我们详细解释了浏览器缓存的几种方式,其中的Disk Cache主要使用的便是HTTP协议中的缓存机制。浏览器会根据请求的结果和缓存标识,会判断是直接从缓存中读取数据还是向服务器发请求判断缓存的有效性,前者被称为强缓存,后者被称为协商缓存

强缓存

即不会向服务器发请求获取资源,直接在缓存中读取数据。在控制台中 HTTP status 为200,Size 显示from memory cache 或者 from disk cache;由两个http header 字段来控制:Expires和Cache-Control

Expires

  • 用来指定资源到期的时间,是一个具体的时间点。
  • 是HTTP/1的产物,如果修改了本地时间,会造成缓存失效。有一定的局限性。

Cache-Control

是HTTP/1.1的产物,可以组合多种指令

指令 作用
public 表示可以被客户端和代理服务器缓存
private 表示只可以被客户端缓存
max-age=30 资源30秒之后就过期,需要重新请求
s-max-age=30 覆盖max-age,作用相同,只在代理服务器中生效
no-store 不缓存任何响应
no-cache 资源会被缓存,但是立即失效,下次会发起请求验证资源是否过期
max-scale=30 30秒内,即使缓存过期,也使用该资源(无敌金身期)
min-fresh=30 希望在30秒内获取最新的响应
强缓存判断是否缓存的依据来自于是否超出某个时间或者某个时间段,而不关心服务器端文件是否已经更新

协商缓存

在强缓存失效之后,浏览器会携带缓存标识向服务器发起请求,服务器会根据缓存标识来和浏览器协商资源的缓存情况,这一过程称之为协商缓存。协商成功,返回304和空的响应体,协商失败,返回200和新的资源。

Last-Modified和If-Modified-Since

  • 浏览器在第一次访问资源的时候,服务器会在response header中加入last-Modified=timevalue(该资源的最后修改时间),浏览器接收到响应,会将资源和header一同缓存起来。
  • 浏览器再次请求该资源时,会将Last-Modified的值timevalue放在If-Modified-Since中,即If-Modified-Since=timevalue
  • 服务器接收到请求,会将timevalue与该资源的最后修改时间进行对比,如果没有变化,返回304和空的响应体,如果timevalue小于最后修改时间,说明资源发生了变化,返回200和新的资源。

但是Last-Modified存在一些弊端:

  • 如果本地打开缓存文件,不管有没有修改,都会造成Last-Modified 的值发生变化,服务端不能命中缓存导致发送同样的资源
  • 因为Last-Modified的值是以秒为最小单位,如果在不可感知的时间内修改完成文件,服务端还是会认为资源会命中,不返回正确的资源。

Etag和If-None-Match

Etag是服务器响应请求时,返回当前资源文件的一个唯一标识(由服务器生成),只要资源有变化,Etag就会重新生成

  • 同上面Last_Modified类似,第一次请求的时候,服务器会生成一个唯一标示作为Etag的值(hashvalue)返回给浏览器
  • 浏览器再次请求资源的时候,会把hashvalue(Etag的值)作为请求头里的If-None-Match 的值。服务器只需要比对这两个值是否一致,就可以很好地判断资源有没有被修改过。

二者对比

  • 在精度上,Etag优于Last-Modified
  • 在性能上,Etag逊于Last-Modified
  • 在优先级上,Etag高于Last-Modified

缓存优先级

  • 强缓存优先于协商缓存
  • 如果没有设置任何缓存策略,浏览器会采用启发式算法,通常会取响应头中的Date减去Last-Modified 值的 10% 作为缓存时间。

参考文章

一文读懂前端缓存

深入理解浏览器的缓存机制

浅谈Web缓存