3. Storing Responses in Caches (在缓存中存储响应)
除非满足以下所有条件, 否则缓存禁止 (MUST NOT) 存储对请求的响应:
-
请求方法被缓存理解;
-
响应状态码是最终的 (Final) (参见 [HTTP] 的第 15 节);
-
如果响应状态码是 206 或 304, 或存在 must-understand 缓存指令 (参见第 5.2.2.3 节): 缓存理解该响应状态码;
-
响应中不存在 no-store 缓存指令 (参见第 5.2.2.5 节);
-
如果缓存是共享的: private 响应指令不存在或允许共享缓存存储修改后的响应; 参见第 5.2.2.7 节);
-
如果缓存是共享的: 请求中不存在 Authorization 头部字段 (参见 [HTTP] 的第 11.6.2 节) 或存在明确允许共享缓存的响应指令 (参见第 3.5 节); 并且
-
响应至少包含以下之一:
-
public 响应指令 (参见第 5.2.2.9 节);
-
private 响应指令, 如果缓存不是共享的 (参见第 5.2.2.7 节);
-
Expires 头部字段 (参见第 5.3 节);
-
max-age 响应指令 (参见第 5.2.2.1 节);
-
如果缓存是共享的: s-maxage 响应指令 (参见第 5.2.2.10 节);
-
允许缓存的缓存扩展 (参见第 5.2.3 节); 或
-
定义为启发式可缓存 (Heuristically Cacheable) 的状态码 (参见第 4.2.2 节).
-
请注意, 缓存扩展可以覆盖上面列出的任何要求; 参见第 5.2.3 节.
在此上下文中, 如果缓存识别请求方法或响应状态码并实现所有指定的与缓存相关的行为, 则缓存"理解 (Understood)" 该请求方法或响应状态码.
请注意, 在正常操作中, 某些缓存不会存储既没有缓存验证器 (Cache Validator) 也没有明确过期时间的响应, 因为这样的响应通常不值得存储. 然而, 缓存并不禁止存储这样的响应.
3.1 Storing Header and Trailer Fields (存储头部和尾部字段)
缓存在存储响应时必须 (MUST) 包含所有接收到的响应头部字段 -- 包括未识别的字段; 这确保了新的 HTTP 头部字段可以成功部署. 然而, 存在以下例外:
-
Connection 头部字段以及其中列出名称的字段, 根据 [HTTP] 第 7.6.1 节的要求, 在转发消息之前必须删除. 这可以 (MAY) 通过在存储之前删除来实现.
-
同样, 某些字段的语义要求在转发消息之前删除它们, 这可以 (MAY) 通过在存储之前删除来实现; 参见 [HTTP] 第 7.6.1 节的一些示例.
-
no-cache (第 5.2.2.4 节) 和 private (第 5.2.2.7 节) 缓存指令可以具有参数, 分别防止所有缓存和共享缓存存储头部字段.
-
缓存在转发请求时使用的代理特定的头部字段禁止 (MUST NOT) 存储, 除非缓存将代理的身份纳入缓存键. 实际上, 这仅限于 Proxy-Authenticate ([HTTP] 第 11.7.1 节)、Proxy-Authentication-Info ([HTTP] 第 11.7.3 节) 和 Proxy-Authorization ([HTTP] 第 11.7.2 节).
缓存可以 (MAY) 将尾部字段 (Trailer Fields) 与头部字段分开存储或丢弃它们. 缓存禁止 (MUST NOT) 将尾部字段与头部字段合并.
3.2 Updating Stored Header Fields (更新已存储的头部字段)
在多种情况下, 缓存需要从另一个 (通常是更新的) 响应更新已存储响应的头部字段; 例如, 参见第 3.4、4.3.4 和 4.3.5 节.
在这样做时, 缓存必须 (MUST) 将提供的响应中的每个头部字段添加到已存储的响应中, 替换已存在的字段值, 但有以下例外:
-
第 3.1 节中排除存储的头部字段,
-
缓存的已存储响应所依赖的头部字段, 如下所述,
-
由接收方自动处理和删除的头部字段, 如下所述, 以及
-
Content-Length 头部字段.
在某些情况下, 缓存 (尤其是在用户代理中) 存储的是处理接收响应的结果, 而不是响应本身, 更新影响该处理的头部字段可能导致不一致的行为和安全问题. 在这种情况下, 缓存可以 (MAY) 在例外情况下从更新已存储响应中省略这些头部字段, 但应该 (SHOULD) 将这种省略限制在确保已存储响应完整性所必需的字段.
例如, 浏览器可能在接收响应时解码其内容编码 (Content Coding), 从而在其存储的数据与响应的原始元数据之间产生脱节. 使用不同的 Content-Encoding 头部字段更新该存储的元数据将是有问题的. 同样, 浏览器可能存储解析后的 HTML 树而不是响应中接收到的内容; 在这种情况下更新 Content-Type 头部字段将不可行, 因为解析时对格式所做的任何假设现在都将无效.
此外, 某些字段由 HTTP 实现自动处理和删除, 例如 Content-Range 头部字段. 即使实际上没有进行处理, 实现也可以 (MAY) 自动从更新中省略这些头部字段.
请注意, Content-* 前缀并不表示头部字段从更新中省略; 它是 MIME 头部字段的约定, 而不是 HTTP 的约定.
3.3 Storing Incomplete Responses (存储不完整的响应)
如果请求方法是 GET, 响应状态码是 200 (OK), 并且已接收到整个响应头部部分, 则缓存可以 (MAY) 存储不完整的响应 (参见 [HTTP] 第 6.1 节), 前提是将已存储的响应记录为不完整. 同样, 206 (Partial Content) 响应可以 (MAY) 作为不完整的 200 (OK) 响应存储. 然而, 如果缓存不支持 Range 和 Content-Range 头部字段, 或者不理解这些字段中使用的范围单位 (Range Units), 则缓存禁止 (MUST NOT) 存储不完整或部分内容响应.
缓存可以 (MAY) 通过发出后续范围请求 (参见 [HTTP] 第 14.2 节) 并将成功的响应与已存储的响应组合来完成已存储的不完整响应, 如第 3.4 节所定义. 除非响应已完成, 或者请求是部分的并指定了完全在不完整响应内的范围, 否则缓存禁止 (MUST NOT) 使用不完整的响应来回答请求. 缓存禁止 (MUST NOT) 向客户端发送部分响应, 除非使用 206 (Partial Content) 状态码明确标记它.
3.4 Combining Partial Content (组合部分内容)
如果连接过早关闭或请求使用了一个或多个 Range 指定符 (参见 [HTTP] 第 14.2 节), 响应可能仅传输部分表示. 经过几次这样的传输后, 缓存可能已接收到同一表示的多个范围. 如果它们都共享相同的强验证器 (Strong Validator) 并且缓存符合 [HTTP] 第 15.3.7.3 节中的客户端要求, 则缓存可以 (MAY) 将这些范围组合成单个已存储的响应, 并重用该响应来满足后续请求.
当将新响应与一个或多个已存储的响应组合时, 缓存必须 (MUST) 使用新响应中提供的头部字段更新已存储响应的头部字段, 如第 3.2 节所述.
3.5 Storing Responses to Authenticated Requests (存储经过身份验证的请求的响应)
共享缓存禁止 (MUST NOT) 使用对带有 Authorization 头部字段 (参见 [HTTP] 第 11.6.2 节) 的请求的缓存响应来满足任何后续请求, 除非响应包含带有响应指令 (第 5.2.2 节) 的 Cache-Control 字段, 该指令允许共享缓存存储它, 并且缓存符合该响应的该指令的要求.
在本规范中, 以下响应指令具有这种效果: must-revalidate (第 5.2.2.2 节)、public (第 5.2.2.9 节) 和 s-maxage (第 5.2.2.10 节).