问题的开端是由http响应状态码304引出的!
一般而言,第一次访问一个网站,返回的状态码是200。那304状态码有什么用?
这跟浏览器性能优化分不开。
例如,浏览器直接使用缓存而不发起请求,减少请求;或者发出了请求,但服务器和浏览器资源一致,没有发生改变,这时候就没必要将数据再传一次,这样一来减少了数据传输(返回304和一个空的响应体),减轻了服务器的压力。
附一张浏览器缓存流程图:(仿别人用ProcessOn工具画的)
浏览器缓存流程图" class="has" height="900" src="https://img-blog.csdnimg.cn/20190327203438583.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzI3OTU0NjQz,size_16,color_FFFFFF,t_70" width="680" />
看完流程图,别慌。了解一下上面提到几个单词。
Last-Modified和If-Modified-Since、Etag和If-None-Match这两组header字段都是成对出现,即第一次请求的响应头带上某个字段(Last-Modified或者Etag),则后续请求会带上对应的请求字段(If-Modified-Since或者If-None-Match),若响应头没有Last-Modified或Etag字段,则后续请求头也不会有对应字段。
为什么既有Last-Modified还有Etag,这两者功能不是一样的吗?
你可能会觉得使用Last-Modified已经足以让浏览器知道本地的缓存副本是否足够新,为什么还需要Etag呢?
http1.1中Etag的出现主要是为了解决Last-Modified难解决的问题:
- 一些文件也许会周期性的更改,但是他的内容并不改变(仅仅修改文件时间),这个时候我们并不希望客户端认为这个文件被修改了,而需要重新发送get请求;
- 某些文件修改非常频繁,比如1s内修改了N次,而If-Modified-Since能检查到的时间粒度是秒级的,其结果是无法判断这种修改结果;
- 某些服务器不能精确得到文件的最后修改时间。
这时,利用Etag能够更加准确地控制缓存,因为Etag是服务器自动生成的对应资源在服务器端的唯一标识符。
浏览器怎么根据If-Modified-Since和If-None-Match决定返回304还是200状态码?
第一次请求, 服务器端返回ETag:"50b1c1d4f775c61:df3";第二次请求同一资源,客户端发送一个If-None-Match头,这个头的内容就是第一次请求时服务器返回的Etag:2e681a-6-5d044840;如果ETag没有改变,说明资源没有被修改,服务器返回状态304和空的响应体;
第一次请求,浏览器在response header中添加 Last-Modified,值是这个资源在服务器上的最后修改时间;浏览器下一次请求这个资源,浏览器检测到有 Last-Modified这个header,于是添加If-Modified-Since这个header,值就是Last-Modified中的值;服务器再次收到这个资源请求,会根据 If-Modified-Since 中的值与服务器中这个资源的最后修改时间对比,如果没有变化,返回304和空的响应体,直接从缓存读取;如果If-Modified-Since的时间小于服务器中这个资源的最后修改时间,说明文件有更新,于是返回新的资源文件和200状态码。
Etag定义可以好好看下百度百科:https://baike.baidu.com/item/ETag/4419019?fr=aladdin
推荐这个人,有浏览器实际缓存操作:https://www.cnblogs.com/softidea/p/5986339.html