HTTP 协议的缓存机制概述

HTTP 协议的缓存机制涉及到多个请求头字段,并且整个缓存机制的细节行为也存在各类状况的差别,譬如说何时访问本地缓存不发送请求,何时发送请求查看资源是否更新,获取 response 什么状况下更新缓存等。之前我对此只知其一;不知其二只是笼统的知道一些概念,譬如 Cache-Control 能够控制缓存的时间和是否须要缓存,可是缓存过时后的行为,有缓存后浏览器是否有 http 请求都不甚了解。因此特意 google 下,此篇是对此的知识梳理。php

协议概述

什么状况下可使用本地缓存?譬如说咱们用 get 方式请求了一个资源 http://mytest.domain.com/static/images/bg.png,那么咱们下次再请求这个图片资源的时候符合哪些条件可使用本地缓存呢?html

  • url 必须是 http://mytest.domain.com/static/images/bg.png,若是是 http://mytest.domain.com/static/images/bg.png?t=12312321 就会发起新的请求,由于 url 不一样。
  • 发送请求的 method 必须可被缓存,譬如 get。
  • 第一次请求 response(即本地缓存)若是有一个 Vary 头,他的值列出的是一系列 http header,第二次请求的请求头中那些在 Vary 值中所列的头,必须和第一次请求相同(具体规则)。
  • 第二次请求不包含请求头 Pragma: no-cache
  • 第二次请求不包含请求头 Cache-Control: no-cache|max-age=0
  • 第一次请求的 response(即本地缓存)不包含 Cache-Control: no-cache
  • 本地缓存没有过时
  • 或者虽然本地缓存已通过期,可是服务器验证缓存和服务器资源一致,容许使用本地缓存的状况(即得到304 response)

注1:上述任何提一个条件均可以被 cache-control extension 覆盖算法

注2:response header中不只仅能够 Cache-Control: no-cache,还能够 Cache-Control: no-cache="Set-Cookie" 详见浏览器

如何计算本地缓存是否过时

浏览器是经过比较缓存剩余有效时间和当前缓存已存在时间来判断的:response_is_fresh = (freshness_lifetime > current_age)freshness_lifetime 取值优先级次序以下列表所示(排在上面的优先级越高):缓存

  1. Cache-Control: s-maxage=xx
  2. Cache-Control: max-age=xx
  3. Expires: xxxxx
  4. 按规则进行计算(推测)

注1;若是有多个重复的上述头,那么是非法的,视做 response(资源)过时服务器

freshness_lifetime 计算(推测)规则:

规范并无给出具体的算法,可是给出了最坏状况(but does impose worst-case constraints on their results),若是 response(资源)有 Last-Modified 头,那么推荐用从当前到lastmodified这个时间段的 10% 做为 freshness_lifetime,而且 response(资源)的 current_age 若是已经超过24小时,必须在这个 response 上加上113 warn-code头。不多有浏览器实现了freshness_lifetime的自助计算(推测),因此仍是仍是鼓励给出上述显式的1 - 3三种状况。网络

current_age 计算规则:

泛泛来讲就是 response(资源)在本地的驻足时间加上网络传输时间,由于网络传输时间的计算有多个条件,规范实在看的我头晕,因此具体计算规则详见规范dom

本地缓存过时,如何经过服务器验证缓存的是否依然有效(即304的状况)

首先,若是第一次的 response(资源)头中显示申明了一些禁止缓存的头(譬如:"no-store" or "no-cache" 等等),就不存在过时不过时的问题,由于这个资源不容许缓存。其次,过时缓存也不必定不可用,若是在断网或者第二次请求带上 max-stale 这个请求头,那么浏览器可使用过时的缓存(masx-stale 表示在缓存过时后多少时间内浏览器依然可使用缓存)。碰到过时缓存,浏览器能够发送一个条件请求(conditional request)。这个请求的 url 依然是第一次请求的 url,只是会带上些当前资源的一些信息,以供服务器验证这个缓存是否依然可用仍是须要更新:google

  1. response(资源)的 Last-Modified 头所带的值会放到条件请求的 If-Modified-Since 头中,或者是 If-Unmodified-Since 又或者 If-Range。
  2. response(资源)的 ETag 头所带的值会放到条件请求的 If-None-Match 头中,或者 If-Match 又或者 If-Range。

服务器会根据不一样的条件请求头来验证资源,具体的行为详见规范,这里不细致展开。针对条件请求,服务器返回会有三种状况:url

  1. 一个带有304 status code 的返回。表示缓存能够被更新和重用。
  2. 一个带有 body 的完整的 response。表示用这个 response 做为请求的返回,而且视条件能够用这个完整的 response 替换浏览器原有的缓存(此资源)。
  3. 若是返回一个5xx的 response,那么浏览器能够选择就显示这个5xx的返回,或者使用本地缓存(尽管多是过时的)- 规范没有规定应该选择哪一种处理,应该是取决于浏览器的行为。

若是是一个304的返回,规范说这个返回能够更新本地缓存,更新策略分三种:

  1. 若是这个304 response 带有资源有效性的强验证头,那么浏览器会寻找本地缓存,寻找那些带有一样强验证头的缓存,而后用这个最新的 response 去更新这些匹配的缓存(同一个资源可能浏览器保存有多份缓存,譬如日期不一样等)。
  2. 若是这个304 response 带有资源有效性的弱验证头,那么浏览器一样会找相匹配的缓存,可是只会更新最新的那条匹配的缓存。
  3. 若是这个304 response 没有带有任何资源有效性验证头,而且浏览器缓存只有一份,而且这份也一样没带有任何资源有效性验证头,那么浏览器就会用这个304 response,更新本地缓存。

协议流程图

假设第一次请求一个资源,返回 header 里面带上以下字段:
Cache-Control: max-age=600
Last-Modified: Wed, 28 Aug 2013 10:36:42 GMT
ETag: "124752e0d85461a16e76fbdef2e84fb9"

抛开细枝末节的东西,那么第二次请求一般大体流程图以下:

当前资源缓存是否过时:response_is_fresh = (freshness_lifetime > current_age)
                                            |
                               -----------------------------------
                              |                                   |        
                              是                                  否
                              |                                   |
                  发送请求,带上请求头                          从本地缓存中获取资源(不发请求)
                  If-Modified-Since: 此资源Last-Modified的值      
                  If-None-Match: 此资源ETag的值
                              |
                  服务器根据 If-Modified-Since 和 If-None-Match
                  两个值判断资源是否更新过
                              |
                  -------------------------
                 |                         |
                 是                        否
                 |                         |
返回一个 status code:200 的 response    返回一个 status code:304 的 response
response body 里面是请求的资源           response body 为空
                 |                         |
浏览器用 response body里面的资源         依然从本地缓存里面获取资源
替换本地缓存中的资源

特殊状况

当你去浏览器验证的时候可能会碰到一些特殊状况,就是缓存有效,可是你刷新浏览器依然发送的条件请求。实际上是由于浏览器在请求头中加入了一些料,譬如: Cache-Control: max-age=0。你刷新的方式能够有不少种,譬如:按F5,按ctrl+F5,在地址栏按回车等等。这些不一样的行为都会影响浏览器发送请求的行为。这里有一些参考《在浏览器地址栏按回车、F五、Ctrl+F5刷新网页的区别

参考资料

rfc7234

相关文章
相关标签/搜索