跳到主要内容

13. HTTP 中的缓存 (Caching in HTTP)

13 Caching in HTTP

HTTP 通常用于分布式信息系统, 使用响应缓存 (response cache) 可以改善性能. HTTP/1.1 协议包含若干元素, 旨在让缓存尽可能有效地工作. 由于这些元素与协议的其他方面不可分割, 并且彼此相互作用, 因此有必要把 HTTP 的基本缓存设计与 method, header, response code 等详细说明分开描述.

如果缓存不能显著改善性能, 它就没有用处. HTTP/1.1 中缓存的目标是在许多情况下消除发送 request 的需要, 并在许多其他情况下消除发送完整 response 的需要. 前者减少了许多操作所需的网络往返次数; 为此使用 "expiration" 机制 (见第 13.2 节). 后者减少了网络带宽需求; 为此使用 "validation" 机制 (见第 13.3 节).

对性能, 可用性和断连操作的需求要求我们能够放宽语义透明性 (semantic transparency) 这个目标. HTTP/1.1 协议允许 origin server, cache 和 client 在必要时显式降低透明性. 但是, 由于非透明操作可能使非专业用户困惑, 也可能与某些 server application 不兼容 (例如用于订购商品的应用), 协议要求只有在下列情况下才可放宽透明性:

  - 当由 client 或 origin server 放宽时, 只能通过显式的协议级 request 放宽.

- 当由 cache 或 client 放宽时, 只能在向最终用户发出显式 warning 的情况下放宽.

因此, HTTP/1.1 协议提供以下重要元素:

  1. 当所有相关方都要求时, 提供完整语义透明性的协议特性.

2. 允许 origin server 或 user agent 显式请求并控制非透明操作的协议特性.

3. 允许 cache 为未保留所请求语义透明性近似程度的 response 附加 warning 的协议特性.

一个基本原则是, client 必须能够检测到任何潜在的语义透明性放宽.

  Note: server, cache 或 client 的实现者可能会面对本规范未显式讨论的设计决策. 如果某个决策可能影响语义透明性, 除非经过谨慎而完整的分析表明破坏透明性有显著收益, 实现者都应倾向于保持透明性.

13.1.1 缓存正确性 (Cache Correctness)

正确的 cache MUST 使用它所保存的, 适合该 request 的最新 response 来响应 request (见第 13.2.5, 13.2.6 和 13.12 节), 并且该 response 满足以下条件之一:

  1. 通过向 origin server 重新验证该 response, 已经检查它与 origin server 本会返回的 response 等价 (第 13.3 节);

2. 它 "足够新鲜" (见第 13.2 节). 在默认情况下, 这意味着它满足 client, origin server 和 cache 三者中限制最少的新鲜度要求 (见第 14.9 节); 如果 origin server 如此指定, 则只需满足 origin server 的新鲜度要求.

如果某个已存储 response 按 client 和 origin server 二者中最严格的新鲜度要求并非 "足够新鲜", 在经过仔细考虑的情况下, cache MAY 仍然返回该 response, 但必须带有适当的 Warning header (见第 13.1.5 和 14.46 节), 除非这种 response 被禁止 (例如被 "no-store" cache-directive 或 "no-cache" cache-request-directive 禁止; 见第 14.9 节).

3. 它是适当的 304 (Not Modified), 305 (Proxy Redirect), 或 error (4xx 或 5xx) response message.

如果 cache 不能与 origin server 通信, 则只要 response 能够从 cache 正确提供, 正确的 cache SHOULD 按上述方式响应; 否则它 MUST 返回一个指示发生通信失败的 error 或 warning.

如果 cache 收到一个 response (完整 response 或 304 (Not Modified) response), 并且它通常会把该 response 转发给发起 request 的 client, 但收到的 response 已不再新鲜, 则 cache SHOULD 将其转发给该 client, 不添加新的 Warning (但也不移除任何已有的 Warning header). cache SHOULD NOT 仅仅因为 response 在传输途中变陈旧就尝试重新验证它; 这可能导致无限循环. 收到没有 Warning 的陈旧 response 的 user agent MAY 向用户显示 warning 指示.

13.1.2 警告 (Warnings)

每当 cache 返回既非 first-hand 又非 "足够新鲜" (即第 13.1.1 节条件 2 的含义) 的 response 时, 它 MUST 使用 Warning general-header 附加相应 warning. Warning header 以及当前定义的 warning 在第 14.46 节描述. warning 使 client 能够采取适当动作.

Warning MAY 用于其他目的, 包括与缓存相关或无关的目的. 使用 warning 而不是 error status code, 是为了把这些 response 与真正的 failure 区分开.

Warning 被分配三位 warn-code. 第一位表示在成功 revalidation 后, Warning MUST 或 MUST NOT 从已存储的 cache entry 中删除:

1xx 描述 response 的新鲜度或 revalidation 状态的 warning, 因而 MUST 在成功 revalidation 后删除. 1xx warn-code MAY 只由 cache 在验证 cached entry 时生成. client MUST NOT 生成它.

2xx 描述 entity-body 或 entity-header 某些方面的 warning, 这些方面不会通过 revalidation 得到修正 (例如 entity-body 的有损压缩), 因而 MUST NOT 在成功 revalidation 后删除.

代码本身的定义见第 14.46 节.

HTTP/1.0 cache 会缓存 response 中的所有 Warning, 而不会删除第一类 Warning. 传递给 HTTP/1.0 cache 的 response 中的 Warning 携带一个额外的 warning-date 字段, 以防未来的 HTTP/1.1 接收方误以为某个错误缓存的 Warning 仍然有效.

Warning 还携带 warning text. 该文本 MAY 使用任何适当的自然语言 (可能基于 client 的 Accept header), 并包含一个 OPTIONAL 的字符集使用指示.

一个 response MAY 附加多个 warning (由 origin server 或 cache 附加), 包括多个具有相同 code number 的 warning. 例如, server 可以用英文和 Basque 文本提供同一个 warning.

