缓存相关字段:
http1.0:pragma、expires
http1.1:cache-control、last-modified、if-modified-since、etag、if-none-match
强缓存:
不发起http请求,直接使用本地缓存,比如浏览器地址栏回车,使用浏览器的刷新按钮,在Expires或max-age生效的情况下,触发强缓存。
不经常被修改的文件,通常用强缓存
协商缓存(弱缓存):
在使用本地缓存前,先与服务器协商,核对缓存文件是否为最新。请求头携带If-None-Match或If-Modified-Since,也可都携带。
一、Cache-control请求头/响应头
1.客户端设置
max-age:表示本地缓存的资源将在xx秒之后过期
no-cache:跳过强缓存和协商缓存阶段,直接请求服务器
no-store:不在本地缓存数据。
2.服务端设置
max-age:让客户端在xx秒内,不发起请求,直接使用本地缓存。(max-age=0相当于no-cache)
s-maxage: 覆盖max-age或者Expires头,但是仅适用于共享缓存(比如各个代理),私有缓存会忽略它。
no-cache:强制浏览器重新发送请求,但浏览器会缓存到本地。(不使用缓存)
no-store: 强制浏览器重新发送请求,并且禁止浏览器缓存数据。(不缓存)
public: 响应可以被任何对象(包括:发送请求的客户端,代理服务器,等等)缓存
private: 响应只能被单个用户缓存,不能作为共享缓存(即代理服务器不能缓存它)。私有缓存可以缓存响应内容,比如:对应用户的本地浏览器。
must-revalidate:一旦资源过期(比如已经超过max-age),在成功向原始服务器验证之前,缓存不能用该资源响应后续请求。
immutable:响应正文不会随时间而改变。资源未过期,客户端不应发送重新验证请求头(例如If-None-Match或If-Modified-Since)来检查更新,即使用户显式地刷新页面。
二、Pragma 请求头/响应头
pragma:no-cache
http1.0使用的缓存字段,现在不常用,向后兼容只支持 HTTP/1.0 协议的缓存服务器,与 Cache-Control: no-cache 效果一致。
三、过期时间expires和cache-control:max-age
Expires: Thu, 29 Oct 2020 11:29:05 GMT 和 cache-control:max-age=10效果类似,max-age是相对时间,表示过了10s过后 再重新请求 而expires是绝对时间,表示过了这个时间,再重新请求。
1.首次请求
客户端:请求a.js文件
服务器:返回a.js + cache-control:max-age=10 + 过期时间Expires(Expires: Thu, 29 Oct 2020 11:29:05 GMT)
注意:expires主要是用来兼容http1.0版本的浏览器
如果在Cache-Control响应头设置了 "max-age" 或者 "s-max-age" 指令,那么 Expires 头会被忽略。
2.后续请求
客户端:
(1)先判断本地时间是否超过了过期时间,若时间没过,不发起请求,直接使用本地缓存。
(2)时间过期,重复步骤1
问题:
假设已过期,浏览器再次请求服务器,但a.js相比上次并未做任何改变,那这次请求我们是否通过某种方式加以避免?
四、Last-Modified(响应头)和if-Modified-Since(请求头)
同时设置cache-control:max-age和 last-modified表示强制缓存和协商缓存共用,若cache-control设置为max-age=0或者no-cache,则只有协商缓存生效
1.首次请求
客户端:请求a.js文件
服务器:返回a.js + cache-control:max-age=10 + last-modified(Thu, 22 Oct 2020 10:23:29 GMT) + 状态码200
2.后续请求
客户端:
(1)先判断本地时间是否超过了过期时间max-age,若未过期,使用浏览器缓存。
(2)过期,客户端带上if-Modified-Since(也就是上次请求服务器返回的Last-Modified),服务器将客户端传来的if-Modified-Since与自己的Last-Modified做对比。
(3)若if-Modified-Since ≠ Last-Modified,服务器返回新的a.js + 新的Last-Modified + cache-control:max-age=10
(4)若if-Modified-Since = Last-Modified,服务器返回状态码304,表示文件没修改过,让客户端使用浏览器的缓存。
问题:
1. 资源文件每次一被修改,Last-Modified也会跟着修改,如果修改过后又进行版本回退,内容跟浏览器已缓存的一致,但用户还是要重新请求资源,因为Last-Modified改变了。
2. Last-Modified只能精确到秒,如果恰好在一秒之内变化了,Last-Modified是不会修改成功的。
五、Etag(响应头)和if-None-Match(请求头)
服务端生成资源文件的唯一标识符Etag,资源更改,则一定要生成新的Etag值
对于大文件,不会根据整个文件去生成hash 可以使用文件大小或者文件开头去生成hash
md5是 摘要算法,也叫hash算法
1. 是单向的 无法反解
2. 对于相同内容 加密结果相同;不同的内容 加密结果的长度相同
1.首次请求
客户端:请求a.js文件
服务器:返回a.js + cache-control:max-age=10 + last-modified(Thu, 22 Oct 2020 10:23:29 GMT) + Etag + 200
2.后续请求
客户端:
(1)先判断本地时间是否超过了过期时间max-age,未过期,则使用浏览器缓存。
(2)若过期,客户端带上if-Modified-Since + if-None-Match,服务器先将客户端传来的if-None-Match与自己的Etag做对比。
(3)若if-None-Match ≠ Etag 服务器返回新的a.js + 新的Etag + Last-Modified + cache-control:max-age=10
(4)若if-None-Match = Etag,服务器返回状态码304,表示文件没修改过,让客户端使用浏览器的缓存,if-Modified-Since和Last-Modified 也不需要再比较了。
面试点睛:
1. 为什么有了Last-Modified还要用etag?
- 防止Last-Modified变了,但文件内容没变的情况下,浏览器需要重新请求服务器
- 防止文件在1s内发生变化,而Last-Modified不变
2. 用户行为如何触发缓存?
- 打开网页,地址栏输入地址: 查找 disk cache 中是否有匹配。如有则使用;如没有则发送网络请求。
- 普通刷新 (F5):因为 TAB 并没有关闭,因此 memory cache 是可用的,会被优先使用(如果匹配的话)。其次才是 disk cache。
- 强制刷新 (Ctrl + F5):浏览器不使用缓存,因此发送的请求头部均带有 Cache-control: no-cache(为了兼容,还带了 Pragma: no-cache),服务器直接返回 200 和最新内容。