HTTP协议:页面缓存之Cache-Control 和 Etag
Cache-Control是HTTP协议的一个组成部分,在处理请求与响应时所规定的指示通常用于提升网页加载速度,并且能够防止页面缓存不当影响对请求与响应的质量管理。 Cache-Control属于单向指令类别,并不表示响应部分也会包含相同的指令
一、请求
1. Cache-Control
| 允许的指令 | 值 | 含义 |
|---|---|---|
| max-age | delta-seconds | 客户端不愿意接受age超过这个值的缓存。并且不接受过期缓存,除非max-stale存在。 |
| max-stale | delta-seconds | 如果有值,客户端可以接受过期时间不超过指定值的缓存 如果没有值,客户端愿意接受过期缓存而无论过期过久。 |
| min-fresh | delta-seconds | 客户端愿意接受一个新鲜度不小于当前age加上指定时间的响应。简单说在指定的后续一段时间内不会过期的响应。 |
| no-cache | 无 | 客户端示意缓存,在使用缓存的时候必须进行校验。 |
| no-store | 无 | 客户端示意缓存,不要存储本次请求的响应。但是对于已经缓存的内容则没有影响。 |
| no-transform | 无 | 不得对资源进行转换或转变。Content-Encoding, Content-Range, Content-Type等HTTP头不能由代理修改。例如,非透明代理可以对图像格式进行转换,以便节省缓存空间或者减少缓慢链路上的流量。 no-transform指令不允许这样做。 |
| only-if-cached | 无 | 客户端只接受缓存给出的响应,如果缓存没有命中应该返回一个504 |
2. ETag
Etag被视为URL的一部分,在网络传输中用以标识URL对象是否发生变更,并区分不同语言环境以及不同的Session等信息。其核心作用类似于Cookie机制,在客户端与服务器之间建立有效的关联关系。通常情况下,在处理HTTP GET请求时会伴随使用ETags,并通过If-None-Match头字段来优化缓存机制。具体而言,在服务器端生成相应的ETag后,在后续请求中将此标记传递给客户端系统。这种设计使得客户端能够通过发送特定信息来指示服务器是否需要重新加载最新的资源版本。”
请求流程
Etag由服务器端生成,客户端通过If-Match或者说If-None-Match这个条件判断请求来验证资源是否修改。常见的是使用If-None-Match.请求一个文件的流程可能如下:
====第一次请求===
1.客户端发起 HTTP GET 请求一个文件;
2.服务器处理请求,返回文件内容和一堆Header,当然包括Etag(例如”2e681a-6-5d044840”)(假设服务器支持Etag生成和已经开启了Etag).状态码200
====第二次请求===
1.客户端发起 HTTP GET 请求一个文件,注意这个时候客户端同时发送一个If-None-Match头,这个头的内容就是第一次请求时服务器返回的Etag:2e681a-6-5d044840
2.服务器判断发送过来的Etag和计算出来的Etag匹配,因此If-None-Match为False,不返回200,返回304,客户端继续使用本地缓存;
作用
Etag 是一种机制,在于补充 Last-Modified 无法解决的特定问题。
对于那些虽然修改频率较高但文件内容未变的情况(仅仅修改的只是修改时间),这个时候我们并不希望客户端认为这个文件被修改了而重新进行 GET 请求;
当文件在极短时间内频繁更新时(比方说秒级以下的时间内进行了 N 次修改),If-Modified-Since 能检查到的时间粒度是秒级的(或者说 UNIX 记录 MTIME 只能精确到秒),这种修改却无法判断或者说 ETag 的作用就体现出来了;
某些服务器可能无法精确获取文件的最后修改时间信息;
为此,在 HTTP/1.1 标准中对 ETag 的具体实现细节并未做严格规定;
需要注意的是,在 HTTP/1.1 标准中对 ETag 的具体实现细节并未做严格规定,
唯一规定的是 ETag 需要放在" " 内;
此外,
在某些情况下(例如多语言网站或不同 Session 中的 cookie 不同),
即使相同的 URL 也可能返回不同的内容。
这种情况下如果过 Proxy,
Proxy 就无法区分导致串门,
只能简单的取消 cache 功能。
Etag 解决了这个问题,
因为它能区分相同 URL 下的不同对象。
二、响应
1. Cache-Control
RFC:7234#5.2.2 Response Cache-Control Directives]
| 允许的指令 | 值 | 含义 |
|---|---|---|
| must-revalidate | 无 | 一旦缓存过期,必须向源服务器进行校验,不得使用过期内容。如果无法连接必须返回504。 |
| proxy-revalidate | 与must-revalidate相同,但仅对公共缓存生效。 | |
| no-cache | field-name | 如果值,在没有成功通过源站校验的情况下不得使用缓存。 有值,在进行验证的时候不要发送值指示的头域。 如Cache-Control: no-cache="set-cookie,set-cookie2",表示不要携带cookie进行验证。 |
| no-store | 无 | 不要缓存当前请求的响应 |
| no-transform | 无 | 与请求头语义相同 |
| public | 无 | 任何缓存都可以进行缓存,即使响应默认是不可缓存或仅私有缓存可存的情况。 |
| private | field-name | 没有值,公有缓存不可存储;即使默认是不可缓存的,私有缓存也可以存储 。有值,将无值时的作用,限制到指定头字段上。公有有缓存不可存储指定的头字段,而其他字段可以缓存。 |
| max-age | delta-seconds | 在经过指定时间后将过期 |
| s-maxage | delta-seconds | 指定响应在公共缓存中的最大存活时间,它覆盖max-age和expires字段。 |
三、参考资料
关于Cache-Control头的详细说明,请参阅文档中的规定。
其中Etag字段可参考官方文档和权威资源。