当一个 response 附加多个 warning 时, 向用户显示所有 warning 可能既不实际也不合理. 本版本 HTTP 不为决定显示哪些 warning 及其顺序规定严格优先级规则, 但给出了一些启发式建议.

13.1.3 Cache-Control 机制 (Cache-control Mechanisms)

HTTP/1.1 中的基本缓存机制 (server 指定的过期时间和 validator) 是给 cache 的隐式 directive. 在某些情况下, server 或 client 可能需要向 HTTP cache 提供显式 directive. 为此使用 Cache-Control header.

Cache-Control header 允许 client 或 server 在 request 或 response 中传送多种 directive. 这些 directive 通常会覆盖默认缓存算法. 一般规则是, 如果 header value 之间存在任何明显冲突, 则采用限制最严格的解释 (即最有可能保持语义透明性的解释). 但是, 在某些情况下, cache-control directive 会被显式规定为削弱语义透明性的近似程度 (例如 "max-stale" 或 "public").

cache-control directive 在第 14.9 节详细描述.

13.1.4 显式 User Agent 警告 (Explicit User Agent Warnings)

许多 user agent 允许用户覆盖基本缓存机制. 例如, user agent 可能允许用户指定 cached entity (即使是显式陈旧的 entity) 从不验证. 或者 user agent 可能习惯性地向每个 request 添加 "Cache-Control: max-stale=3600". user agent SHOULD NOT 默认采用非透明行为, 也 SHOULD NOT 默认采用导致缓存异常低效的行为, 但 MAY 通过用户的显式动作被显式配置为这样做.

如果用户覆盖了基本缓存机制, 每当这导致显示的信息可能不满足 server 的透明性要求时 (尤其是已知所显示 entity 陈旧时), user agent SHOULD 向用户显式指示. 由于协议通常允许 user agent 判断 response 是否陈旧, 该指示只需在实际发生这种情况时显示. 该指示不必是对话框; 可以是图标或其他指示器.

如果用户以会异常降低 cache 有效性的方式覆盖了缓存机制, user agent SHOULD 持续向用户指示这种状态, 使用户不会无意中消耗过多资源或遭受过高延迟.

13.1.5 规则和警告的例外 (Exceptions to the Rules and Warnings)

在某些情况下, cache 的操作者 MAY 选择配置它, 即使 client 未请求也返回 stale response. 这个决定不应草率作出, 但出于可用性或性能原因可能是必要的, 尤其是在 cache 与 origin server 连接较差时. 每当 cache 返回 stale response 时, 它 MUST 将其标记为 stale (使用 Warning header), 使 client software 能够提醒用户可能存在问题.

这也允许 user agent 采取步骤获取 first-hand 或 fresh response. 因此, 如果 client 显式请求 first-hand 或 fresh response, cache SHOULD NOT 返回 stale response, 除非出于技术或策略原因无法满足.

13.1.6 客户端控制的行为 (Client-controlled Behavior)

虽然 origin server (以及在较小程度上, intermediate cache 通过贡献 response age) 是过期信息的主要来源, 但在某些情况下, client 可能需要控制 cache 是否不经 validation 就返回 cached response 的决定. client 使用 Cache-Control header 的若干 directive 来做到这一点.

client 的 request MAY 指定它愿意接受的未验证 response 的最大 age; 指定值为零会强制 cache 重新验证所有 response. client MAY 还指定 response 过期前必须保留的最短时间. 这两个选项都会增加对 cache 行为的约束, 因而不会进一步放宽 cache 对语义透明性的近似.

client MAY 还指定它愿意接受 stale response, 直到某个最大陈旧程度. 这会放松对 cache 的约束, 因而可能违反 origin server 对语义透明性的指定约束, 但为支持断连操作, 或在连接性较差时支持高可用性, 可能是必要的.

13.2 过期模型 (Expiration Model)

13.2.1 服务器指定的过期 (Server-Specified Expiration)

当 cache 可以完全避免向 origin server 发出 request 时, HTTP caching 效果最好. 避免 request 的主要机制是 origin server 提供未来的显式过期时间, 指示某个 response MAY 用于满足后续 request. 换言之, cache 可以在不先联系 server 的情况下返回 fresh response.

我们期望 server 为 response 分配未来的显式过期时间, 是因为它认为在达到该过期时间之前, entity 不太可能以语义上显著的方式改变. 只要 server 谨慎选择过期时间, 这通常会保持语义透明性.

过期机制只适用于从 cache 取得的 response, 不适用于立即转发给发起 request 的 client 的 first-hand response.

如果 origin server 希望强制语义透明的 cache 验证每个 request, 它 MAY 分配一个过去的显式过期时间. 这意味着 response 总是 stale, 因而 cache SHOULD 在把它用于后续 request 前验证它. 关于强制 revalidation 的更严格方式见第 14.9.4 节.

如果 origin server 希望强制任何 HTTP/1.1 cache 无论如何配置都验证每个 request, 它 SHOULD 使用 "must-revalidate" cache-control directive (见第 14.9 节).

server 使用 Expires header 或 Cache-Control header 的 max-age directive 来指定显式过期时间.

过期时间不能用于强制 user agent 刷新其显示或重新加载 resource; 它的语义只适用于缓存机制, 并且这些机制只需在发起对该 resource 的新 request 时检查 resource 的过期状态. cache 与 history mechanism 的差异见第 13.13 节.

13.2.2 启发式过期 (Heuristic Expiration)

由于 origin server 并不总是提供显式过期时间, HTTP cache 通常会分配启发式过期时间, 使用其他 header value (例如 Last-Modified time) 的算法来估计合理的过期时间. HTTP/1.1 规范不提供具体算法, 但对其结果施加最坏情况约束. 由于启发式过期时间可能损害语义透明性, 应谨慎使用, 并且我们鼓励 origin server 尽可能提供显式过期时间.

