HTTP缓存体系

策略 说明
缓存存储策略 这个策略的作用只有一个,用于决定 Http 响应内容是否可缓存到客户端
缓存过期策略 这个策略的作用也只有一个,那就是决定客户端是否可直接从本地缓存数据中加载数据并展示(否则就发请求到服务端获取)
缓存对比策略 将缓存在客户端的数据标识发往服务端,服务端通过标识来判断客户端 缓存数据是否仍有效,进而决定是否要重发数据。浏览器缓存控制机制有两种:HTML Meta标签 vs. HTTP头信息

HTTP缓存控制

HTML Meta标签控制缓存

浏览器缓存机制,其实主要就是HTTP协议定义的缓存机制(如: Expires; Cache-control等)。但是也有非HTTP协议定义的缓存机制,如使用HTML Meta 标签,可以在HTML页面的节点中加入标签,代码如下:
用于设定网页的到期时间,一旦过期则必须到服务器上重新调用。需要注意的是必须使用GMT时间格式; 用于设定禁止浏览器从本地机的缓存中调阅页面内容,设定后一旦离开网页就无法从Cache中再调出;
只有部分浏览器可以支持,而且所有缓存代理服务器都不支持,因为代理不解析HTML内容本身。而广泛应用的还是 HTTP头信息 来控制缓存。

HTTP头信息控制缓存

  • 浏览器第一次请求流程图:
  • 浏览器再次请求

HTTP协议

http报文就是客户端(如浏览器)和web服务器通信时发送和响应的的数据。
http请求由三部分组成分别是:请求行、消息报头、请求正文
http响应也是由三个部分组成,分别是:状态行、消息报头、响应正文
与缓存相关的信息,都包含在消息报头(header)中

与缓存有关的header

Request:

参数 说明
Cache-Control: max-age=0 以秒为单位
If-Modified-Since: Mon, 19 Nov 2012 08:38:01 GMT 缓存文件的最后修改时间。
If-None-Match: "0693f67a67cc1:0" 缓存文件的Etag值
Cache-Control: no-cache 不使用缓存
Pragma: no-cache 不使用缓存

Response:

参数 说明
Cache-Control: public 响应被缓存, 代理服务器可缓存
Cache-Control: private 响应被缓存, 只客户端缓存
Cache-Control:no-cache 提醒浏览器要从服务器提取文档进行验证
Cache-Control:no-store 绝对禁止缓存(用于机密,敏感文件)
Cache-Control: max-age=60 60秒之后缓存过期(相对时间)
Date: Mon, 19 Nov 2012 08:39:00 GMT 当前response发送的时间
Expires: Mon, 19 Nov 2012 08:40:01 GMT 缓存过期的时间(绝对时间)
Last-Modified: Mon, 19 Nov 2012 08:38:01 GMT 服务器端文件的最后修改时间
ETag: "20b1add7ec1cd1:0" 服务器端文件的Etag值

Expires

Expires的值为web服务器返回的到期时间(GMT 格林威治时间),浏览器下次请求时间小于服务器返回的时间则浏览器直接从缓存中获取数据,而不用再次发送请求

Cache-Control

Cache-Control常见的取值有private、public、no-cache、max-age,no-store
private:客户端可以缓存
public :客户端和代理服务器都可缓存
max-age=:缓存存储的最大周期,超过这个时间缓存被认为过期(单位秒)
no-cache:这个很容易让人产生误解,使人误以为是响应不被缓存,实际上Cache-Control:no-cache是会被缓存的,只不过每次在向浏览器提供响应数据时,浏览器每次都要向服务器发送请求,由服务器来决策来评估缓存的有效性。
no-store: 所有内容都不缓存(真真的不缓存)
更多详细的Cache-Control的取值参考MDN文档https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Cache-Control

强制缓存:Expires和Cache-Control区别

Expires和Cache-control称为强制缓存,都是在缓存未失效时,浏览器不向服务端发起请求,直接从缓存中取数据。
Expires是上古时代Http1.0的东西了,现在默认浏览器均默认使用的是Http1.1了,所以它的作用是可以基本忽略。Expires有一个缺点,就是返回的时间的服务器的绝对时间,用本地时间和服务端时间比较是否过期,是不严谨的,用户是可以随便修改本地时间的,那这样缓存随时可以过期,所以被Cache-Control:max-age=取代了,在http1.1中Cache-Control优先级高于Expires。软件工程的特点是向下兼容,Expires一直就没有抛弃,对于使用http1.0协议的浏览器仍然有作用。

