將 Vary 設為伺服器用來選擇公開頁面表示形式的請求標頭名稱。在 Mac 上一次測試一個標頭,比較傳回的文件與中繼資料,並在變更設定前檢查預設回應。
本指南適用於透過 Mac 終端機維護公開文件並檢查其傳送情況的人。範例使用虛構的「Shape Guide」,同一個網址可提供 HTML 或純文字。目標是找出實際的選擇輸入、設定相應的回應標頭,並驗證結果。
頁面的 Vary 標頭應包含哪些內容?
Vary 會列出可能影響表示形式選擇的請求欄位,除了請求方法與目標網址之外。其值是標頭名稱,例如 Accept,而不是要求的媒體類型。它描述選擇行為;加入此標頭並不會實作該行為。RFC 9110: HTTP Semantics, section 12.5.5.
例如,如果伺服器使用 Accept 選擇 HTML 或純文字,相關的回應欄位就是 Vary: Accept。如果傳送層也使用 Accept-Encoding 選擇壓縮或未壓縮的輸出,則應保留這個維度:Vary: Accept, Accept-Encoding。這些是有條件的範例,不是適用於每個頁面的通用設定。內容協商。
實際要問的是:如果這個標頭改變,為一個請求選出的快取回應是否可能不適用於另一個請求?HTTP 快取要求,在未重新驗證而重複使用回應前,必須確認指定欄位相符。缺少的指定欄位只會與另一個同樣缺少該欄位的請求相符,不會與欄位存在的請求相符。RFC 9111: HTTP Caching, section 4.1。
先列出選擇規則清單。檢查應用程式的文件選擇規則,以及之後套用的任何回應轉換。結果有所改變是有用的證據,但兩個請求得到相同結果,並不能證明某個標頭對所有支援值都無關緊要。
編輯前先建立請求矩陣
以虛構的 Shape Guide 為例,假設預期行為是:偏好 HTML 時提供 HTML,偏好純文字時提供純文字,而缺少 Accept 時則提供 HTML。兩種版本都包含相同的中性資訊:「圓形、正方形、三角形」。這些是說明用的需求,不是對任何已發布網站的觀察結果。
| 測試 | 請求設定 | 預期結果 | 檢查內容 |
|---|---|---|---|
| HTML | Accept: text/html |
HTML 文件 | 內文、Content-Type、Vary |
| 純文字 | Accept: text/plain |
純文字文件 | 內文、Content-Type、Vary |
| 預設 | 不含 Accept 欄位 |
HTML 文件 | 預設選擇與 Vary |
| 重複測試 | 純文字之後再請求 HTML | 再次得到 HTML 文件 | 先前的變體是否影響傳送 |
| 未使用的輸入 | 變更選擇規則未涵蓋的標頭 | 相同的表示形式 | 是否需要調查該標頭 |
第一次比較時,請固定壓縮設定。否則,位元組差異可能來自壓縮,而非文件不同。先調查格式選擇,再單獨檢查壓縮。這項區分反映了 Accept 與 Accept-Encoding 各自不同的作用。內容協商。
記錄有意義的差異,不要只依賴檔案大小。在這個範例中,純文字回應應包含形狀名稱而不含 HTML 標記,且媒體類型應指出它是純文字。如果產生的時間戳記有所變動,在將每個位元組差異視為新變體前,應先調查原因。
這個矩陣是診斷輔助工具,不是完整的協商測試套件。基本案例正常後,請加入頁面實際支援的混合偏好與品質值。單一類型的簡單請求無法涵蓋選擇演算法的所有分支。
在 Mac 終端機擷取可比較的回應
你需要一份含有虛構內容的公開測試文件、其最終 HTTPS 網址,以及已安裝 bash 和 curl 的 Mac。此範例假設測試文件已支援上述 HTML/純文字行為。它不會建立、上傳或發布該文件。
開啟 Terminal,貼上以下完整區塊。系統提示時,輸入虛構測試文件的網址。指令碼會建立新的暫存報告目錄,並將各回應本文與標頭存放在一起;不會覆寫先前的報告。
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 錯誤,指令碼就會停止。重試前請先檢查回報的目錄:先前的擷取檔案可能仍在,失敗的傳輸也可能留下不完整檔案。指令碼刻意不跟隨重新導向,因此請檢查每個標頭檔中的狀態列。如果擷取到重新導向,請改用最終文件網址並建立新的報告。curl 手冊。
使用指令碼列印的路徑,在 Finder 中開啟報告目錄。用純文字編輯器檢查檔案。比較 html.body 與 plain.body,接著比較 html.body 與 html-again.body。也要仔細檢查 absent.headers,不能只看明確指定偏好的回應。
將儲存的結果視為這組請求序列的觀察資料。它們無法證明共用快取實際儲存了任何內容。如果需要測試快取重複使用,請在文件記載的可快取條件下,透過預定的傳送路徑重複此序列,並反轉順序,先請求純文字。
在產生回應的層級套用結果
對此示範用的 Shape Guide 而言,格式選擇證據應能支持將 Accept 列為選擇欄位。請在產生或定稿 HTTP 回應的地方設定。如果另一個層級已加入必要的 Vary 成員,加入格式選擇成員時要保留它。最終清單應描述合併後的選擇行為。RFC 9110: HTTP Semantics。
以下是格式選擇與壓縮協商都啟用時,適用於此範例的回應標頭片段。它不是完整的 HTTP 回應,也不是伺服器設定檔。
Vary: Accept, Accept-Encoding
在協商變體與預設回應中使用相同的預期 Vary 清單,並在適用的 304 Not Modified 回應中保留它。MDN 明確記載了這項一致性要求。請將這些回應路徑納入驗證,不要只檢查第一個成功的 HTML 回應。Vary 標頭。
加入 meta 元素並使用 http-equiv="Vary",不是受支援的替代做法。WHATWG 定義了一組特定的 HTML pragma 指令,其中不包含 Vary,並說明這些指令大多與 HTTP 標頭分開處理。請使用實際的回應標頭設定。HTML Standard: Pragma directives。
如果發布介面無法設定回應標頭,請檢查主機或應用程式層是否提供有文件說明的支援方式。如果無法使用這類功能,這個虛構範例的實用替代方案是讓 HTML 與純文字文件使用不同網址,並維持穩定的表示形式,而不是在同一網址協商格式。這會移除該項特定的選擇維度;其他轉換仍需各自評估。
診斷缺少、過多或誤導性的變化
根據擷取結果決定下一步調查方向。以下判斷輔助表適用於範例中所述的行為;它不代表任何特定主機服務的實際狀況。
| 觀察結果 | 下一步調查 |
|---|---|
HTML 與純文字請求產生不同格式,但 Vary 未列出 Accept |
檢查最終回應標頭的組合位置 |
明確指定偏好的變體包含 Accept,但預設回應未包含 |
檢查回退回應路徑 |
| 重複的 HTML 請求收到純文字版本 | 比較來源端的選擇與透過快取傳送的結果 |
| 所有請求都產生相同的 HTML 文件 | 在加入與格式相關的成員前,確認是否存在格式協商 |
Vary 出現無關標頭 |
移除前先找出需要該標頭的選擇規則 |
| 瀏覽器調整大小會改變版面配置,但傳送的 HTML 未改變 | 確認這是回應式呈現,而非伺服器端選擇 |
預設回應缺少標頭的情況特別值得注意:RFC 9111 說明,如果預設回應省略 Vary,該回應可能會在之後的請求中被選用,即使當時已有其他替代版本。除了明確指定偏好的變體,也要修正預設路徑。RFC 9111: HTTP Caching。
不要用 Vary: * 來掩蓋尚未完成的盤點。依據 HTTP 快取規則,包含該成員的已儲存回應永遠無法通過比對。它也不會指出哪些請求欄位會選擇你的文件。RFC 9111: HTTP Caching。
在終端機比較之後,檢查瀏覽器仍然很有用。WHATWG 的 Fetch Standard 明確指出,選擇 HTTP 快取回應時會將 Vary 納入考量。除了使用受控的 curl 請求,也要透過瀏覽器的一般載入路徑檢查文件。Fetch Standard: HTTP-network-or-cache fetch。
讓頁面傳送方式符合 Google 搜尋的要求
對於依裝置而異的頁面,請區分回應式設計與動態服務。Google 說明,回應式設計會在不同裝置上透過相同網址提供相同 HTML;動態服務則會使用 User-Agent 選擇不同 HTML,並依賴 Vary: User-Agent。Google 建議採用回應式設計,因為它較容易實作與維護。Mobile site and mobile-first indexing best practices。
因此,虛構的 Shape Guide 若只是用 CSS 重新排列相同的 HTML,僅憑這項版面變動,並不需要依 User-Agent 變化。如果伺服器確實依裝置選擇不同 HTML,請將這些裝置案例加入請求矩陣。Google 也建議,桌面版與行動版之間應維持主要內容一致。Mobile site and mobile-first indexing best practices。
最後整理一份簡短、方便審查的傳送紀錄:文件選擇規則、請求矩陣、擷取的回應,以及最終的 Vary 清單。清單中的每個標頭都應有明確用途,每個支援的變體都應能區分,而預設回應也應遵循相同的傳送約定。
來源
- RFC 9110: HTTP Semantics — IETF / RFC Editor。
- RFC 9111: HTTP Caching — IETF / RFC Editor。
- Fetch Standard — WHATWG。
- HTML Standard — WHATWG。
- curl — How To Use — curl 專案。
- Vary 標頭 — MDN Web Docs。
- 內容協商 — MDN Web Docs。
- Mobile site and mobile-first indexing best practices — Google Search Central。