13.2.3 Age 计算 (Age Calculations)

为了知道 cached entry 是否 fresh, cache 需要知道它的 age 是否超过其 freshness lifetime. 第 13.2.4 节讨论如何计算后者; 本节描述如何计算 response 或 cache entry 的 age.

在本讨论中, 我们用术语 "now" 表示 "执行计算的 host 上时钟的当前值". 使用 HTTP 的 host, 尤其是运行 origin server 和 cache 的 host, SHOULD 使用 NTP [28] 或类似协议, 将其时钟同步到全球准确的时间标准.

HTTP/1.1 要求 origin server 尽可能在每个 response 中发送 Date header, 给出生成 response 的时间 (见第 14.18 节). 我们用术语 "date_value" 表示 Date header 的值, 其形式适合算术运算.

HTTP/1.1 使用 Age response-header 来传达从 cache 获得 response message 时估计的 age. Age field value 是 cache 对自 origin server 生成或 revalidate 该 response 以来经过时间的估计.

本质上, Age value 是 response 从 origin server 到当前路径中驻留在各级 cache 内的时间, 加上它在网络路径中传输的时间.

我们用术语 "age_value" 表示 Age header 的值, 其形式适合算术运算.

response 的 age 可以用两种完全独立的方式计算:

  1. now 减去 date_value, 如果本地时钟与 origin server 的时钟同步得足够好. 如果结果为负, 则用零替代.

2. age_value, 如果 response 路径上的所有 cache 都实现 HTTP/1.1.

既然收到 response 时我们有两种独立方式来计算其 age, 就可以将它们组合为:

      corrected_received_age = max(now - date_value, age_value)

只要我们拥有近似同步的时钟或全 HTTP/1.1 路径, 就能得到可靠的 (保守的) 结果.

由于网络引入的延迟, server 生成 response 的时间与下一个 outbound cache 或 client 接收该 response 的时间之间可能经过显著间隔. 如果不修正, 这种延迟可能导致 age 不正确地偏低.

由于导致返回 Age value 的 request 必须先于该 Age value 的生成而发起, 我们可以通过记录 request 发起时间来修正网络引入的延迟. 因此, 收到 Age value 时, MUST 相对于 request 发起时间解释它, 而不是相对于收到 response 的时间. 无论经历多少延迟, 该算法都会产生保守行为. 因此我们计算:

      corrected_initial_age = corrected_received_age
+ (now - request_time)

其中 "request_time" 是引发此 response 的 request 被发送时的时间 (按本地时钟).

cache 收到 response 时的 age 计算算法摘要:

      /*
* age_value
* is the value of Age: header received by the cache with
* this response.
* date_value
* is the value of the origin server's Date: header
* request_time
* is the (local) time when the cache made the request
* that resulted in this cached response
* response_time
* is the (local) time when the cache received the
* response
* now
* is the current (local) time
*/

apparent_age = max(0, response_time - date_value);
corrected_received_age = max(apparent_age, age_value);
response_delay = response_time - request_time;
corrected_initial_age = corrected_received_age + response_delay;
resident_time = now - response_time;
current_age = corrected_initial_age + resident_time;

cache entry 的 current_age 通过把该 cache entry 自上次由 origin server 验证以来经过的时间 (秒) 加到 corrected_initial_age 上计算. 当从 cache entry 生成 response 时, cache MUST 在 response 中包含单个 Age header field, 其值等于该 cache entry 的 current_age.

response 中存在 Age header field 意味着该 response 不是 first-hand. 但是反过来并不成立, 因为 response 中缺少 Age header field 并不意味着它是 first-hand, 除非 request 路径上的所有 cache 都符合 HTTP/1.1 (即旧 HTTP cache 未实现 Age header field).

13.2.4 过期计算 (Expiration Calculations)

为了决定 response 是 fresh 还是 stale, 我们需要比较它的 freshness lifetime 与 age. age 按第 13.2.3 节所述计算; 本节描述如何计算 freshness lifetime, 以及如何判断 response 是否已经过期. 在下面的讨论中, 这些值可以用任何适合算术运算的形式表示.

我们用术语 "expires_value" 表示 Expires header 的值. 我们用术语 "max_age_value" 表示 response 中 Cache-Control header 的 "max-age" directive 携带的秒数的适当值 (见第 14.9.3 节).

max-age directive 优先于 Expires, 因此如果 response 中存在 max-age, 计算很简单:

      freshness_lifetime = max_age_value

否则, 如果 response 中存在 Expires, 计算为:

      freshness_lifetime = expires_value - date_value

注意, 这两个计算都不受时钟偏差影响, 因为所有信息都来自 origin server.

如果 response 中没有 Expires, Cache-Control: max-age 或 Cache-Control: s-maxage (见第 14.9.3 节), 并且 response 不包含其他缓存限制, cache MAY 使用启发式方法计算 freshness lifetime. 如果某个 response 的 age 超过 24 小时且尚未添加 Warning 113, cache MUST 为其附加 Warning 113.

此外, 如果 response 有 Last-Modified time, 启发式过期值 SHOULD 不超过自该时间以来间隔的某个比例. 该比例的典型设置可能是 10%.

判断 response 是否已过期的计算非常简单:

      response_is_fresh = (freshness_lifetime > current_age)

13.2.5 消除过期值歧义 (Disambiguating Expiration Values)

因为过期值是乐观分配的, 两个 cache 可能为同一个 resource 包含不同的 fresh value.

如果正在执行 retrieval 的 client 为某个 request 收到一个非 first-hand response, 而该 request 在它自己的 cache 中已经是 fresh, 并且已有 cache entry 中的 Date header 比新 response 上的 Date 更新, 则 client MAY 忽略该 response. 如果这样做, 它 MAY 使用 "Cache-Control: max-age=0" directive (见第 14.9 节) 重试 request, 以强制与 origin server 检查.

