浏览器缓存策略

从用户行为到缓存机制

  1. 请求资源后,可能是200,也可能是304,也可能是200(from cache)。不用缓存/第一次请求,就是200;做了新鲜度校验就是304(Not Modified,将不会返回body,因此这个响应会很快返回);使用缓存并且缓存依然新鲜就是from cache。
  2. 缓存问题是被用户行为触发的,用户大概有三种行为:输入URL、重新加载、强制重新加载。
  3. 输入URL,浏览器首先会判断这是一个URL还是一些关键字,如果是后者,会触发默认搜索引擎的功能。如果是URL,那么就会先检查缓存,并且根据缓存里的策略,选择下一步怎么办。
  4. 重新加载页面,比如按F5。浏览器会发起新鲜度检查。
  5. 强制重新加载,比如Ctrl + F5。那么浏览器将无视缓存,要求服务器返回全新的资源。(这就是下面说的,可以在请求头里添加Cache-Control: no-cache,拒绝接受服务器/代理提供的缓存,就要问源服务器拿最新的)

HTTP缓存控制

  1. 现在都是HTTP/1.1了,HTTP/1.0时代是通过Pragma和Expires来规范的,为了兼容那些客户端可能也会在响应里带上。通过Pragma告诉客户端要不要做新鲜度检测,Expires描述一个资源的过期时间。
  2. 首先会通过Cache-Control来定义缓存策略。根据定义的不同的值(有些可以组合使用),才有不同的操作。
  3. 需要注意的是,其实这个字段既可以出现在响应头里,也可以出现在请求头里。一般我们会更多的关注响应头里的,那相当于是服务器告诉浏览器缓存这个返回应该怎么被对待。请求头里则是客户端对(代理)服务器的说明。

禁止进行缓存

  1. Cache-Control: no-store
  2. 缓存中不得存储任何关于客户端请求和服务端响应的内容。
  3. 效果:每次由客户端发起的请求都会下载完整的响应内容,不理缓存。

强制确认缓存

  1. Cache-Control: no-cache
  2. 不直接使用缓存,先问原服务器是否应该使用当前的缓存(这就是新鲜度校验)。
  3. 针对这种设置,缓存会先将此请求发到服务器(带有与本地缓存相关的验证字段),服务器端会验证请求中所描述的缓存是否过期( If-None-Match 或 If-Modified-Since),若未过期(返回304),则缓存使用本地缓存副本。

给缓存一个生命期

  1. 最重要的就是Cache-Control: max-age=<senconds>表示资源能够被缓存(保持新鲜)的最大时间(距离上一次请求的时间)。
  2. 在生命期内会直接使用缓存,否则去确认新鲜度。
  3. HTTP/1.0时代通过Expires字段标识资源失效时间,但是服务器和客户端的时间如果是不一致的,就会出问题。现在他对缓存控制的优先级低于Cache-Control

新鲜度检测

  1. 服务器的响应会在头部放一些字段,来告诉客户端怎么来做新鲜度检测。。
  2. 如果有Cache-Control: must-revalidate,那么是必须到原服务器去检查新鲜度的。
    1.Last-Modified,这里返回的是一个时间。他是一个弱校验器,只能精确到秒。当响应里给出这个头部信息的话,浏览器会在新鲜度检查请求里增加If-Modified-Since头部,并将之前收到的Last-Modified的值附上。说他是弱校验是因为只要文件变化了就认为修改了,但其实可能文件的内容是完全一样的(比如我删掉文件,重新上传一个)
  3. ETag,Entity Tag,实体标签。这就是强校验器了,如果响应里给了这个头部,那么新鲜度检查的时候请求头部会带上If-None-Match,值为之前接收到的Etag的值。很明显,强校验器的结果优先于弱校验器。Etag的问题是分布式服务器(比如CDN)计算Etag的算法应该一致。
  4. 注意,同时存在的情况下,Etag的优先级是高于Last-Modefied

私有和共享缓存

  1. 缓存不仅仅可以在浏览器上,所以产生缓存的时候,需要有策略来控制。
  2. 通过对Cache-Control设置private或者public来实现控制
  3. private: 响应是专用于某单个用户的,中间人不能缓存此响应,该响应只能应用于浏览器私有缓存中。
  4. public:表示该响应可以被任何中间人(比如中间代理、CDN等)缓存。共享缓存存储的响应能够被多个用户使用。比如代理缓存、网关缓存、CDN、反向代理缓存和负载均衡器等部署在服务器上的缓存方式。共享缓存是很有用的,每一级的共享缓存,都能减少请求链的长度。
  5. 常见的 HTTP 缓存只能存储 GET 响应,对于其他类型的响应则无能为力。

总结

最后再串起来描述一下:

  1. 浏览器获取资源,根据用户的行为,会决定是否让浏览器缓存来决定策略。
  2. 浏览器缓存会根据上次响应对缓存设置来做决策:
    1. Cache-Control: no-store或者Expires的时间过期(前者优先级大),就跟服务器要全新的资源
    2. Cache-Control: no-cache或者Pragma: no-cache做新鲜度校验。
    3. Cache-Control: max-age=<senconds>或者Expires的时间还没过期,那么就直接使用缓存。
  3. 做新鲜度校验的时候:
    1. 响应本身可以给Cache-Control设置Private或者Public,这会导致浏览器是仅仅把自己本地的缓存当做缓存,还是也认可客户端到源服务器之间的共享缓存也是可以认可的缓存对象。
    2. 如果有Cache-Control: must-revalidate,那么会直接找源服务器确认
    3. 如果之前响应里给了Etag,那么请求里带If-None-Match去检验
    4. 如果之前响应里给了Last-Modified,那么请求里带If-Modified-Since去检验
    5. 同时设置了,则Etag相关判断优先于(覆盖)Last-Modified