缓存控制
通常浏览器缓存策略分为两种:强缓存和协商缓存,并且缓存策略都是通过设置 HTTP Header 来实现的
# 强缓存
强缓存可以通过设置两种 HTTP Header 实现:
Expires
和Cache-Control
- 强缓存表示在缓存期间不需要请求,
status: 200
# Expires
该字段的值是一个绝对时间
Expires: Wed, 22 Oct 2018 08:41:00 GMT
Expires 是 HTTP/1.0 的产物,表示资源会在 Wed, 22 Oct 2018 08:41:00 GMT
后过期,需要再次请求。并且 Expires 受限于本地时间,如果修改了本地时间,可能会造成缓存失效。
# Cache-control
该字段的值是一个相对时间,而且在请求头与响应头中都可设置
Cache-control: max-age=30
- Cache-Control 出现于 HTTP/1.1,优先级高于 Expires 。该属性值表示资源会在 30 秒后过期,需要再次请求。
- Cache-Control 可以在请求头或者响应头中设置,并且可以组合使用多种指令
在 HTTP/1.1 中,Cache-Control 是最重要的规则,主要用于控制网页缓存,主要取值为:
# 可缓存性相关
- public:所有内容都将被缓存(客户端和代理服务器都可缓存)
- private:所有内容只有客户端可以缓存,Cache-Control 的默认取值
- no-cache:表示客户端可以缓存资源,但每次使用缓存资源前都必须重新验证其有效性
- no-store:所有内容都不会被缓存,即不使用强制缓存,也不使用协商缓存
# 到期时间相关
- max-age=xxx (xxx is numeric):缓存内容将在 xxx 秒后失效
- s-maxage=xxx: 代理缓存的有效时间
- max-stale=xxx: 表明客户端愿意接收一个已经过期的资源,只要代理缓存过期时间不超过 xxx 秒
- min-fresh=xxx: 表明客户端可以接受在代理缓存在到期前 xxx 秒的缓存
# 重新验证和重新加载相关
- must-revalidate:一旦资源过期(比如已经超过 max-age),在成功向原始服务器验证之前,缓存不能用该资源响应后续请求。
- proxy-revalidate:与 must-revalidate 作用相同,但它仅适用于共享缓存(例如代理),并被私有缓存忽略。
# 其他
- only-if-cached:表明客户端只接受已缓存的响应,并且不要向原始服务器检查是否有更新的拷贝。
Cache-Control 的值是可以混合使用的,比如:
Cache-Control: private, max-age=0, no-cache
当混合使用的时候它们的优先级如下图所示:
# 协商缓存
如果强缓存不可用,就需要通过协商缓存,向服务器发起请求验证资源是否有更新。协商缓存可以通过设置两种 HTTP Header 实现:Last-Modified 和 ETag 当浏览器发起请求验证资源时,如果资源没有做改变,那么服务端就会返回 304 状态码,且不会返回数据体,并更新浏览器缓存有效期;否则,返回 200,并返回文件内容,根据缓存信息设置缓存。
# Last-Modified 和 If-Modified-Since
Last-Modified 表示资源的最后修改日期,If-Modified-Since 会将 Last-Modified 的值发送给服务器,询问服务器在该日期后资源是否有更新,有更新的话就会将新的资源发送回来,否则返回 304 状态码。
但是 Last-Modified 存在一些弊端:
- 如果本地打开缓存文件,即使没有对文件进行修改,但还是会造成 Last-Modified 被修改,服务端不能命中缓存导致发送相同的资源
- 因为 Last-Modified 只能以秒计时,如果在不可感知的时间内修改完成文件,那么服务端会认为资源还是命中了,不会返回正确的资源因为以上这些弊端,所以在 HTTP / 1.1 出现了 ETag
# ETag 与 If-None-Match
ETag 类似于文件指纹,If-None-Match 会将当前 ETag 发送给服务器,询问该资源 ETag 是否变动,有变动的话就将新的资源发送回来。并且 ETag 优先级比 Last-Modified 高。
# 如果什么缓存策略都没设置,那么浏览器会怎么处理?
对于这种情况,浏览器会采用一个启发式的算法,通常会取响应头中的 Date 减去 Last-Modified 值的 10% 作为缓存时间。