如果 cache 对同一 representation 有两个 fresh response, 且它们具有不同 validator, 它 MUST 使用 Date header 较新的那个. 这种情况可能由于 cache 汇聚来自其他 cache 的 response, 或由于 client 请求 reload 或 revalidation 某个看似 fresh 的 cache entry 而出现.

13.2.6 消除多个响应歧义 (Disambiguating Multiple Responses)

因为 client 可能经由多条路径接收 response, 使某些 response 流经一组 cache, 而其他 response 流经另一组 cache, 所以 client 接收 response 的顺序可能不同于 origin server 发送它们的顺序. 我们希望 client 使用最近生成的 response, 即使较旧的 response 看起来仍然 fresh.

entity tag 和 expiration value 都不能对 response 强加顺序, 因为较晚的 response 可能有意携带较早的 expiration time. Date value 的排序粒度为一秒.

当 client 尝试 revalidate 某个 cache entry, 而它收到的 response 包含的 Date header 看起来早于现有 entry 的 Date 时, client SHOULD 无条件重复 request, 并包含:

      Cache-Control: max-age=0

以强制任何 intermediate cache 直接向 origin server 验证其副本, 或包含:

      Cache-Control: no-cache

以强制任何 intermediate cache 从 origin server 获取新副本.

如果 Date value 相等, 则 client MAY 使用任一 response (或者 MAY 在极其谨慎时请求一个新 response). 如果在同一秒内生成的 response 的 expiration time 重叠, server MUST NOT 依赖 client 能够在这些 response 之间确定性地选择.

13.3 验证模型 (Validation Model)

当 cache 有一个它希望用于响应 client request 的 stale entry 时, 它首先必须与 origin server (或者可能是带有 fresh response 的 intermediate cache) 检查, 看它的 cached entry 是否仍然可用. 我们称这为 "validating" cache entry. 由于我们不想在 cached entry 仍然可用时付出重传完整 response 的开销, 也不想在 cached entry 无效时付出额外一次往返的开销, HTTP/1.1 协议支持使用 conditional method.

支持 conditional method 的关键协议特性与 "cache validator" 有关. 当 origin server 生成完整 response 时, 它会为其附加某种 validator, 并与 cache entry 一起保存. 当 client (user agent 或 proxy cache) 对它已有 cache entry 的 resource 发起 conditional request 时, 它会在 request 中包含相关 validator.

server 随后将该 validator 与 entity 的当前 validator 比较, 如果匹配 (见第 13.3.3 节), 则以特殊 status code (通常是 304 (Not Modified)) 响应, 且不带 entity-body. 否则, 它返回完整 response (包括 entity-body). 因而, 如果 validator 匹配, 就避免传输完整 response; 如果不匹配, 就避免额外一次往返.

在 HTTP/1.1 中, conditional request 与同一 resource 的普通 request 看起来完全相同, 区别只是它携带一个特殊 header (包含 validator), 隐式地把 method (通常是 GET) 变为 conditional.

协议同时包含正向和反向含义的 cache-validating 条件. 也就是说, 可以请求仅当 validator 匹配时执行 method, 也可以请求仅当没有 validator 匹配时执行 method.

  Note: 缺少 validator 的 response 仍然可以被缓存, 并在过期前从 cache 提供, 除非 cache-control directive 明确禁止. 但是, 如果 cache 没有该 entity 的 validator, 就不能执行 conditional retrieval, 这意味着它过期后无法刷新.

13.3.1 Last-Modified 日期 (Last-Modified Dates)

Last-Modified entity-header field value 常用作 cache validator. 简单来说, 如果 entity 自 Last-Modified value 以来未被修改, 则认为 cache entry 有效.

13.3.2 Entity Tag 缓存验证器 (Entity Tag Cache Validators)

ETag response-header field value, 即 entity tag, 提供一个 "opaque" cache validator. 在不便存储修改日期, HTTP date value 的一秒分辨率不足, 或 origin server 希望避免使用修改日期可能引发的某些悖论时, 这可以允许更可靠的 validation.

Entity Tag 在第 3.11 节描述. 与 entity tag 一起使用的 header 在第 14.19, 14.24, 14.26 和 14.44 节描述.

13.3.3 弱验证器和强验证器 (Weak and Strong Validators)

由于 origin server 和 cache 都会比较两个 validator, 以决定它们代表相同 entity 还是不同 entity, 通常会预期如果 entity (entity-body 或任何 entity-header) 以任何方式改变, 则关联 validator 也会改变. 如果确实如此, 我们称该 validator 为 "strong validator".

但是, 可能存在 server 只希望在语义上显著的改变发生时改变 validator, 而不希望在 entity 的非显著方面改变时改变 validator 的情况. 当 resource 改变时并不总是改变的 validator 是 "weak validator".

Entity tag 通常是 "strong validator", 但协议提供了一种机制, 可将 entity tag 标记为 "weak". 可以把 strong validator 理解为每当 entity 的 bit 改变时都会改变的 validator, 而 weak value 则每当 entity 的含义改变时改变. 或者, 可以把 strong validator 理解为特定 entity 标识符的一部分, 而 weak validator 是一组语义等价 entity 的标识符的一部分.

  Note: strong validator 的一个例子是某个整数, 每当 entity 改变时, 它就在稳定存储中递增.

如果 entity 的修改时间以一秒分辨率表示, 它可能是 weak validator, 因为 resource 可能在同一秒内被修改两次.

支持 weak validator 是可选的. 但是, weak validator 允许更高效地缓存等价对象; 例如, 站点上的 hit counter 如果每隔几天或几周更新一次, 在此期间的任何值都可能 "足够好" 而可视为等价.

validator 的一次 "use" 是指 client 生成 request 并在 validating header field 中包含该 validator, 或 server 比较两个 validator.

