likes
comments
collection

[RFC文档阅读]HTTP Cache

作者站长头像
站长
· 阅读数 3

Cache类型

private cache

意味着该缓存不会与其他用户共享

share cache

  1. Proxy cache 就是中间代理服务器的缓存,但是因为https的流行,这些代理服务器基本上只能转发请求
  2. Managed cached 就是源服务器配置的缓存:nginx,cdn,service worker[RFC文档阅读]HTTP Cache

什么样请求响应时可以被缓存的

  1. 响应的状态码是1xx, 2xx,3xx, 4xx,5xx范围内
  2. 没有出现no-store指令
  3. 响应含有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生命周期计算)

  1. 如果cache是共享的,而且响应头也包含s-maxage,则使用这个值
  2. 如果响应头包含max-age,则使用这个值
  3. 如果响应头包含Expires,则使用这个字段减去Date字段(如果Date字段不存在,则使用接收响应的时间)
  4. 否则如果在响应头没有明确的过期时间,则会应用一种启发式的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字段,如果没有则认为是0date_value 响应头Date字段now 当前时间request_time 请求时间response_time 响应时间

计算方式主要有两种

  1. apparent_age , response_time减去date_value,如果浏览器时间是跟服务器时间同步的应该是比较准确(但是不太可能) apparent_age = max(0, response_time - date_value);
  2. 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

  1. 304状态码,不返回响应内容
  2. 或者完整的响应
  3. 也有可能是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...