浏览器缓存是一个比较复杂但又比较重要的机制,在我们浏览一个页面时发现有异常的情况,通常考虑的就是是不是浏览器做了缓存,所以一般的做法就是按Ctrl+F5组合键重新请求一次这个页面,重新请求页面肯定是最新页面。为什么重新请求就一定能够请求到没有缓存的页面呢?首先在浏览器端,如果是按Ctrl+F5组合键刷新页面,那么浏览器会直接向目标URL发送请求,而不会使用浏览器缓存的数据;其次即使请求发送到服务端,也有可能访问到的是缓存数据,比如,我们的应用服务器的前端部署一个缓存服务器,如Varnish代理,那么Varnish也可能直接使用缓存数据。所以为了保证用户能够看到最新的数据,必须通过HTTP来控制。
当我们使用Ctrl+F5组合键刷新一个页面时,在HTTP的请求头中会增加一些请求头,它会告诉服务端我们要取最新的数据而不是缓存数据。
1、没有使用Ctrl+F5
2、使用Ctrl+F5,在请求头增加了Pragma:no-cache和Cache-Control:no-cache参数。
Cache-Control/Pragma
这个HTTP Head字段用于指定所有缓存机制在整个请求/响应链中必须服从的指令,如果知道该页面是否为缓存,不仅可以控制浏览器,还可以控制和HTTP相关的缓存或代理服务器。HTTP Head字段有一些可选值:
Pragma字段的作用和Cache-Control有点类似,它也是HTTP头中包含一个特殊的指令,使相关的服务器遵守该指令,最常用的就是Pragma:no-cache,他和Cache-Control:no-cahce的作用一样。
Expires
Expires是Web服务器响应消息头字段,在响应http请求时告诉浏览器在过期时间前浏览器可以直接从浏览器缓存取数据,而无需再次请求,浏览器在发送请求之前会检查这个页面的这个字段,看该页面是否已经过期了,过期了就重新向服务器发起请求。Expires 是 HTTP/1 的产物,受限于本地时间,如果修改了本地时间,可能会造成缓存失效。Expires: Wed, 22 Oct 2018 08:41:00 GMT表示资源会在 Wed, 22 Oct 2018 08:41:00 GMT 后过期,需要再次请求。
Last-Modified
浏览器在第一次访问资源时,服务器返回资源的同时,在response header中添加 Last-Modified的header,值是这个资源在服务器上的最后修改时间,浏览器接收后缓存文件和header;
浏览器下一次请求这个资源,浏览器检测到有 Last-Modified这个header,于是添加If-Modified-Since这个header,值就是Last-Modified中的值;服务器再次收到这个资源请求,会根据 If-Modified-Since 中的值与服务器中这个资源的最后修改时间对比,如果没有变化,返回304和空的响应体,直接从缓存读取,如果If-Modified-Since的时间小于服务器中这个资源的最后修改时间,说明文件有更新,于是返回新的资源文件和200。
但是Last-Modified存在一些弊端:
- 如果本地打开缓存文件,即使没有对文件进行修改,但还是会造成 Last-Modified 被修改,服务端不能命中缓存导致发送相同的资源
- 因为 Last-Modified 只能以秒计时,如果在不可感知的时间内修改完成文件,那么服务端会认为资源还是命中了,不会返回正确的资源
既然根据文件修改时间来决定是否缓存尚有不足,能否可以直接根据文件内容是否修改来决定缓存策略?所以在 HTTP / 1.1 出现了 ETag 和If-None-Match
ETag和If-None-match
Etag是服务器响应请求时,返回当前资源文件的一个唯一标识(由服务器生成),只要资源有变化,Etag就会重新生成。浏览器在下一次加载资源向服务器发送请求时,会将上一次返回的Etag值放到request header里的If-None-Match里,服务器只需要比较客户端传来的If-None-Match跟自己服务器上该资源的ETag是否一致,就能很好地判断资源相对客户端而言是否被修改过了。如果服务器发现ETag匹配不上,那么直接以常规GET 200回包形式将新的资源(当然也包括了新的ETag)发给客户端;如果ETag是一致的,则直接返回304知会客户端直接使用本地缓存即可。
参考:
https://www.jianshu.com/p/54cc04190252