strong validator 可在任何上下文中使用. weak validator 只能用于不依赖 entity 精确相等的上下文. 例如, 二者都可用于完整 entity 的 conditional GET. 但是, 只有 strong validator 可用于 sub-range retrieval, 因为否则 client 最终可能得到内部不一致的 entity.

client MAY 使用 weak validator 或 strong validator 发出简单 (非 subrange) GET request. client MUST NOT 在其他形式的 request 中使用 weak validator.

HTTP/1.1 协议对 validator 定义的唯一函数是比较. 依据比较上下文是否允许使用 weak validator, 有两个 validator 比较函数:

  - strong comparison function: 要被认为相等, 两个 validator MUST 在各方面完全相同, 且二者 MUST NOT 是 weak.

- weak comparison function: 要被认为相等, 两个 validator MUST 在各方面完全相同, 但其中任一或二者 MAY 被标记为 "weak" 而不影响结果.

entity tag 是 strong, 除非它被显式标记为 weak. 第 3.11 节给出 entity tag 的语法.

在 request 中用作 validator 的 Last-Modified time 是隐式 weak 的, 除非可以用以下规则推断它是 strong:

  - 该 validator 正由 origin server 与 entity 的实际当前 validator 比较, 并且

- 该 origin server 可靠地知道关联 entity 在所呈现 validator 覆盖的那一秒内没有改变两次.

或者:

  - 该 validator 即将由 client 在 If-Modified-Since 或 If-Unmodified-Since header 中使用, 因为 client 拥有关联 entity 的 cache entry, 并且

- 该 cache entry 包含 Date value, 给出 origin server 发送原始 response 的时间, 并且

- 所呈现的 Last-Modified time 至少早于 Date value 60 秒.

或者:

  - 该 validator 正由 intermediate cache 与其 cache entry 中为该 entity 存储的 validator 比较, 并且

- 该 cache entry 包含 Date value, 给出 origin server 发送原始 response 的时间, 并且

- 所呈现的 Last-Modified time 至少早于 Date value 60 秒.

该方法依赖这样一个事实: 如果 origin server 在同一秒内发送了两个不同 response, 但二者具有相同 Last-Modified time, 那么其中至少一个 response 的 Date value 会等于其 Last-Modified time. 任意设定的 60 秒限制用于防范 Date 和 Last-Modified value 由不同时钟生成, 或在 response 准备过程中的略微不同时间生成的可能性. 如果认为 60 秒过短, 实现 MAY 使用大于 60 秒的值.

如果 client 希望对某个值执行 sub-range retrieval, 而它只有 Last-Modified time 且没有 opaque validator, 则只有当 Last-Modified time 在此处描述的意义上是 strong 时, 它 MAY 这样做.

收到 conditional request 的 cache 或 origin server, 如果该 request 不是 full-body GET request, MUST 使用 strong comparison function 来评估条件.

这些规则允许 HTTP/1.1 cache 和 client 安全地对从 HTTP/1.0 server 获得的值执行 sub-range retrieval.

13.3.4 何时使用 Entity Tag 和 Last-Modified 日期的规则 (Rules for When to Use Entity Tags and Last-Modified Dates)

我们采用一组关于 origin server, client 和 cache 何时应使用各种 validator 类型以及用于何种目的的规则和建议.

HTTP/1.1 origin server:

  - SHOULD 发送 entity tag validator, 除非生成它不可行.

- MAY 发送 weak entity tag 而不是 strong entity tag, 如果性能考虑支持使用 weak entity tag, 或者发送 strong entity tag 不可行.

- SHOULD 发送 Last-Modified value, 如果发送它可行, 除非在 If-Modified-Since header 中使用该日期可能导致语义透明性崩溃并引发严重问题.

换言之, HTTP/1.1 origin server 的首选行为是同时发送 strong entity tag 和 Last-Modified value.

为了合法, strong entity tag MUST 在关联 entity value 以任何方式改变时改变. weak entity tag SHOULD 在关联 entity 以语义上显著的方式改变时改变.

  Note: 为提供语义透明的缓存, origin server 必须避免为两个不同 entity 重用某个特定 strong entity tag value, 或为两个语义不同的 entity 重用某个特定 weak entity tag value. 不管 expiration time 如何, cache entry 都可能保留任意长时间, 因而不宜期望 cache 永远不会再次尝试使用它过去某时获得的 validator 来验证 entry.

HTTP/1.1 client:

  - 如果 origin server 提供了 entity tag, MUST 在任何 cache-conditional request 中使用该 entity tag (使用 If-Match 或 If-None-Match).

- 如果 origin server 只提供了 Last-Modified value, SHOULD 在非 subrange cache-conditional request 中使用该值 (使用 If-Modified-Since).

- 如果 HTTP/1.0 origin server 只提供了 Last-Modified value, MAY 在 subrange cache-conditional request 中使用该值 (使用 If-Unmodified-Since:). user agent SHOULD 提供一种禁用此行为的方法, 以应对困难情况.

- 如果 origin server 同时提供了 entity tag 和 Last-Modified value, SHOULD 在 cache-conditional request 中同时使用两个 validator. 这允许 HTTP/1.0 和 HTTP/1.1 cache 都能适当响应.

HTTP/1.1 origin server 收到同时包含 Last-Modified date (例如在 If-Modified-Since 或 If-Unmodified-Since header field 中) 和一个或多个 entity tag (例如在 If-Match, If-None-Match 或 If-Range header field 中) 作为 cache validator 的 conditional request 时, MUST NOT 返回 304 (Not Modified) response status, 除非这样做与 request 中所有 conditional header field 一致.

HTTP/1.1 caching proxy 收到同时包含 Last-Modified date 和一个或多个 entity tag 作为 cache validator 的 conditional request 时, MUST NOT 向 client 返回本地 cached response, 除非该 cached response 与 request 中所有 conditional header field 一致.

  Note: 这些规则背后的通用原则是, HTTP/1.1 server 和 client 应在其 response 和 request 中传输尽可能多的非冗余可用信息. 接收这些信息的 HTTP/1.1 system 将对收到的 validator 作出最保守的假设.

