HTTP1.0使用Pragma和Expires来进行缓存控制。
Expires的值对应一个GMT(格林尼治时间),比如Mon, 22 Jul 2002 11:12:01 GMT
来告诉浏览器资源缓存过期时间,如果还没过该时间点则不发请求。响应报文中Expires所定义的缓存时间是相对服务器上的时间而言的,其定义的是资源“失效时刻”,这样的话就存在客户端服务端时间不一致的问题。
经过我测试发现,Chrome设置此响应头无效,即还是重新获取资源。这是为什么呢?主要是因为服务端服务程序自动设置了Cache-Control: public, max-age=86400
,导致expires被覆盖:
其可选值为no-cache
,指明浏览器不使用缓存,每次都要从服务端获取资源。
未加Paragma
前:
headers: {
'Content-type': 'application/javascript',
'Cache-Control': 'public, max-age=86400'
}
如修改我们的资源响应头:
app.use('/js/:jsName',(req,res)=>{
let fileName = req.params.jsName;
setTimeout(()=>{
res.sendFile(fileName,{
root: path.join(__dirname,'../js'),
headers: {
'Content-type': 'application/javascript',
'Cache-Control': 'public, max-age=86400',
'Pragma': 'no-cache'
}
});
},2000);
})
然后每次浏览器都会重新发起请求。服务端返回304
或200
。
经过测试发现给浏览器设置请求头,并没有生效,即当服务设置为允许缓存时,就算设置了该请求头,浏览器还是会使用缓存。
<meta http-equiv="Pragma" content="no-cache">
Cache-Control也是一个通用首部字段,这意味着它能分别在请求报文和响应报文中使用。
作为请求头:
作为响应头:
关于Cache-Control的决策树图,来源谷歌开发者文档:
之前的缓存控制字段告诉浏览器是否需要和什么时候向源服务器发起请求或验证。所以还有对应的验证字段。
服务器将资源传递给客户端时,会将资源最后更改的时间以“Last-Modified: GMT”的形式加在实体首部上一起返回给客户端。
客户端会为资源标记上该信息,下次再次请求时,会把该信息附带在请求报文中一并带给服务器去做检查,若传递的时间值与服务器上该资源最终修改时间是一致的,则说明该资源没有被修改过,直接返回304状态码即可。
客户端发送的条件请求头:
If-Modified-Since: Last-Modified-value
按字面意思理解就是“如果自xxx时候以来资源修改了,就发送资源“。该请求首部告诉服务器如果客户端传来的最后修改时间与服务器上的一致,则直接回送304 和响应报头,否则重新发送资源。
If-Unmodified-Since: Last-Modified-value
告诉服务器,若Last-Modified没有匹配上,则应当返回412(Precondition Failed) 状态码给客户端。
但是基于修改时间的方式存在一些问题,那就是有可能资源的修改时间发生变化,但是内容不变,如,我先修改并保存修改,接着我又撤销修改。还有,条件请求头的时间精确到秒,如果资源在一秒内发生多次变化,就无法及时更新了。
针对Last-Modified的问题,ETag采用了标识符的方式,一般采用哈希算法计算唯一标识符,如MD5。
对应的请求头:
**If-None-Match: ETag-value **
当标识符一致,服务端返回304,否则返回200,即重新发送资源。
** If-Match: ETag-value**
告诉服务器如果没有匹配到ETag,或者收到了“*”值而当前并没有该资源实体,则应当返回412。
当同时设置Last-Modified和ETag时,需要两种同时匹配,才返回304。