前言
HTTP 缓存主要流程主要看下图:

HTTP 头部字段
Accept: text/html,image/* # 浏览器可以接收的类型
Accept-Charset: ISO-8859-1 # 浏览器可以接收的编码类型
Accept-Encoding: gzip,compress # 浏览器可以接收压缩编码类型
Accept-Language: en-us,zh-cn # 浏览器可以接收的语言和国家类型
Host: www.lks.cn:80 # 浏览器请求的主机和端口
If-Modified-Since: Tue, 11 Jul 2000 18:23:51 GMT # 某个页面缓存时间
Referer: http://www.lks.cn/index.html # 请求来自于哪个页面
User-Agent: Mozilla/4.0 compatible; MSIE 5.5; Windows NT 5.0 # 浏览器相关信息
Cookie: # 浏览器暂存服务器发送的信息
Connection: close1.0/Keep-Alive1.1 # HTTP请求的版本的特点
Date: Tue, 11 Jul 2000 18:23:51GMT # 请求网站的时间
Allow:GET # 请求的方法 GET 常见的还有 POST
Keep-Alive:5 # 连接的时间;5
Connection:keep-alive # 是否是长连接
Cache-Control:max-age=300 # 缓存的最长时间 300s
HTTP 缓存规则
根据是否需要向服务器发送请求,HTTP 缓存主要可以分为两类:强制缓存与对比缓存。强制缓存如果生效,则直接使用缓存,不再向服务器发送请求,对比缓存无论是否生效,都需要现象服务器发送请求。 当强制缓存与对比缓存同时存在时,强制缓存优先级高于对比缓存。
强制缓存
对于强制缓存来说,header 中会有两个字段来标明失效规则(Expires/Cache-Control),指的是当前资源的有效期 在客户端第一次请求数据时,此时缓存数据库中没有对应的缓存数据,需要请求服务器,服务器返回后,将数据存储至浏览器缓存中。
Expires/Cache-Control 规则
Expires 规则
Expires 的值为服务端返回的到期时间,在响应 http 请求时告诉浏览器在过期时间前浏览器可以直接从浏览器缓存取数据,而无需再次请求。不过 Expires 是 HTTP 1.0 的东西,现在默认浏览器均默认使用 HTTP 1.1,所以它的作用基本忽略。Expires 的一个缺点就是,返回的到期时间是服务器端的时间,这样存在一个问题,比较的时间是客户端本地设置的时间,所以有可能会导致差错,所以在 HTTP 1.1 版开始,使用 Cache-Control 替代。
Cache-Control
用于定义所有的缓存机制都必须遵循的缓存指示,这些指示是一些特定的指令,包括 public、private、no-cache(表示可以存储,但在重新验正其有效性之前不能用于响应客户端请求)、no-store、max-age、s-maxage 以及 must-revalidate 等;Cache-Control 中设定的时间会覆盖Expires中指定的时间。
对比缓存
对比缓存,顾名思义,需要进行比较判断是否可以使用缓存。浏览器第一次请求数据时,服务器会将缓存标识与数据一起返回给客户端,客户端将二者备份至浏览器缓存中。再次请求数据时,客户端将备份的缓存标识发送给服务器,服务器根据缓存标识进行判断,判断成功后,返回 304 状态码,通知客户端比较成功,可以使用缓存数据。 在对比缓存生效时,状态码为 304,并且报文大小和请求时间大大减少。原因是,服务端在进行标识比较后,只返回 header 部分,通过状态码通知客户端使用缓存,不再需要将报文主体部分返回给客户端。对于对比缓存来说,缓存标识的传递是我们着重需要理解的,它在请求 header 和响应 header 间进行传递,一共分为两种标识传递,接下来,我们分开介绍。
Last-Modified 规则
Last-Modified:
服务器在响应请求时,告诉浏览器资源的最后修改时间。
If-Modified-Since:
再次请求服务器时,通过此字段通知服务器上次请求时,服务器返回的资源最后修改时间。 服务器收到请求后发现有头 If-Modified-Since 则与被请求资源的最后修改时间进行比对。 若资源的最后修改时间大于 If-Modified-Since,说明资源又被改动过,则响应整片资源内容,返回状态码 200; 若资源的最后修改时间小于或等于 If-Modified-Since,说明资源无新修改,则响应 HTTP 304,告知浏览器继续使用所保存的 cache。
Etag/If-None-Match 规则(优先级高于Last-Modified/If-Modified-Since)
Etag:
服务器资源的唯一标识符, 浏览器可以根据 ETag 值缓存数据, 节省带宽. 如果资源已经改变, etag 可以帮助防止同步更新资源的相互覆盖. ETag 优先级比 Last-Modified 高.
If-None-Match:
再次请求服务器时,通过此字段通知服务器客户段缓存数据的唯一标识。 服务器收到请求后发现有头 If-None-Match 则与被请求资源的唯一标识进行比对, 不同,说明资源又被改动过,则响应整片资源内容,返回状态码 200; 相同,说明资源无新修改,则响应 HTTP 304,告知浏览器继续使用所保存的 cache。 看到这里,你也许会问,既然已经有了 Last-Modified 已经能够知道本地缓存是否是最新的了,为什么还需要 Etag 呢? 主要是基于以下几个原因:Last-Modified 标注的最后修改时间只能精确到秒,如果有些资源在一秒之内被多次修改的话,他就不能准确标注文件的新鲜度了;如果某些资源会被定期生成,当内容没有变化,但 Last-Modified 却改变了,导致文件没使用缓存有可能存在服务器没有准确获取资源修改时间,或者与代理服务器时间不一致的情形。
不能缓存的请求:
- 不能被缓存的请求 HTTP,例如信息头中包含 Cache-Control:no-cache,pragma:no-cache,或 Cache-Control:max-age=0 等告诉浏览器不用缓存的请求
- 需要根据 Cookie,认证信息等决定输入内容的动态请求是不能被缓存的
- 经过 HTTPS 安全加密的请求(有人也经过测试发现,ie 其实在头部加入 Cache-Control:max-age 信息,firefox 在头部加入 Cache-Control:Public 之后,能够对 HTTPS 的资源进行缓存)
- HTTP 响应头中不包含 Last-Modified/Etag,也不包含 Cache-Control/Expires 的请求无法被缓存
- 目前浏览器的实现是不会对 POST 请求的响应做缓存的(从语义上来说也不应该),并且规范中也规定了返回状态码不允许是 304。不过这并不表示 POST 的响应不能被缓存,根据 RFC 7231 - Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content 中描述的,如果在 POST 请求对应的响应中包含 Freshness 相关信息的话,这次响应也是可以被缓存,具体可以参考上面的那个链接。