HTTP/1.0 client 和 cache 会忽略 entity tag. 通常, 这些 system 收到或使用的 last-modified value 将支持透明且高效的缓存, 因而 HTTP/1.1 origin server 应提供 Last-Modified value. 在极少数情况下, 如果 HTTP/1.0 system 使用 Last-Modified value 作为 validator 可能导致严重问题, 则 HTTP/1.1 origin server 不应提供它.

13.3.5 非验证条件 (Non-validating Conditionals)

entity tag 背后的原则是, 只有 service author 对 resource 的语义有足够了解, 能够选择适当的 cache validation 机制; 而规定任何比字节相等更复杂的 validator comparison function 都会打开麻烦之门. 因此, 任何其他 header 的比较 (为兼容 HTTP/1.0 而使用 Last-Modified 除外) 从不用于验证 cache entry.

13.4 响应可缓存性 (Response Cacheability)

除非受到 cache-control (第 14.9 节) directive 的具体约束, caching system MAY 始终把 successful response (见第 13.8 节) 存储为 cache entry, MAY 在它 fresh 时不经 validation 返回它, 并 MAY 在成功 validation 后返回它. 如果某个 response 既没有 cache validator, 也没有显式 expiration time, 我们不期望它被缓存, 但某些 cache MAY 违反这种期望 (例如网络连接很少或没有网络连接时). client 通常可以通过比较 Date header 与当前时间, 检测这种 response 是否取自 cache.

  Note: 已知某些 HTTP/1.0 cache 会在不提供任何 Warning 的情况下违反这种期望.

但是, 在某些情况下, cache 保留 entity 或在后续 request 中返回它可能是不适当的. 这可能是因为 service author 认为绝对语义透明性是必要的, 或者出于安全或隐私考虑. 因此提供了某些 cache-control directive, 使 server 能够指示某些 resource entity 或其部分无论其他考虑如何都不得缓存.

注意, 如果先前 request 包含 Authorization header, 第 14.8 节通常会阻止 shared cache 保存并返回对该 request 的 response.

收到 status code 为 200, 203, 206, 300, 301 或 410 的 response MAY 由 cache 存储, 并在受过期机制约束的情况下用于答复后续 request, 除非 cache-control directive 禁止 caching. 但是, 不支持 Range 和 Content-Range header 的 cache MUST NOT 缓存 206 (Partial Content) response.

收到任何其他 status code (例如 status code 302 和 307) 的 response, MUST NOT 在对后续 request 的答复中返回, 除非存在 cache-control directive 或其他 header 明确允许. 例如, 这些包括: Expires header (第 14.21 节); "max-age", "s-maxage", "must-revalidate", "proxy-revalidate", "public" 或 "private" cache-control directive (第 14.9 节).

13.5 从缓存构造响应 (Constructing Responses From Caches)

HTTP cache 的目的是存储为 request 而收到的信息, 以用于响应未来 request. 在许多情况下, cache 只是把 response 的适当部分返回给 requester. 但是, 如果 cache 持有基于先前 response 的 cache entry, 它可能必须把新 response 的部分内容与 cache entry 中持有的内容组合起来.

13.5.1 端到端和逐跳头部 (End-to-end and Hop-by-hop Headers)

为定义 cache 和 non-caching proxy 的行为, 我们把 HTTP header 分为两类:

  - End-to-end header, 传输给 request 或 response 的最终接收方. response 中的 end-to-end header MUST 作为 cache entry 的一部分存储, 并 MUST 在任何由 cache entry 形成的 response 中传输.

- Hop-by-hop header, 只对单个传输层连接有意义, 不由 cache 存储, 也不由 proxy 转发.

以下 HTTP/1.1 header 是 hop-by-hop header:

  - Connection
- Keep-Alive
- Proxy-Authenticate
- Proxy-Authorization
- TE
- Trailers
- Transfer-Encoding
- Upgrade

HTTP/1.1 定义的所有其他 header 都是 end-to-end header.

其他 hop-by-hop header MUST 列在 Connection header 中 (第 14.10 节), 才能引入 HTTP/1.1 (或更高版本).

13.5.2 不可修改头部 (Non-modifiable Headers)

HTTP/1.1 协议的某些特性, 如 Digest Authentication, 依赖某些 end-to-end header 的值. transparent proxy SHOULD NOT 修改 end-to-end header, 除非该 header 的定义要求或明确允许这样做.

transparent proxy MUST NOT 修改 request 或 response 中的以下任何 field, 如果它们尚不存在, 也 MUST NOT 添加这些 field:

  - Content-Location

- Content-MD5

- ETag

- Last-Modified

transparent proxy MUST NOT 修改 response 中的以下任何 field:

  - Expires

但如果这些 field 尚不存在, 它 MAY 添加它们. 如果添加 Expires header, MUST 赋予它与该 response 中 Date header 相同的 field-value.

对于包含 no-transform cache-control directive 的 message, 或任何 request, proxy MUST NOT 修改或添加以下任何 field:

  - Content-Encoding

- Content-Range

- Content-Type

non-transparent proxy MAY 修改或添加不包含 no-transform 的 message 中的这些 field, 但如果这样做, 且 message 中尚无 Warning 214 (Transformation applied), 它 MUST 添加该 Warning (见第 14.46 节).

      Warning: unnecessary modification of end-to-end headers might
cause authentication failures if stronger authentication
mechanisms are introduced in later versions of HTTP. Such
authentication mechanisms MAY rely on the values of header fields
not listed here.

request 或 response 的 Content-Length field 按第 4.4 节规则添加或删除. transparent proxy MUST 保留 entity-body 的 entity-length (第 7.2.2 节), 但 MAY 改变 transfer-length (第 4.4 节).

13.5.3 组合头部 (Combining Headers)

