将 Vary 设置为服务器用于选择公共页面表示形式的请求头名称。在 Mac 上一次测试一个请求头,比较返回的文档和元数据,并在更改配置前检查默认响应。

本指南适用于通过 Mac Terminal 维护公共文档并检查其交付情况的人员。示例使用一个虚构的“Shape Guide”,同一个 URL 可提供 HTML 或纯文本。任务是找出实际的选择输入,配置相应的响应头,并验证结果。

页面上的 Vary 头应包含什么?

Vary 用于命名可能影响表示形式选择的请求字段,不包括请求方法和目标 URL。其值是请求头名称,例如 Accept,而不是请求的媒体类型。它描述的是选择行为;添加它并不会实现该行为。RFC 9110:HTTP 语义,第 12.5.5 节。

例如,如果服务器使用 Accept 选择 HTML 或纯文本,相关响应字段就是 Vary: Accept。如果交付层还使用 Accept-Encoding 选择压缩或未压缩的输出,也要保留这一维度:Vary: Accept, Accept-Encoding。这些是有条件的示例,并非适用于所有页面的通用配置。内容协商。

实际要问的是:如果这个请求头发生变化,为某个请求选出的缓存响应是否可能不适用于另一个请求?HTTP 缓存要求在无需重新验证即可复用响应之前,被指定的字段必须匹配。某个指定字段缺失时,只会与另一个同样缺失该字段的请求匹配,不会与包含该字段的请求匹配。RFC 9111:HTTP 缓存,第 4.1 节。

先列出选择依据清单。检查应用的文档选择规则,以及之后应用的任何响应转换。结果发生变化是有用的证据,但两个请求的结果相同,并不能证明某个请求头对所有受支持的取值都无关紧要。

修改前先建立请求矩阵

对于虚构的 Shape Guide,假设预期行为是:偏好 HTML 时返回 HTML,偏好纯文本时返回纯文本,未提供 Accept 时返回 HTML。两个版本都包含相同的中性信息:“Circle, square, triangle.”这些是用于说明的要求,并非对某个已发布网站的观察结果。

测试 请求设置 预期结果 检查内容
HTML Accept: text/html HTML 文档 正文、Content-Type、Vary
纯文本 Accept: text/plain 纯文本文档 正文、Content-Type、Vary
默认 不含 Accept 字段 HTML 文档 默认选择和 Vary
重复请求 纯文本请求后再请求 HTML 再次返回 HTML 文档 之前的变体是否影响交付
未使用的输入 更改选择规则中没有的请求头 相同的选定表示形式 是否需要调查该请求头

首次比较时保持压缩设置不变。否则,字节差异可能来自压缩,而不是文档不同。先调查格式选择,再单独调查压缩。Accept 和 Accept-Encoding 的职责不同。内容协商。

记录有意义的差异,不要只依赖文件大小。在此示例中,纯文本响应应包含不带 HTML 标记的形状名称,其媒体类型也应说明这是纯文本。若生成的时间戳发生变化,应先调查,再将每个字节差异视为新变体。

此矩阵是诊断辅助工具,并非完整的协商测试套件。基本情况可用后,再添加页面实际支持的混合偏好和质量值。简单的单一类型请求无法覆盖选择算法的所有分支。

在 Mac Terminal 中捕获可比较的响应

你需要一份包含虚构内容的公共测试文档、它最终的 HTTPS URL,以及一台装有 bash 和 curl 的 Mac。此示例假设测试文档已支持上述 HTML/纯文本行为。它不会创建、上传或发布该文档。

打开 Terminal,粘贴下面的完整代码块。出现提示时,输入虚构文档的 URL。脚本会创建一个新的临时报告目录,并将每个响应正文与其响应头保存在一起;它不会覆盖先前的报告。

bash <<'BASH'
set -eu

printf 'Public synthetic page URL: ' > /dev/tty
IFS= read -r page_url < /dev/tty

case "$page_url" in
  https://*) ;;
  *) printf 'Enter an HTTPS URL.\n' >&2; exit 1 ;;
esac

report_dir=$(mktemp -d "${TMPDIR:-/tmp}/vary-check.XXXXXX")
printf 'Report directory: %s\n' "$report_dir"

for sample in html plain absent html-again; do
  case "$sample" in
    html|html-again) accept_header='Accept: text/html' ;;
    plain) accept_header='Accept: text/plain' ;;
    absent) accept_header='Accept:' ;;
  esac

  curl --disable --silent --show-error --fail \
    --connect-timeout 5 --max-time 20 \
    --header "$accept_header" \
    --header 'Accept-Encoding: identity' \
    --dump-header "$report_dir/$sample.headers" \
    --output "$report_dir/$sample.body" \
    --url "$page_url"
done

printf 'Finished. Responses saved in: %s\n' "$report_dir"
BASH

这里,--header 用于提供受控的请求字段,而 Accept: 会移除 curl 内部生成的 Accept 字段。--dump-header 和 --output 分别保存响应头和正文。连接和传输时间限制会限定每个请求的时长。相关选项见 curl 手册。

