內容協商
在 HTTP 中,內容協商 是一種機制,用於向同一 URI 提供資源的各種表示,以幫助使用者代理指定最適合使用者的表示(例如,文件語言、影像格式或內容編碼)。
注意: 你可以在 WHATWG 的一個 wiki 頁面中找到 HTTP 內容協商的一些缺點。HTML 透過例如 <source> 元素提供了內容協商的替代方案。
內容協商的原則
特定文件稱為資源。當客戶端想要獲取資源時,客戶端透過 URL 請求它。伺服器使用此 URL 選擇可用的變體之一——每個變體稱為一個表示——並向客戶端返回特定的表示。整個資源以及每個表示都有一個特定的 URL。內容協商決定了在呼叫資源時如何選擇特定的表示。客戶端和伺服器之間有幾種協商方式。

最合適的表示透過以下兩種機制之一來識別:
- 客戶端的特定 HTTP 頭(伺服器驅動協商或主動協商),這是協商特定型別資源的標準方式。
- 伺服器的
300(多種選擇)或406(不可接受)、415(不支援的媒體型別)HTTP 響應狀態碼(代理驅動協商或被動協商),用作回退機制。
多年來,其他內容協商方案,例如透明內容協商和 Alternates 頭,已被提出。它們未能獲得關注並被放棄。
伺服器驅動的內容協商
在伺服器驅動內容協商或主動內容協商中,瀏覽器(或任何其他型別的使用者代理)與 URL 一起傳送多個 HTTP 頭。這些頭描述了使用者的首選。伺服器將它們用作提示,內部演算法選擇最佳內容提供給客戶端。如果無法提供合適的資源,它可能會響應 406(不可接受)或 415(不支援的媒體型別),併為它支援的媒體型別設定頭(例如,分別使用 Accept-Post 或 Accept-Patch 用於 POST 和 PATCH 請求)。該演算法是伺服器特有的,未在標準中定義。請參閱 Apache 協商演算法。

