HTTP 缓存和代理行为

HTTP 从一开始就被设计成可以被广泛代理和缓存的。在允许的情况下,中间代理和客户端本身对 HTTP 进行缓存都是可以预期的。这是 HTTP 能够实现高扩展性的一个基本设计点。

这意味着,无论出于何种原因,只要响应被定义为可缓存的,服务器实现都应该假定这些响应会被缓存。如果服务器没有指定可缓存响应的适当 Cache-Control 指令,它可能永远不会收到后续请求。

以下 HTTP 方法被定义为可缓存的:HEAD、GET 和 POST RFC 7231#section-4.2.3 (第 4.2.3 节)。

返回以下状态码的请求被定义为可缓存的:200、203、204、206、300、301、404、405、410、414 和 501 RFC 7231#section-6.1 (第 6.1 节)。

一个常见的误解是,通过安全 HTTP 连接发出的请求由于安全原因不会被缓存。事实上,HTTP 规范中对 https 没有特殊规定,缓存的方式与非加密 HTTP 相同。大多数现代浏览器对安全连接应用相同的缓存算法。

大多数 Python HTTP 客户端库在缓存方面非常保守,因此在使用这些客户端时,一整类完全有效的 RFC 缓存不会被观察到。假设“它在 Python 工具链中有效”并不意味着它在所有情况下都有效,也不是实现 HTTP 的唯一方法。我们预计浏览器中的 JavaScript 客户端将具有与现有 Python 客户端完全不同的缓存语义(这些语义完全符合 RFC)。

在 OpenStack API 中实现任何内容时,仔细考虑缓存语义对于 API 与现有的大量运行时、编程语言和代理服务器(开放的和商业的)兼容至关重要。

实际中的缓存头

鉴于上述内容(“缓存 […] 是可以预期的”),服务必须提供适当的 Cache-Control 头,以避免像 1747935 中描述的错误,其中一个中间代理无限期地缓存响应,尽管底层资源发生了变化。

为了避免此问题,至少,定义为“可缓存”且不另行控制缓存的响应必须包含一个头:

Cache-Control: no-cache

尽管听起来是这样,但 no-cache(由 RFC 7234#section-5.2.1.4 定义)仅意味着在可以针对源服务器验证的情况下才使用缓存资源。但是,如果没有可以发送回服务器的 If-Modified-SinceIf-None-Match 条件请求头,no-cache 意味着不会发生缓存。有关验证的更多信息,请参阅 RFC 7234#section-4.3

这意味着,至少所有返回 200 状态的 GET 请求的响应都需要该头,除非响应中明确表达了显式的缓存要求。

MDN 提供了对 Cache-Control 头 的良好概述,并提供了一些关于指示期望缓存的方法的指导。如果期望缓存,除了 Cache-Control 头之外,还必须存在诸如 ETagLast-Modified 之类的头。

描述如何进行缓存验证和条件请求处理超出了这些指南的范围,因为要求会因服务而异。