HTTP的缓存控制

384 阅读4分钟

浏览器缓存

浏览器的缓存是怎么控制的呢?

首先一个普通的(这里不考虑代理缓存)“请求 - 应答”流程如下所示:

  • 浏览器发现缓存无数据,于是发送请求,向服务器获取资源
  • 服务器响应请求,返回资源,同时标记资源的有效期
  • 浏览器缓存资源,等待下次重用

服务器和浏览器是如何沟通缓存机制的呢?

Cache-Control

服务器和浏览器可以用Cache-Control字段来协商缓存规则

  • Cache-Control:no-store,不允许缓存,用于某些变化非常频繁的数据,例如抢红包页面。
  • Cache-Control:no-cache,可以缓存,但在使用之前必须要去服务器验证是否过期,是否有最新的版本。
  • Cache-Control:must-revalidate,如果缓存不过期就可以继续使用,但过期了如果还想用就必须去服务器验证。
  • Cache-Control:max-age=30,相当于告诉浏览器,“这个页面只能缓存 30 秒,之后就算是过期,不能用。”(max-age时间的计算起点是响应报文的创建时刻(即 Date 字段,也就是离开服务器的时刻))

另外,点“刷新”按钮的时候,浏览器会在请求头里加一个“Cache-Control: max-age=0”,Ctrl+F5 的“强制刷新”请求头加了“Cache-Control: no-cache”,含义和“max-age=0”基本一样,就看后台的服务器怎么理解,通常两者的效果是相同的。

if-Modified-Since & Last-modified

Last-modified”代表源头服务器认定的资源做出修改的日期及时间,需要第一次的响应报文预先提供“Last-modified”,然后第二次请求时“if-Modified-Since”就可以带上缓存里的原值,验证资源是否是最新的。

服务器只在所请求的资源在给定的日期时间之后对内容进行过修改的情况下才会将资源返回,状态码为 200 。如果请求的资源从那时起未经修改,那么返回一个不带有消息主体的 304 响应,并且在“Last-Modified”首部中会带有上次修改时间。“If-Modified-Since”只可以用在 GETHEAD 请求中。

If-None-Match & ETag

ETag是资源的一个唯一标识,主要是用来解决修改时间无法准确区分文件变化的问题。

比如,一个文件在一秒内修改了多次,但因为修改时间是秒级,所以这一秒内的新版本无法区分。

再比如,一个文件定期更新,但有时会是同样的内容,实际上没有变化,用修改时间就会误以为发生了变化,传送给浏览器就会浪费带宽。使用 ETag 就可以精确地识别资源的变动情况,让浏览器能够更有效地利用缓存。

ETag 还有“强”“弱”之分。

强 ETag 要求资源在字节级别必须完全相符,弱 ETag 在值前有个“W/”标记,只要求资源在语义上没有变化,但内部可能会有部分发生了改变(例如 HTML 里的标签顺序调整,或者多了几个空格)。

服务器第一次请求时提供“ETag”,然后第二次请求时“If-None-Match”就可以带上缓存里的“Etag”的值。如果资源没有变,服务器就回应一个“304 Not Modified”,表示缓存依然有效。

例如:

刷新页面时浏览器就会同时发送缓存控制头“max-age=0”和条件请求头“If-None-Match”,如果缓存有效服务器就会返回 304。

代理缓存

客户端缓存和代理缓存是不一样的,客户端的缓存只是用户自己使用,而代理的缓存可能会为非常多的客户端提供服务。

  • Cache-Control: public ,表明响应可以被任何对象(包括:发送请求的客户端,代理服务器,等等)缓存

  • Cache-Control: private,表明响应只能客户端缓存,不能作为共享缓存(即代理服务器不能缓存它)。

  • Cache-Control: s-maxage,代表资源在代理上能够缓存多长时间。

  • Cache-Control: max-stale =5 ,如果代理上的缓存过期了也可以接受,但不能过期太多,超过 5 秒也会不要。

  • Cache-Control: min-fresh = 5,意思是缓存必须有效,而且必须在 5 秒后依然有效。

  • Cache-Control: only-if-cached,表示只接受代理缓存的数据,不接受源服务器的响应。如果代理上没有缓存或者缓存过期,就应该给客户端返回一个 504(Gateway Timeout)。

  • Cache-Control: no-transform,代理有时候会对缓存下来的数据做一些优化,比如把图片生成 png、webp 等几种格式,方便今后的请求处理,而“no-transform”就会禁止这样做,不允许代理对资源进行转换或转变。

最后

感谢大家阅读,如有问题欢迎纠正!