HTTP/1.1 標準定義了啟動伺服器驅動協商的標準頭列表(例如 Accept、Accept-Encoding 和 Accept-Language)。儘管 User-Agent 不在此列表中,但有時也用於傳送請求資源的特定表示。然而,這並不總是被認為是好的做法。伺服器使用 Vary 頭來指示它實際用於內容協商的頭(或更準確地說,相關的請求頭),以便 快取可以最佳地工作。
除此之外,還有一個實驗性提案,旨在向可用頭列表中新增更多頭,稱為客戶端提示。客戶端提示宣傳使用者代理執行在何種裝置上(例如,臺式計算機或移動裝置)。
即使伺服器驅動內容協商是就資源的特定表示達成一致的最常用方式,它也有幾個缺點:
- 伺服器不完全瞭解瀏覽器。即使有客戶端提示擴充套件,它也不完全瞭解瀏覽器的功能。與客戶端做出選擇的被動內容協商不同,伺服器的選擇總是有些武斷。
- 來自客戶端的資訊相當冗長(HTTP/2 頭壓縮緩解了此問題)並且存在隱私風險(HTTP 指紋識別)。
- 由於傳送了給定資源的多個表示,共享快取效率較低,伺服器實現也更復雜。
Accept 頭
Accept 頭列出了代理願意處理的媒體資源的 MIME 型別。這是一個逗號分隔的 MIME 型別列表,每個型別都與一個質量因子結合,質量因子是一個引數,指示不同 MIME 型別之間的相對偏好程度。
Accept 頭由瀏覽器或任何其他使用者代理定義,並可能根據上下文而異。例如,獲取 HTML 頁面或影像、影片或指令碼。在位址列中輸入的文件或透過 <img>、<video> 或 <audio> 元素連結的元素不同。瀏覽器可以自由使用他們認為最合適的頭值;常見瀏覽器的預設值的詳盡列表可用。
Accept-CH 頭
注意: 這是名為客戶端提示的實驗性技術的一部分。Chrome 46 或更高版本提供了初步支援。Device-Memory 值在 Chrome 61 或更高版本中。
實驗性的 Accept-CH 列出了伺服器可用於選擇適當響應的配置資料。有效值為:
| 值 | 含義 |
|---|---|
Device-Memory |
表示裝置的近似 RAM 量。此值是透過四捨五入到最接近的 2 的冪並將該數字除以 1024 得出的近似值。例如,512 兆位元組將報告為 0.5。 |
視口寬度 |
指示 CSS 畫素中的佈局視口寬度。 |
寬度 |
指示物理畫素中的資源寬度(換句話說,影像的固有大小)。 |
Accept-Encoding 頭
Accept-Encoding 頭定義了可接受的內容編碼(支援的壓縮)。該值是一個 q 因子列表(例如,br, gzip;q=0.8),指示編碼值的優先順序。預設值 identity 的優先順序最低(除非另有說明)。
壓縮 HTTP 訊息是提高網站效能最重要的途徑之一。它縮小了傳輸資料的大小,並更好地利用了可用頻寬。瀏覽器總是傳送此頭,伺服器應配置為使用壓縮。
Accept-Language 頭
Accept-Language 頭用於指示使用者的語言偏好。它是一個帶有質量因子值的列表(例如,de, en;q=0.7)。預設值通常根據使用者代理的圖形介面語言設定,但大多數瀏覽器允許設定不同的語言偏好。
由於基於配置的熵增加,修改後的值可用於指紋識別使用者。不建議更改它,網站不能信任此值來反映使用者的實際意圖。網站設計師最好避免透過此頭使用語言檢測,因為它可能導致糟糕的使用者體驗。
- 他們應始終提供一種覆蓋伺服器選擇語言的方法,例如,透過在網站上提供語言選單。大多數使用者代理為
Accept-Language頭提供一個適合使用者介面語言的預設值。終端使用者通常不會修改它,因為他們要麼不知道如何修改,要麼根據他們的計算環境無法修改。 - 一旦使用者覆蓋了伺服器選擇的語言,網站就不應再使用語言檢測,而應堅持明確選擇的語言。換句話說,網站只有入口頁面才應使用此頭來選擇適當的語言。
User-Agent 頭
注意: 儘管此頭在選擇內容方面有合法用途,但依賴它來定義使用者代理支援的功能被認為是不好的做法。
User-Agent 頭標識傳送請求的瀏覽器。此字串可能包含一個由空格分隔的產品令牌和註釋列表。
產品令牌是一個名稱,後跟 / 和版本號,例如 Firefox/4.0.1。使用者代理可以包含任意數量的此類令牌。註釋是一個由括號分隔的可選字串。註釋中提供的資訊未標準化,儘管一些瀏覽器會新增多個由 ; 分隔的令牌。
Vary 響應頭
與之前由客戶端傳送的 Accept-* 頭不同,Vary HTTP 頭由 Web 伺服器在其響應中傳送。它指示伺服器在伺服器驅動內容協商階段使用的頭列表。需要 Vary 頭來告知快取決策標準,以便它可以重現。這允許快取正常工作,同時確保將正確的內容提供給使用者。
特殊值 * 表示伺服器驅動的內容協商也使用未在頭中傳達的資訊來選擇適當的內容。
Vary 頭是在 HTTP 1.1 版本中新增的,它允許快取正常工作。要與伺服器驅動內容協商一起工作,快取需要知道伺服器用於選擇傳輸內容的標準。這樣,快取就可以重播演算法,並能夠直接提供可接受的內容,而無需向伺服器發出更多請求。顯然,萬用字元 * 會阻止快取的發生,因為快取無法知道其背後的元素。有關更多資訊,請參閱 HTTP 快取 > 變化的響應。
代理驅動的協商
伺服器驅動協商有一些缺點:它不能很好地擴充套件。協商中每個功能使用一個頭。如果你想使用螢幕尺寸、解析度或其他維度,你需要建立一個新的 HTTP 頭。然後,這些頭必須隨每個請求傳送。如果只有少數幾個頭,這不是問題,但隨著頭數量的增加,訊息大小最終可能會影響效能。傳送的頭越精確,傳送的熵就越多,從而導致更多的 HTTP 指紋識別和相應的隱私問題。
HTTP 允許另一種協商型別:代理驅動協商或被動協商。在這種情況下,當面對模糊請求時,伺服器會發送一個包含可用替代資源連結的頁面。使用者會看到這些資源並選擇要使用的資源。

不幸的是,HTTP 標準沒有指定用於在可用資源之間進行選擇的頁面格式,這使得該過程無法自動化。除了回退到伺服器驅動協商之外,此方法幾乎總是與指令碼結合使用,特別是與 JavaScript 重定向結合使用:在檢查協商標準後,指令碼執行重定向。第二個問題是,需要再發一個請求才能獲取真實資源,從而減慢了資源對使用者的可用性。