http缓存策略由缓存存储策略,缓存过期策略,缓存对比策略三部分构成,这三部分并不是完全分开设置的,具体设置方法和缓存执行结果如下:
缓存存储策略
缓存存储策略用于判断是否缓存 http响应内容。
请求头/响应头
Cache-Control
- public(可缓存到代理服务器和客户端),
- private(可缓存到客户端),
- max-age(可缓存到客户端,超过指定时间后过期,单位秒),
- s-maxage(可缓存到代理服务器,超过指定时间后过期,单位秒),
- no-cache(可缓存到客户端,缓存后立即过期,每次访问都需从服务器验证资源有效性200或304,相当于max-age=0, must-revalidation),
- no-store(不进行任何缓存)。
如Cache-Control: private,max-age=3600。
缓存过期策略
缓存过期策略用于判断是否直接读取本地缓存(否则向服务器发送请求,但不一定重新下载)。
请求头/响应头
Expires
配置规则为一个绝对时间点(如:Fri, 02 Nov 2018 10:30:57 GMT)。
在缓存存储策略中设置max-age事实上也起到了设置缓存过期策略的作用,当设置cache-control : max-age=3600时,相当于expires : 当前时间+3600秒。
需要注意的是超过过期时间并不一定会从服务器下载新的文件,最终结果还要根据三种策略综合判定。
Cache-Control
- max-stale(缓存资源过期后的指定时间内仍然视为有效,相当于将过期时间设置为 Expires : Expires+max-stale),
- min-fresh(缓存资源过期前的指定时间内即视为无效,相当于将过期时间设置为 Expires : Expires-min-fresh)。
当多项配置同时存在时,客户端会按照最保守的策略执行(即最先过期的规则)。
缓存对比策略
缓存对比策略用于验证缓存资源的有效性(从而判断继续使用缓存资源还是从服务器重新下载)。
响应头
ETag (服务器资源唯一标识符,如:7b7d124715407066726535778e)。
HTTP/1.1 引入了Etag(Entity Tags)主要为了解决 Last-Modified 无法解决的一些问题。如某些文件可能会周期性更新,但内容并不改变(仅改变修改时间),这时候我们并不希望客户端认为这个文件被修改了而重新 下载;某些文件修改非常频繁,例如:在一秒的时间内进行多次修改,If-Modified-Since 能检查到的粒度是 s 级的,无法判断(或者说 UNIX 记录 MTIME 只能精确到秒);某些服务器不能精确的得到文件的最后修改时间等等。
nginx中Etag是根据资源更新时间转换为16进制,结合内容长度生成,如下:
Content-Length: 612
Last-Modified: Tue, 23 Apr 2019 10:18:21 GMT
ETag: "5cbee66d-264"
express 中采用 etag 库生成Etag标签,静态资源一般都只是生成的长度-时间戳,动态接口可以配置hash等。
koa 的 etag 插件底层还是 express 的 etag库。
Last-Modified ,值为请求资源上一次更新时间,如:Fri, 02 Nov 2018 10:30:57 GMT
请求头
If-None-Match 值为上一次收到的(多个)ETag,如果与当前ETag不同,则重新下载资源,否则返回304。
If-Match 值为上一次收到的(多个)ETag,仅在与当前ETag相同时进行请求,一般用于更新资源。
If-Modified-Since ,值为上一次请求的响应头中的Last-modified ,如果与当前Last-modified不同,则重新下载资源,否则返回304。
If-Unmodified-Since,值为上一次请求的响应头中的Last-modified ,仅在与当前ETag相同时返回响应。
使用Etag比对的优先级和精确性均高于使用Last-modified比对,同时存在时前者将覆盖后者。
强制缓存
通常只要客户端存在缓存资源,且Cache-Control配置max-age,min-fresh,max-stale和Expires配置中的最保守策略尚未过期,即触发强制缓存,浏览器直接使用缓存资源,响应状态码为200(form memory cache)或200(from disk cache)。如:
Cache-Control:max-age=1days,min-fresh=1days,max-stale=1days
Expires:当前时间三天后
根据上面的配置可以计算出以下缓存过期策略:
Expires策略 三天后
max-age策略 当前时间 + 1天 = 一天后
max-stale策略 三天后(即Expires配置)+ 1天 = 四天后
min-fresh策略 三天后(即Expires配置)- 1天 = 两天后
综上,最保守策略为一天后,此时如果浏览器中存在缓存资源,则会直接读取。
协商缓存
如果客户端存在缓存资源,缓存存储策略和缓存过期策略综合计算得出的最保守策略已经过期,即触发协商缓存。
- 此时浏览器会优先使用ETag比对:
- 自动设置请求头中的If-None-Match为上一次请求该资源的ETag值,如果相同则响应返回304,浏览器继续使用缓存资源,否则重新从服务器中下载资源。
- 在验证资源有效或重新下载资源之后浏览器会自动更新缓存资源的Cache-Control,Expires,Date(缓存资源更新时间),Age(该缓存资源已经存在时间)。
- 如果没有ETag配置则使用Last-Modified比对,浏览器在请求头中会设置If-Modified-Since为上次请求的Last-Modified值,其余执行原理与Etag比对类似。
- 如果响应头中没有指定过期策略,则浏览器会将该资源响应结果中的Date和Last-Modified的时间差的10%(单位秒)加上当前时间作为Expires时间。
禁止静态资源缓存
对于需要禁用缓存的场景可以用以下几种方法实现:
- 请求头设置Cache-Control:no-cache,no-store,must-revalidation
- 设置静态资源版本号,如:<script src="./js/index.js?version=1.0.1"></script>(这种方法对于静态资源比较灵活可控,但是对代理服务器不起作用)
- 设置meta标签<meta http-equiv="Cache-Control" content="no-cache,no-store,must-revalidation">(对代理服务器不起作用)