There are only two hard things in Computer Science: cache invalidation and naming things.
前段时间在优化 HTTP 缓存策略,以减轻一下 API 的压力。这段时间空闲一些就做个简单整理归纳。参考资料主要是 O’REILLY 的《HTTP 权威指南》和网上的一些文章。
服务器可以 Expires 和 Cache-Control 两个 Header 来设置缓存过期时间。
Expires 比较简单,就只能设置一个绝对日期,而后来发布的 Cache-Control 自然更强大一些,除了能指定缓存有效期之外还能更进一步指定缓存控制策略。
正因为 Expires 过于简单,所以它的局限也比较大。例如因为其使用绝对日期,所以必须保证浏览器的时钟跟服务器的时钟同步,《HTTP 权威指南》更倾向于使用 Cache-Control 也是基于这个理由。
不过 Expires 特别适用于设置静态文件缓存,因为通常这些文件很少有变化。
另外,当 Cache-Control 设置了 max-age 值时,Expires 会被忽略。
通过条件请求(Conditional GET)可以实现缓存再验证。HTTP 定义了 5 个条件请求 Header,对于缓存再验证来说最有用的 2 个是 If-Modified-Since 和 If-None-Match 。
##If-Modified-Since
If-Modified-Since 可以与服务器返回的 Last-Modified 配合工作。
当请求中带有 If-Modified-Since 时,如果服务器会通过返回 200 OK 来新的响应内容或者返回 304 Not Modified 来表示缓存未过期。
有些情况下仅使用最后修改日期进行再验证是不够的。
有些文档可能会被周期性地重写(比如,从一个后台进程中写入),但实际包含的数据常常是一样的。 尽管内容没有变化,但修改日期会发生变化。
有些服务器无法准确地判定其页面的最后修改日期。
所以 HTTP 引入 ETag 。
ETag 可能包含响应内容的序列号或版本号,或者是它的校验和指纹信息。Last-Modified 精确度比 ETag 低。
当请求中带有 If-None-Match 时,如果服务器会通过返回 200 OK 来新的响应内容或者返回 304 Not Modified 来表示缓存未过期。
If-Modified-Since 当与 If-None-Match 一同出现时,If-Modified-Since 会被忽略掉。
浏览器通过 Expires 和 Cache-Control 来判断是从缓存中读数据,还是发出 HTTP 请求读数据,好处是可以减少 HTTP 请求数。
在后续的缓存再验证时,服务器根据 If-Modified-Since 和 If-None-Match 来判断是返回 200 还是 304 状态,好处只是减少网络传输量,但不能减少 HTTP 请求数。