Cache类型
private cache
意味着该缓存不会与其他用户共享
share cache
- Proxy cache
就是中间代理服务器的缓存,但是因为https的流行,这些代理服务器基本上只能转发请求 - Managed cached
就是源服务器配置的缓存:nginx,cdn,service worker
什么样请求响应时可以被缓存的
- 响应的状态码是1xx, 2xx,3xx, 4xx,5xx范围内
- 没有出现no-store指令
- 响应含有Expire字段或者以下的指令:public,private,max-age,s-maxage,immutable等
缓存响应的key
一般使用请求方法 + 请求的URI作为缓存的key,但是可以使用vary字段,添加响应头作为key的一部分
例如:Vary: Accept-Language
Freshness
只要响应的age不超过它的freshness lifetime 就认为是是“新鲜的”,否则就会认为已经过期
freshness lifetime 是指响应从服务器产生到它的过期时间这段时间范围
explicit expiration time 是指响应头包含Expires字段或者max-age指令
age 是指该响应从服务产生后所过去的时间
浏览器也可以使用max-age或者min-fresh请求指令来建议服务器返对应的响应(例如浏览器强制刷新的时候就会带上max-age=0)
Freshness Lifetime (Freshness生命周期计算)
- 如果cache是共享的,而且响应头也包含s-maxage,则使用这个值
- 如果响应头包含max-age,则使用这个值
- 如果响应头包含Expires,则使用这个字段减去Date字段(如果Date字段不存在,则使用接收响应的时间)
- 否则如果在响应头没有明确的过期时间,则会应用一种启发式的Freshness Lifetime去计算
如果响应头出现多个Expires或者多个Cache-Control: max-age
,则可以使用第一个字段的值或者认为这个响应已经stale(行为未定义,要根据浏览器实际行为参考),如果缓存指令出现冲突,例如(max-age和no-cache同时出现)则只会应用最严格的那个指令;如果max-age是无效的值,则也会认为该响应是stale的
Calculating Heuristic Freshness (启发式计算Freshness)
主要原因因为服务器可能不会提供明确的过期时间(没有Expires和max-age),所以这个时候缓存自己要给文件计算一个过期时间,例如使用其他字段Last-Modified时间辅助计算,但是这里不会给出一个固定的算法,要浏览器自己定义。
但是还是给出一个参考:( Last-Modified - Date)* 10%(可以认为如果隔了这么一段时间才修改,大概下次修改也会在这段时间之内,所以生命周期也在这个时间范围内)
Age计算
age_value 响应头Age字段,如果没有则认为是0
date_value 响应头Date字段
now 当前时间
request_time 请求时间
response_time 响应时间
计算方式主要有两种
- apparent_age , response_time减去date_value,如果浏览器时间是跟服务器时间同步的应该是比较准确(但是不太可能)
apparent_age = max(0, response_time - date_value); - corrected_age_value (只要支持http1.1以上的都使用这个计算方式)
response_delay = response_time - request_time;
corrected_age_value = age_value + response_delay;
然后corrected_age_value 还可以被用作corrected_initial_age,
但是还有一种情况就是返回头没有Age字段的时候(age_value则视为0参与corrected_age_value计算),corrected_initial_age可以下面保守的方式计算,分别统计apparent_age和corrected_age_value,再求最大值:
corrected_initial_age = max(apparent_age, corrected_age_value);
resident_time = now - response_time;
current_age = corrected_initial_age + resident_time;
Serving Stale Responses
如果响应头包含no-cache ,must-revalidate,s- maxage和proxy-revalidate,是禁止缓存返回过期的响应的
除非浏览器无法与服务器建立链接,或者请求时带有max-stale指令才允许返回过期的响应
Validation (验证资源)
如果响应被缓存了(也就是必须被缓存过的响应才会进入验证阶段,这由与响应能不能被缓存相关),而且缓存已经过期,或者无法被选则(具体原因后面补充),需要使用conditional request mechanism (条件请求验证机制)去发送一个请求让服务端返回新的响应
Sending a Validation Request 发送验证请求
缓存会合成一个请求,复制mehod,uri还有vary字段所标记的其他头部字段,还有添加If-Modified-Since,If-Unmodified-Since ,If-Match ,If-None-Match 这些precondition header ;这里f-Match ,If-None-Match比If-Modified-Since,If-Unmodified-Since优先级更高(毕竟验证更加准确)
Handling a Validation Response
- 304状态码,不返回响应内容
- 或者完整的响应
- 也有可能是5xx
Cache-Control
cache-control的指令可以分别在请求/响应中生效,也就是cache-control可以出现在请求头中/响应头中,而他们在请求和响应中所支持的指令也不一定相同,就算指令相同也不一定含义都一样
Response Directives
当返回附带以下指令,cache必须遵守这些指令来处理
max-age
指出新鲜度时间范围,当响应的age超过这个范围就认为是过期的
但是响应的age并不是根据现在的时间减去接收响应的时间来计算的
must-revalidate
指出一旦响应缓存已经过期了,必须在重新validate后才能使用,禁止任何情况下使用过期的响应缓存
因为在无法connnect服务器情况下,有可能使用过期的响应缓存来进行回复请求,所以must-revalidate会禁止这种情况,直接返回一个504错误码
一般情况下需要跟max-age 配合使用
must-understand
指出如果cache理解根据状态码来缓存响应,才应该缓存响应
一般must-understand 会搭配 no-store使用
Cache-Control: must-understand, no-store
如果cache不支持must-understand,那就使用no-store指令
if cache support must-understand, it stores the response with an understanding of cache requirements based on its status code
no-cache
指出响应缓存必须是经过Validation和接收到(successful response )成功的响应才能被使用
这意味着每次使用缓存前都需要发送验证请求到服务器验证
这里注意no-cache并不是代表“don’t cache”,no-cache意味着还是能缓存响应但是使用缓存之前必须经过revalidate。
真正的“don’t cache”是no-store指令
no-store
指出无论private cache 还是 share cache都不应该缓存响应
no-transform
因为一些中间服务器会把内容转换成各种格式,例如图片可以转换成不同格式来减少传输大小,所以这里会禁止这种行为
private
意味着响应只能存在private cache
只要返回资源有涉及到用户的个人信息,都应该使用这个指令,减少个人信息泄漏
proxy-revalidate
同must-validate,但是仅仅对share cache生效
public
指出这里响应应该缓存在share cache里面
s-maxage
类似max-age 但是只对share cache生效
immutable
指出响应在生命周期内是不会更新内容的
一般情况下,当强制刷新浏览器就(哪怕缓存没有过期)会发送请求去服务器进行validate
当缓存还没过期的时候(还在生命周期内),即使强制刷新也不会发送请求去validate(因为没必要)
这适合带版本永不更新的资源
stale-while-revalidate
指出缓存过期后的一段时间内仍然可以使用过期的缓存,而这段时间内资源会在后台进行revalidate,这样的好处就是减少直接的revalidate提高用户体验,因为都在后台进行revalidate了
Cache-Control: max-age=604800, stale-while-revalidate=86400
在604800秒后缓存过期,但是在接着的86400秒时间内仍然可以使用过期缓存,而且还会在这段时间内在后台进行revalidate
stale-if-error
指出过期的缓存,如果在进行revalidate时,服务器返回(500, 502, 503, or 504)仍然可以使用一段时间,当时在这一段时间之后还是会进入正常的过期流程
Request Directives
当请求头附带以下指令,是说明这是请求对cache的一种期望,但是不一定必须满足
max-age
允许cache返回生命周期在一定时间范围的缓存
很多浏览器会使用max-age=0来进行强制刷新
max-stale
允许cache返回过期时间在一定范围内的缓存
min-fresh
指出cache返回的缓存,它的生命周期至少还有一定时间,要是少于这个时间则禁止使用这个缓存,但是大部分浏览器都不支持这个指令
no-cache
请求cache在使用缓存时先进行revalidate
no-store
让cache不要缓存响应,哪怕响应是可以被缓存的
no-transform
同Response的指令
only-if-cached
指出希望获取已经缓存的响应,所以如果cache已经缓存了资源,直接返回即可
问题思考
max-age=0和no-cache区别
max-age=0,仅仅让缓存失效了,正常情况下还是要进行revalidate,但是在无法connnect服务器的情况下浏览器还是可以返回过期的缓存,而no-cache是禁止了这种行为
所以no-cache 更加等效于 must-revalidate, max-age=0
怎么移除浏览器缓存
一旦缓存生成了,那是没有完美的办法移除,因为在缓存生效期间都不会有请求都直接到服务器(除非设置了no-cache,must-revalidate)
一种方法是使用POST请求去请求同一个url,让缓存失效,但这种方式实现比较麻烦
另外一种方式就是使用Clear-Site-Data响应头字段
例如:Clear-Site-Data: cache
可以清除cache,cookies,storage,executionContexts
但是并不是所有浏览器都支持(safari就不支持)
怎么看待http缓存
一旦使用了http缓存,当然可以提高网页加载性能和提高整体体验,但是也意味着开发者对页面没办法实现绝对的控制(例如页面代码更新没有办法让所有用户访问时都能立即体验得到,除非使用no-cache等指令强制每次请求都去revalidate)
Referer
https://developer.mozilla.org...
https://www.rfc-editor.org/rf...
https://www.rfc-editor.org/rf...