4.1.1 强制缓存
HTTP的强制缓存对一致性问题的处理策略就如它的名字一样,十分直接:假设在某个时点到来以前,譬如收到响应后的10分钟内,资源的内容和状态一定不会被改变,因此客户端可以无须经过任何请求,在该时点前一直持有和使用该资源的本地缓存副本。
根据约定,强制缓存在浏览器的地址输入、页面链接跳转、新开窗口、前进和后退中均可生效,但在用户主动刷新页面时应当自动失效。HTTP协议中设有以下两类Header实现强制缓存。
1.Expires
Expires是HTTP/1.0协议中开始提供的Header,后面跟随一个截止时间参数。当服务器返回某个资源时带有该Header,意味着服务器承诺资源在截止时间之前不会发生变动,浏览器可直接缓存该数据,不再重新发请求,示例:
HTTP/1.1 200 OK Expires: Wed, 8 Apr 2020 07:28:00 GMT
Expires是HTTP协议最初版本中提供的缓存机制,设计非常直观易懂,但考虑得并不周全,它至少存在以下几个明显问题:
·受限于客户端的本地时间。譬如,在收到响应后,客户端修改了本地时间,将时间前后调整几分钟,就可能会造成缓存提前失效或超期持有。
·无法处理涉及用户身份的私有资源。譬如,某些资源被登录用户缓存在自己的浏览器上是合理的,但如果被代理服务器或者内容分发网络缓存起来,则可能被其他未认证的用户所获取。
·无法描述“不缓存”的语义。譬如,浏览器为了提高性能,往往会自动在当次会话中缓存某些MIME类型的资源,在HTTP/1.0的服务器中就缺乏强制手段不允许浏览器缓存某个资源。以前为了实现这类功能,通常不得不使用脚本,或者手工在资源后面增加时间戳(譬如“xx.js?t=1586359920”、“xx.jpg?t=1586359350”)来保证每次资源都会重新获取。
关于“不缓存”的语义,在HTTP/1.0中其实预留了“Pragma:no-cache”来表达,但Pragma参数在HTTP/1.0中并没有确切描述其具体行为,随后就被HTTP/1.1中出现过的Cache-Control所替代。现在,尽管主流浏览器通常都会支持Pragma,但行为仍然是不确定的,实际并没有什么使用价值。
2.Cache-Control
Cache-Control是HTTP/1.1协议中定义的强制缓存Header,它的语义比Expires丰富了很多,如果Cache-Control和Expires同时存在,并且语义存在冲突(譬如Expires与max-age/s-maxage冲突)的话,规定必须以Cache-Control为准。Cache-Control的使用示例如下:
HTTP/1.1 200 OK Cache-Control: max-age=600
Cache-Control在客户端的请求Header或服务器的响应Header中都可以存在,它定义了一系列参数,且允许自行扩展(即不在标准RFC协议中,由浏览器自行支持的参数),其标准的参数主要有如下几个。
·max-age和s-maxage:max-age后面跟随一个以秒为单位的数字,表明相对于请求时间(在Date Header中会注明请求时间)多少秒以内缓存是有效的,即多少秒以内不需要重新从服务器中获取资源。相对时间避免了Expires中采用的绝对时间可能受客户端时钟影响的问题。s-maxage中的“s”是“share”的缩写,意味“共享缓存”的有效时间,即允许被CDN、代理等持有的缓存有效时间,用于提示CDN这类服务器应在何时让缓存失效。
·public和private:指明是否涉及用户身份的私有资源,如果是public,则可以被代理、CDN等缓存;如果是private,则只能由用户的客户端进行私有缓存。
·no-cache和no-store:no-cache指明该资源不应该被缓存,哪怕是同一个会话中对同一个URL地址的请求,也必须从服务端获取,令强制缓存完全失效,但此时下一节中的协商缓存机制依然是生效的;no-store不强制会话中相同URL资源的重复获取,但禁止浏览器、CDN等以任何形式保存该资源。
·no-transform:禁止以任何形式修改资源。譬如,某些CDN、透明代理支持自动GZip压缩图片或文本,以提升网络性能,而no-transform禁止了这样的行为,它不允许Content-Encoding、Content-Range、Content-Type进行任何形式的修改。
·min-fresh和only-if-cached:这两个参数是仅用于客户端的请求Header。min-fresh后面跟随一个以秒为单位的数字,用于建议服务器能返回一个不少于该时间的缓存资源(即包含max-age且不少于min-fresh的数字)。only-if-cached表示客户端要求不给它发送资源的具体内容,此时客户端仅能使用事先缓存的资源来进行响应,若缓存不能命中,就直接返回503/Service Unavailable错误。
·must-revalidate和proxy-revalidate:must-revalidate表示在资源过期后,一定要从服务器中进行获取,即超过了max-age的时间后,就等同于no-cache的行为,proxy-revalidate用于提示代理、CDN等设备资源过期后的缓存行为,除对象不同外,语义与must-revalidate完全一致。