将 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 列表。列表中的每个请求头都应有明确的用途,每个受支持的变体都应保持可区分,而默认响应也应遵循相同的交付约定。
来源
- RFC 9110:HTTP 语义 — IETF / RFC 编辑部。
- RFC 9111:HTTP 缓存 — IETF / RFC 编辑部。
- Fetch 标准 — WHATWG。
- HTML 标准 — WHATWG。
- curl — 使用方法 — curl 项目。
- Vary 头 — MDN Web Docs。
- 内容协商 — MDN Web Docs。
- 移动网站和移动优先索引最佳实践 — Google Search Central。