当 cache 向 server 发出 validating request, 而 server 提供 304 (Not Modified) response 或 206 (Partial Content) response 时, cache 随后构造一个 response 发送给发起 request 的 client.

如果 status code 是 304 (Not Modified), cache 使用 cache entry 中存储的 entity-body 作为该外发 response 的 entity-body. 如果 status code 是 206 (Partial Content), 且 ETag 或 Last-Modified header 完全匹配, cache MAY 把 cache entry 中存储的内容与 response 中新收到的内容组合起来, 并把结果用作该外发 response 的 entity-body (见第 13.5.4 节).

cache entry 中存储的 end-to-end header 用于构造的 response, 但有以下例外:

  - 任何已存储的 warn-code 为 1xx 的 Warning header (见第 14.46 节) MUST 从 cache entry 和转发的 response 中删除.

- 任何已存储的 warn-code 为 2xx 的 Warning header MUST 保留在 cache entry 和转发的 response 中.

- 304 或 206 response 中提供的任何 end-to-end header MUST 替换 cache entry 中相应 header.

除非 cache 决定移除该 cache entry, 它还 MUST 用收到的 incoming response 中的相应 header 替换与 cache entry 一起存储的 end-to-end header, Warning header 除外, 如上所述. 如果 incoming response 中某个 header field-name 匹配 cache entry 中多个 header, 所有这些旧 header MUST 被替换.

换言之, incoming response 中收到的 end-to-end header 集合会覆盖 cache entry 中存储的所有相应 end-to-end header (但已存储且 warn-code 为 1xx 的 Warning header 即使未被覆盖也会删除).

  Note: 该规则允许 origin server 使用 304 (Not Modified) 或 206 (Partial Content) response 来更新与同一 entity 或其 sub-range 的先前 response 关联的任何 header, 虽然这样做不一定总有意义或正确. 该规则不允许 origin server 使用 304 (Not Modified) 或 206 (Partial Content) response 完全删除它在先前 response 中提供过的 header.

13.5.4 组合字节范围 (Combining Byte Ranges)

response 可能只传输 entity-body 的某个字节子范围, 原因可能是 request 包含一个或多个 Range specification, 也可能是 connection 过早中断. 经过多次这样的传输后, cache 可能已收到同一 entity-body 的多个 range.

如果 cache 为某个 entity 存有非空 subrange 集合, 而 incoming response 又传输另一个 subrange, 则在同时满足以下两个条件时, cache MAY 将新 subrange 与现有集合组合:

  - incoming response 和 cache entry 都有 cache validator.

- 两个 cache validator 使用 strong comparison function 匹配 (见第 13.3.3 节).

如果任一要求未满足, cache MUST 只使用最近的 partial response (基于每个 response 传输的 Date value; 如果这些值相等或缺失, 则使用 incoming response), 并 MUST 丢弃其他 partial information.

13.6 缓存协商响应 (Caching Negotiated Responses)

使用 server-driven content negotiation (第 12.1 节) 时, 如 response 中存在 Vary header field 所示, cache 将 response 用于后续 request 的条件和过程会改变. server 对 Vary header field 的使用见第 14.44 节.

server SHOULD 使用 Vary header field 通知 cache, 在受 server-driven negotiation 约束的 cacheable response 的多个 representation 之间进行选择时使用了哪些 request-header field. Vary field value 命名的 header field 集合称为 "selecting" request-headers.

当 cache 收到后续 request, 其 Request-URI 指定一个或多个包含 Vary header field 的 cache entry 时, 除非新 request 中存在的所有 selecting request-header 都匹配原始 request 中相应的已存储 request-header, 否则 cache MUST NOT 使用这样的 cache entry 为新 request 构造 response.

如果且仅如果第一个 request 中的 selecting request-header 可以通过在对应 BNF 允许的位置添加或移除线性空白 (LWS), 和/或按照第 4.2 节关于 message header 的规则合并具有相同 field name 的多个 message-header field, 转换为第二个 request 中的 selecting request-header, 则定义这两个 request 的 selecting request-header 匹配.

Vary header field-value 为 "*" 总是匹配失败, 对该 resource 的后续 request 只能由 origin server 正确解释.

如果 cached entry 的 selecting request header field 与新 request 的 selecting request header field 不匹配, 则 cache MUST NOT 使用 cached entry 满足 request, 除非它首先把新 request 作为 conditional request 转发给 origin server, 且 server 以 304 (Not Modified) 响应, 并包含指示要使用哪个 entity 的 entity tag 或 Content-Location.

如果 cached representation 被分配了 entity tag, 则转发的 request SHOULD 是 conditional 的, 并在 If-None-Match header field 中包含该 resource 的所有 cache entry 的 entity tag. 这向 server 传达 cache 当前持有的 entity 集合, 使得如果这些 entity 中任何一个匹配所请求 entity, server 就可以在其 304 (Not Modified) response 中使用 ETag header field 告诉 cache 哪个 entry 是适当的. 如果新 response 的 entity-tag 匹配某个现有 entry 的 entity-tag, 新 response SHOULD 用于更新该现有 entry 的 header field, 并且结果 MUST 返回给 client.

如果任何现有 cache entry 对关联 entity 只包含 partial content, 则除非 request 请求的 range 可由该 entry 完全满足, 否则其 entity-tag SHOULD NOT 包含在 If-None-Match header field 中.

如果 cache 收到一个 successful response, 其 Content-Location field 匹配同一 Request-URI 的现有 cache entry, 其 entity-tag 不同于现有 entry, 且其 Date 比现有 entry 更新, 则该现有 entry SHOULD NOT 用于响应未来 request, 并 SHOULD 从 cache 中删除.

13.7 共享缓存和非共享缓存 (Shared and Non-Shared Caches)

出于安全和隐私原因, 必须区分 "shared" cache 和 "non-shared" cache. non-shared cache 是只能由单个用户访问的 cache. 在这种情况下, accessibility SHOULD 由适当的安全机制强制执行. 所有其他 cache 都被视为 "shared". 本规范的其他章节对 shared cache 的操作施加某些约束, 以防止隐私丧失或访问控制失效.

