当我们在用户代理端(浏览器)通过http协议访问某个互联网资源时,其请求和响应过程可能会经过多个中间缓存服务器,大致如下图所示
图中的浏览器和后端服务器是完成http请求交互的双方,而身处中间的缓存服务器只是充当了中间人角色,对于交互的双方来说,中间人可以起到一个传话筒的作用,也可以基于通信双方的要求做些必要的事情。HTTP规范为通信各方定义了统一的行为准则。
HTTP 缓存是一种响应消息的本地存储,以及控制其内的消息的存储、获取和删除的子系统。缓存存储了可缓存的响应是为了减少将来的响应时间和网络带宽消耗。任何客户端或者服务器可以使用缓存,但是,当服务器作为隧道而使用时,不能使用缓存。
共享缓存是一种缓存,它存储响应用于给一个以上的用户来复用,通常(但并不总是)部署作为一个中间人的一部分。与之相对的是私有缓存,这种缓存专门用于某一个用户,通常部署为一个用户代理的一个组件。
主要的缓存 key 是由请求方法以及目标 URI 组成的(可能带有查询组件)。但是,因为如今普遍使用的 HTTP 缓存通常都被限制为只对 GET 的响应进行缓存,因此,许多缓存简单地拒绝其他方法,并只使用 URI 作为主要的缓存 key。
1, 可缓存的方法
- GET
回应给 GET 请求的响应是可缓存的,缓存以 使用它来满足随后的 GET 和 HEAD 请求,除非 Cache-Control 头字段另有指定 - HEAD
回应给 HEAD 请求的响应是可缓存的,缓存可以使用它来满足随后的 HEAD 请求,除非 Cache-Control 头字段另有指定 - POST
回应给 POST 请求的响应,仅当它们包含明确的新鲜信息时,才是可缓存的(对 POST响应的缓存并没有被广泛实现)
2, 可缓存的状态码
响应如果带有被定义为默认是可缓存的状态码(比如 200、203、204、206、300、301、404、405、410、414 和 501),那么,该响应能够被一个使用试探性过期的缓存所复用,除非在请求方法的定义或者显式缓存控制里另有指定;其他所有状态码默认是不可缓存的。
状态码 | 说明 |
---|---|
OK 请求成功。 | |
Non-Authoritative Information 服务器已成功处理了请求,但返回的实体头部元信息不是在原始服务器上有效的确定集合,而是来自本地或者第三方的拷贝。 | |
Partial Content 服务器已经成功处理了部分 GET 请求。类似于 FlashGet 或者迅雷这类的 HTTP 下载工具都是使用此类响应实现断点续传或者将一个大文档分解为多个下载段同时下载。该请求必须包含 Range 头信息来指示客户端希望得到的内容范围,并且可能包含 If-Range 来作为请求条件。 | |
Multiple Choice 被请求的资源有一系列可供选择的回馈信息,每个都有自己特定的地址和浏览器驱动的商议信息。用户或浏览器能够自行选择一个首选的地址进行重定向。 | |
Moved Permanently 被请求的资源已永久移动到新位置,并且将来任何对此资源的引用都应该使用本响应返回的若干个 URI 之一。 | |
Found 请求的资源现在临时从不同的 URI 响应请求。由于这样的重定向是临时的,客户端应当继续向原有地址发送以后的请求。只有在Cache-Control或Expires中进行了指定的情况下,这个响应才是可缓存的。 | |
Temporary Redirect 请求的资源现在临时从不同的URI 响应请求。由于这样的重定向是临时的,客户端应当继续向原有地址发送以后的请求。只有在Cache-Control或Expires中进行了指定的情况下,这个响应才是可缓存的。 | |
Gone 被请求的资源在服务器上已经不再可用,而且没有任何已知的转发地址。这样的状况应当被认为是永久性的。 |
需要注意的是,所有状态码能够被缓存,仅当这个响应里带有明确的新鲜度信息;但是,定义为可缓存的状态码是允许在没有明确的新鲜度信息的情况下被缓存的。同样,状态码的定义能够在缓存行为上施加约束。
3, 可被存储的响应规则
1)请求方法被定义为可缓存的,且可被缓存所理解,并且
2)响应的状态码可被缓存所理解,并且
3)在请求或响应头字段中不包含“no-store”指令,并且
4)如果是共享缓存,则在响应头中不包含“private”指令,并且
5)如果是共享缓存,则在请求头中不包含Authorization头字段,除非响应明确允许缓存,并且
6)响应满足以下任何一个条件:
- 包含一个Expires头字段
- 包含一个max-age响应指令
- 包含一个s-maxage响应指令(共享缓存)
- 包含缓存控制扩展(Cache Control Extension)来允许可被缓存
- 拥有一个默认定义为可被缓存的状态码
- 包含一个public响应指令
需要注意的是,上述所列出的任何要求都可以被 cache-control 扩展所覆盖
因为源服务器并不总是会提供明确过期时间的,因此,当它没有指定一个过期时间的时候,缓存可以指派一个启发式过期时间,原理是利用一种使用其他头字段值来估算出一个看似合理的过期时间的算法。
当响应中未提供明确的过期时间,同时其响应的状态码是可缓存的,或响应被明确标记为可缓存的(比如,带有一个 public 响应指令)情况下,如果响应带有一个 Last-Modified
头字段,规范鼓励缓存所使用的启发式过期时间的值不超过那个头字段的时间以后的某个比例,通常这个比例会设置为 10%。
注意: 对于带有 query
组件的 URI(也就是说,包含有 "?" 的 URI),【RFC2616】章节 13.9 禁止缓存对这种 URI 计算启发式新鲜度。实践中,这项要求并未被广泛实现。因此,规范鼓励源服务器去发送明确的指令(比如,Cache-Control: no-cache
),如果它们希望阻止缓存的话。
需要注意的是,在常规操作中,如果一个响应既没有缓存验证器也没有一个明确过期时间,某些缓存将不会存储这种响应,因为存储这种响应通常是没有用处的。但是,缓存不会被禁止存储这种响应。
对于响应中只要etag的情况,等同于响应头中带有Cache-Control:no-cache效果一般。