Last-Modified

服务器响应浏览器请求,告诉浏览器资源的最后修改时间

Etag

服务器响应请求时,告诉客户端(浏览器)当前请求的资源在服务器标识(Etag的生成算法由服务器决定,不同的web服务器生成etag的算法可能还不一样,Http协议并没有要求etag的生成规则,如文件小的时候可以使用md5sum sha1sum,或者根据文件修改时间,文件大小,文件inode文件属性综合生成,这里不做详细叙述),我们可以将其理解为一个资源的唯一标识,只要文件发生变化Etag的值也变化。

对比缓存:Last-Modified/If-Modified-Since和Etag/If-None-Match

Last-Modified和Etag称为对比缓存,所谓对比缓存,顾明思议,就是需要服务器来比较判断,来告诉客户端(浏览器)是否可以使用本地缓存,对比缓存生效时,服务器返回给客户端(浏览器)的Http Code为304,服务器只是返回的http header信息,并无响应正文,客户端通过服务器返回的状态码304,知道本地缓存并无修改,可以直接使用本地缓存,这样大大的较少的客户端请求响应时间。
对比缓存大致流程是这样的,当浏览器请求服务器的某资源时, 服务器得到资源的最后修改时间(Last-Modified)或根据一定的算法生成资源的标识(Etag),并将Last-Modified或If-Modified-Since返回给浏览器,浏览器把Last-Modified或Etag 和 资源内容同时缓存在本地,当下次再次向服务器请求此资源时,会将If-Modified-Since: Mon, 07 Nov 2016 07:51:11 GMT 或If-None-Match: xxxxxxx"的请求头把发送给服务器,服务器再次计算资源的Last-Modified或Etag,如果和客户端传来的值比较不相同,则表明资源发生了变化,则给浏览器返回Http Code 200。并将资源内容返回给浏览器,如果相同表示没有变化就给浏览器返回Http Code 304 ,并不需要返回资源内容给浏览器。
对于浏览器来说的话,一般会在强制缓存过期的情况下(或者按F5刷新,不同的浏览器可能不一样,firefox是按F5)如果资源原先的响应header中带有Last-Modified和Etag的话,浏览器请求时会在请求header中带上If-Modified-Since和If-None-Match。

Last-Modified和Etag区别

在这里,也许你可能会问,通过Last-Modified就可以知道资源内容是否发生了变化,为什么还需要Etag呢,这不是多此一举吗?,主要原因Etag解决了Last-modified没能解决的问题,Etag比Last-Modified更加严谨。
1.一些文件也许会周期性的更改,但是他的内容并不改变(仅仅改变的修改时间),这个时候我们并不希望客户端认为这个文件被修改了
2.某些文件修改非常频繁,比如在秒以下的时间内进行修改,(比方说1s内修改了N次),If-Modified-Since能检查到的粒度是s级的,这种修改无法判断
3.如需要对动态生成的内容做缓存,那就可以用etag来控制缓存了
需要注意的是,如果同时有Last-Modified和Etag存在,在发送请求时,浏览器会一次性的将这两个值都发给服务器,没有优先级,服务器是都比较,还是只比较一个,不同的web服务器可能比较逻辑不一样吧。

Http请求头中Cache-Control: no-cache的意义

一般按ctrl+f5强制刷新时,请求头里面都带有Cache-Control: no-cache,其实这是跳过本地的强制缓存和告诉服务器跳过对比缓存,也就是重新请求资源了。对于前端同学在通过GET请求后端API接口时,在ajax的请求头中统一带上Cache-Control: no-cache

缓存体系关系

几种状态码的区别

总结

  1. 对于强制缓存(200),服务器通知浏览器一个缓存时间,在缓存时间内,下次请求,直接用缓存,不在时间内,执行对比缓存策略。
  2. 对于对比缓存(也称304缓存),将缓存信息中的Etag和Last-Modified通过请求发送给服务器,由服务器校验,返回304状态码时,浏览器直接使用缓存。