如果 curl 报告传输失败,或遇到 --fail 所涵盖的 HTTP 错误,脚本就会停止。重试前先检查报告目录:可能会保留较早的捕获结果,失败的传输也可能留下不完整的文件。脚本会刻意避免跟随重定向,因此请检查每个响应头文件中的状态行。如果捕获到的是重定向,请使用最终文档 URL 并创建一份新报告。curl 手册。

使用脚本打印的路径,在 Finder 中打开报告目录。用纯文本编辑器检查文件。将 html.body 与 plain.body 比较,再将 html.body 与 html-again.body 比较。对 absent.headers 的检查应与对明确偏好响应的检查同样仔细。

将保存的结果视为这组请求序列的观察结果。它们不能证明共享缓存存储了任何内容。如果需要测试缓存复用,请在文档所述的可缓存条件下,通过预期的交付路径重复该序列,并颠倒顺序,让纯文本先请求。

在生成响应的层应用结果

对于示例中的 Shape Guide,格式选择的证据应表明 Accept 是一个选择字段。在生成或最终处理 HTTP 响应的位置配置它。如果另一个层已添加必要的 Vary 成员,在加入格式选择成员时应保留它。最终列表应描述组合后的选择行为。RFC 9110:HTTP 语义。

以下是示例的响应头片段,假设格式选择和压缩协商都已启用。它不是完整的 HTTP 响应,也不是服务器配置文件。

Vary: Accept, Accept-Encoding

在协商出的变体和默认响应中使用相同的预期 Vary 列表,并在适用的 304 Not Modified 响应中保留它。MDN 明确记录了这一一致性要求。请将这些响应路径纳入验证,不要只检查首次成功的 HTML 响应。Vary 头。

添加带有 http-equiv="Vary" 的 meta 元素并非受支持的替代方法。WHATWG 定义了一组特定的 HTML pragma 指令,其中不包括 Vary,并解释说这些指令大多与 HTTP 头相互独立。请使用实际的响应头配置。HTML 标准:Pragma 指令。

如果发布界面无法设置响应头,请检查托管层或应用层是否提供有文档说明的支持。如果没有,对于这个虚构示例,一个可行的替代方案是为 HTML 和纯文本文档使用不同的 URL,并提供稳定的表示形式,而不是在一个 URL 上协商格式。这会移除这一项选择维度;其他转换仍需单独评估。

诊断缺失、多余和误导性的变化

根据捕获结果决定下一步调查内容。以下决策辅助表适用于示例中说明的行为,并非对任何特定托管服务的断言。

观察结果 下一步调查
HTML 和纯文本请求返回不同格式,但 Vary 未包含 Accept 检查最终响应头的组装位置
明确指定的变体包含 Accept,但默认响应未包含 检查回退响应路径
重复的 HTML 请求收到纯文本版本 比较源站选择和经缓存交付的结果
所有请求都返回相同的 HTML 文档 在添加与格式相关的成员前,确认是否存在格式协商
Vary 中出现无关请求头 移除前先找出要求它的选择规则
浏览器调整大小会改变布局,但交付的 HTML 不变 检查这是响应式呈现,还是服务器端选择

默认响应缺少 Vary 的情况尤其值得注意:RFC 9111 说明,如果默认响应省略 Vary,即使存在其他可选项,该响应也可能被选用于后续请求。应与明确指定的变体一起修正默认路径。RFC 9111:HTTP 缓存。

不要用 Vary: * 来代替尚未完成的清单。根据 HTTP 缓存规则,包含该成员的已存储响应永远无法通过匹配测试。它不会指出哪些请求字段用于选择文档。RFC 9111:HTTP 缓存。

在 Terminal 中比较之后,检查浏览器仍然很有用。WHATWG 的 Fetch 标准明确指出,选择 HTTP 缓存响应时会考虑 Vary。请通过正常的浏览器加载路径检查文档,也通过受控的 curl 请求检查。Fetch 标准:HTTP 网络或缓存获取。

使页面交付方式符合 Google 搜索要求

对于因设备而异的页面,要区分响应式设计和动态提供内容。Google 将响应式设计描述为在不同设备上通过相同 URL 提供相同 HTML;而动态提供内容则根据 User-Agent 选择不同 HTML,并依赖 Vary: User-Agent。Google 建议采用响应式设计,因为它更易于实施和维护。移动网站和移动优先索引最佳实践。

因此,虚构的 Shape Guide 如果只是用 CSS 调整相同 HTML 的布局,仅凭这一布局变化并没有理由按 User-Agent 进行变化。如果服务器确实根据设备选择不同的 HTML,就将这些设备情况加入请求矩阵。Google 还建议保持桌面版和移动版的主要内容一致。移动网站和移动优先索引最佳实践。

最后整理一份简明且便于审查的交付记录:文档选择规则、请求矩阵、捕获的响应,以及最终的 Vary 列表。列表中的每个请求头都应有明确的用途,每个受支持的变体都应保持可区分,而默认响应也应遵循相同的交付约定。

来源