13.8 错误或不完整响应的缓存行为 (Errors or Incomplete Response Cache Behavior)

cache 收到不完整 response (例如数据字节数少于 Content-Length header 中指定的字节数) 时, MAY 存储该 response. 但是, cache MUST 将其视为 partial response. partial response MAY 按第 13.5.4 节所述组合; 结果可能是 full response, 也可能仍是 partial. cache MUST NOT 在未使用 206 (Partial Content) status code 显式标记的情况下向 client 返回 partial response. cache MUST NOT 使用 200 (OK) status code 返回 partial response.

如果 cache 在尝试 revalidate 某个 entry 时收到 5xx response, 它 MAY 将该 response 转发给发起 request 的 client, 或者表现得好像 server 未响应. 在后一种情况下, 它 MAY 返回先前收到的 response, 除非 cached entry 包含 "must-revalidate" cache-control directive (见第 14.9 节).

13.9 GET 和 HEAD 的副作用 (Side Effects of GET and HEAD)

除非 origin server 显式禁止缓存其 response, 对任何 resource 应用 GET 和 HEAD method SHOULD NOT 产生在这些 response 取自 cache 时会导致错误行为的副作用. 它们 MAY 仍然有副作用, 但 cache 在其缓存决策中不要求考虑这些副作用. cache 始终应遵守 origin server 对 caching 的显式限制.

我们注意到该规则有一个例外: 由于某些应用传统上使用带 query URL (即 rel_path 部分包含 "?") 的 GET 和 HEAD 来执行有显著副作用的操作, 除非 server 提供显式 expiration time, 否则 cache MUST NOT 将对此类 URI 的 response 视为 fresh. 这具体意味着, 来自 HTTP/1.0 server 的此类 URI response SHOULD NOT 取自 cache. 相关信息见第 9.1.1 节.

13.10 更新或删除后的失效 (Invalidation After Updates or Deletions)

在 origin server 上对 resource 执行某些 method 的效果, 可能导致一个或多个现有 cache entry 以非透明方式失效. 也就是说, 虽然它们可能继续 "fresh", 但它们不能准确反映 origin server 对该 resource 上新 request 本会返回的内容.

HTTP 协议无法保证所有此类 cache entry 都被标记为 invalid. 例如, 在 origin server 导致变更的 request 可能未经过存有 cache entry 的 proxy. 但是, 若干规则有助于降低错误行为的可能性.

在本节中, 短语 "invalidate an entity" 表示 cache 将从其存储中移除该 entity 的所有实例, 或将这些实例标记为 "invalid", 并要求在它们可用于响应后续 request 前进行强制 revalidation.

某些 HTTP method MUST 导致 cache 使某个 entity 失效. 该 entity 是 Request-URI 引用的 entity, 或 Location 或 Content-Location header (如果存在) 引用的 entity. 这些 method 是:

  - PUT

- DELETE

- POST

为防止 denial of service attack, 基于 Location 或 Content-Location header 中 URI 的 invalidation MUST 仅在 host 部分与 Request-URI 中相同时执行.

对于它不理解的 method, 经过这些 request 的 cache SHOULD 使 Request-URI 引用的任何 entity 失效.

13.11 强制写穿 (Write-Through Mandatory)

所有可能导致 origin server resource 被修改的 method MUST 写穿到 origin server. 当前这包括除 GET 和 HEAD 之外的所有 method. cache 在将 request 传输给 inbound server 并收到 inbound server 的对应 response 之前, MUST NOT 对来自 client 的此类 request 作出 reply. 这不阻止 proxy cache 在 inbound server 发送最终 reply 之前发送 100 (Continue) response.

HTTP/1.1 不允许另一种方案 (称为 "write-back" 或 "copy-back" caching), 原因是提供一致更新很困难, 以及在 write-back 前 server, cache 或 network failure 会引发问题.

13.12 缓存替换 (Cache Replacement)

如果在同一 resource 的任何现有 response 已被缓存时, 从该 resource 收到新的 cacheable response (见第 14.9.2, 13.2.5, 13.2.6 和 13.8 节), cache SHOULD 使用新 response 答复当前 request. 它 MAY 将其插入 cache storage, 并且如果它满足所有其他要求, MAY 用它响应任何未来 request, 这些 request 以前会导致返回旧 response. 如果它把新 response 插入 cache storage, 则适用第 13.5.3 节的规则.

  Note: Date header value 早于现有 cached response 的新 response 不可缓存.

13.13 历史列表 (History Lists)

user agent 通常有 history mechanism, 例如 "Back" button 和 history list, 可用于重新显示同一 session 中先前取回的 entity.

history mechanism 与 cache 不同. 尤其是, history mechanism SHOULD NOT 试图展示 resource 当前状态的语义透明视图. 相反, history mechanism 旨在准确展示用户在取回该 resource 时看到的内容.

默认情况下, expiration time 不适用于 history mechanism. 如果 entity 仍在 storage 中, history mechanism SHOULD 显示它, 即使该 entity 已过期, 除非用户已专门配置 agent 刷新过期的 history document.

这不应被解释为禁止 history mechanism 告诉用户某个视图可能已经 stale.

  Note: 如果 history list mechanism 不必要地阻止用户查看 stale resource, 这往往会迫使 service author 避免在本来希望使用 HTTP expiration control 和 cache control 时使用它们. service author 可能认为, 当用户使用导航控件 (如 BACK) 查看先前获取的 resource 时, 不向用户呈现 error message 或 warning message 很重要. 即使有时此类 resource 不应被缓存, 或应快速过期, user interface 方面的考虑也可能迫使 service author 转而采用其他防止 caching 的手段 (例如 "once-only" URL), 以免受到 history mechanism 工作不当的影响.