HTTP 協議中 Vary 的一些研究
提醒:本文最後更新於 1835 天前,文中所描述的資訊可能已發生改變,請謹慎使用。
經常抓包看 HTTP 請求的同學應該對 Vary 這個響應頭欄位並不陌生,它有什麼用?用 PageSpeed 工具檢查頁面時,經常看到「Specify a Vary: Accept-Encoding header(請指定一個 Vary: Accept-Encoding 標頭)」這樣的建議,為什麼要這樣做?本文記錄我對 Vary 的一些研究,其中就包含這些問題的答案。
HTTP 內容協商
要了解 Vary 的作用,先得了解 HTTP 的內容協商機制。有時候,同一個 URL 可以提供多份不同的文件,這就要求服務端和客戶端之間有一個選擇最合適版本的機制,這就是內容協商。
協商方式有兩種,一種是服務端把文件可用版本列表發給客戶端讓使用者選,這可以使用 300 Multiple Choices 狀態碼來實現。這種方案有不少問題,首先多一次網路往返;其次服務端同一文件的某些版本可能是為擁有某些技術特徵的客戶端準備的,而普通使用者不一定了解這些細節。舉個例子,服務端通常可以將靜態資源輸出為壓縮和未壓縮兩個版本,壓縮版顯然是為支援壓縮的客戶端而準備的,但如果讓普通使用者選,很可能選擇錯誤的版本。
所以 HTTP 的內容協商通常使用另外一種方案:服務端根據客戶端傳送的請求頭中某些欄位自動傳送最合適的版本。可以用於這個機制的請求頭欄位又分兩種:內容協商專用欄位(Accept 欄位)、其他欄位。
首先來看 Accept 欄位,詳見下表:
請求頭欄位 | 說明 | 響應頭欄位 |
---|---|---|
Accept | 告知伺服器傳送何種媒體型別 | Content-Type |
Accept-Language | 告知伺服器傳送何種語言 | Content-Language |
Accept-Charset | 告知伺服器傳送何種字符集 | Content-Type |
Accept-Encoding | 告知伺服器採用何種壓縮方式 | Content-Encoding |
例如客戶端傳送以下請求頭:
Accept:*/* Accept-Encoding:gzip,deflate,sdch Accept-Language:zh-CN,en-US;q=0.8,en;q=0.6
表示它可以接受任何 MIME 型別的資源;支援採用 gzip、deflate 或 sdch 壓縮過的資源;可以接受 zh-CN、en-US 和 en 三種語言,並且 zh-CN 的權重最高(q 取值 0 - 1,最高為 1,最低為 0,預設為 1),服務端應該優先返回語言等於 zh-CN 的版本。
瀏覽器的響應頭可能是這樣的:
Content-Type: text/javascript
Content-Encoding: gzip
表示這個文件確切的 MIME 型別是 text/javascript;文件內容進行了 gzip 壓縮;響應頭沒有 Content-Language 欄位,通常說明返回版本的語言正好是請求頭 Accept-Language 中權重最高的那個。
有時候,上面四個 Accept 欄位並不夠用,例如要針對特定瀏覽器如 IE6 輸出不一樣的內容,就需要用到請求頭中的 User-Agent 欄位。類似的,請求頭中的 Cookie 也可能被服務端用做輸出差異化內容的依據。
由於客戶端和服務端之間可能存在一個或多箇中間實體(如快取伺服器),而快取服務最基本的要求是給使用者返回正確的文件。如果服務端根據不同 User-Agent 返回不同內容,而快取伺服器把 IE6 使用者的響應快取下來,並返回給使用其他瀏覽器的使用者,肯定會出問題 。
所以 HTTP 協議規定,如果服務端提供的內容取決於 User-Agent 這樣「常規 Accept 協商欄位之外」的請求頭欄位,那麼響應頭中必須包含 Vary 欄位,且 Vary 的內容必須包含 User-Agent。同理,如果服務端同時使用請求頭中 User-Agent 和 Cookie 這兩個欄位來生成內容,那麼響應中的 Vary 欄位看上去應該是這樣的:
Vary: User-Agent, Cookie
也就是說 Vary 欄位用於列出一個響應欄位列表,告訴快取伺服器遇到同一個 URL 對應著不同版本文件的情況時,如何快取和篩選合適的版本。
有 BUG 的快取服務
再來看 PageSpeed 的「Specify a Vary: Accept-Encoding header」這個提示,按照上面的說明,Accept-Encoding 屬於內容協商專用欄位,服務端只需要在響應頭中增加 Content-Encoding 欄位,用來指明內容壓縮格式;或者不輸出 Content-Encoding 表明內容未經過壓縮就可以了。而快取伺服器,應該針對不同的 Content-Encoding 快取不同內容,再根據具體請求中的 Accept-Encoding 欄位返回最合適的版本。
但是有些實現得有 BUG 的快取伺服器,會忽略響應頭中的 Content-Encoding,從而可能給不支援壓縮的客戶端返回快取的壓縮版本。有兩個方案可以避免這種情況發生:
- 將響應頭中的 Cache-Control 欄位設為 private,告訴中間實體不要快取它;
- 增加 Vary: Accept-Encoding 響應頭,明確告知快取伺服器按照 Accept-Encoding 欄位的內容,分別快取不同的版本;
通常為了更好的利用中間實體的快取功能,我們都用第二種方案。
對於 css、js 這樣的靜態資源,只要客戶端支援 gzip,服務端應該總是啟用它;同時為了避免有 BUG 的快取伺服器給使用者返回錯誤的版本,還應該輸出 Vary: Accept-Encoding。
Nginx 和 SPDY
通常,上面說的這些工作,Web Server 都可以幫我們搞定。對於 Nginx 來說,下面這個配置可以自動給啟用了 gzip 的響應加上 Vary: Accept-Encoding:
gzip_vary on;
用 curl 驗證我部落格的 js 檔案,響應頭如下:
[email protected]:~$ curl --head https://imququ.com/.../xx.js
HTTP/1.1 200 OK
Server: nginx
Date: Tue, 31 Dec 2013 16:34:48 GMT
Content-Type: application/x-javascript
Content-Length: 66748
Last-Modified: Tue, 31 Dec 2013 14:30:52 GMT
Connection: keep-alive
Vary: Accept-Encoding
ETag: "52c2d51c-104bc"
Expires: Fri, 29 Dec 2023 16:34:48 GMT
Cache-Control: max-age=315360000
Strict-Transport-Security: max-age=31536000
Accept-Ranges: bytes
可以看到,服務端正確輸出了「Vary: Accept-Encoding」,一切正常。
但是用 Chrome 自帶抓包工具看下,這個響應頭卻是這樣:
HTTP/1.1 200 OK
cache-control: max-age=315360000
content-encoding: gzip
content-type: application/x-javascript
date: Tue, 31 Dec 2013 16:35:27 GMT
expires: Fri, 29 Dec 2023 16:35:27 GMT
last-modified: Tue, 31 Dec 2013 14:30:52 GMT
server: nginx
status: 200
strict-transport-security: max-age=31536000
version: HTTP/1.1
我的部落格支援 SPDY/2 協議,用 Chrome 訪問我部落格會走 SPDY,所以上面的響應頭看上有點不同尋常,例如欄位名都變成了小寫;多了 status、version 等欄位,這些變化下次專門介紹(注:見「SPDY 3.1 中的請求 / 響應頭」)。神奇的是儘管服務端沒任何變化,但響應中的 Vary: Accept-Encoding 卻不見了。
SPDY 規定客戶端必須支援壓縮,這意味著 SPDY 伺服器可以直接啟用壓縮而不用關心請求頭中的 Accept-Encoding 欄位。下面這段來自 Nginx 支援的 SPDY/2 協議:
User-agents are expected to support gzip and deflate compression. Regardless of the Accept-Encoding sent by the user-agent, the server may select gzip or deflate encoding at any time. [via]
於是,對於支援 SPDY 的客戶端來說,Vary: Accept-Encoding 沒有用途,Nginx 選擇直接去掉它,可以節省一點流量。curl 或其他不支援 SPDY 協議的客戶端還是走 HTTP 協議,所以看到的響應頭是常規的。
Nginx 的這個做法是否合適一直有爭論,實際上並不是所有支援 SPDY 的 Web Server 都會這麼做。例如即使通過 SPDY 協議訪問 Google 首頁的 js 檔案,依然可以看到 vary: Accept-Encoding:
HTTP/1.1 200 OK
status: 200 OK
version: HTTP/1.1
age: 25762
alternate-protocol: 443:quic
cache-control: public, max-age=31536000
content-encoding: gzip
content-length: 154614
content-type: text/javascript; charset=UTF-8
date: Tue, 31 Dec 2013 23:23:51 GMT
expires: Wed, 31 Dec 2014 23:23:51 GMT
last-modified: Mon, 16 Dec 2013 21:54:35 GMT
server: sffe
vary: Accept-Encoding
x-content-type-options: nosniff
x-xss-protection: 1; mode=block
另外,現階段 Chrome 和 Firefox 都支援 SPDY 協議,但 PageSpeed Chrome 版和 Firefox 版都沒有針對 SPDY 協議做特別處理,所以用它們測試我的部落格,還是會提示「Specify a Vary: Accept-Encoding header」,這有點讓人哭笑不得。不過 PageSpeed 線上版 已經更新規則,估計擴充套件版也快了。如果你有潔癖,可以在 Nginx 配置里加上「add_header vary accept-encoding;」手動輸出 Vary
響應頭。
PS:Vary 在 IE 下有很多坑,使用時要格外小心。網上這部分文章比較多,例如 hax 早年寫的 IE 與 Vary 頭,可以點過去了解下。
--EOF--
發表於 2014-01-01 23:48:33 ,並被新增「 HTTP 、 Nginx 、 SPDY 」標籤 。檢視本文 Markdown 版本 »
提醒:本文最後更新於 1835 天前,文中所描述的資訊可能已發生改變,請謹慎使用。
相關推薦
HTTP 協議中 Vary 的一些研究
提醒:本文最後更新於 1835 天前,文中所描述的資訊可能已發生改變,請謹慎使用。 經常抓包看 HTTP 請求的同學應該對 Vary 這個響應頭欄位並不陌生,它有什麼用?用 PageSpeed 工具檢查頁面時,經常看到「Specify a Vary: Accept-Encoding header
HTTP協議中的一些概念
抓包 在上圖中,可清晰的看到客戶端瀏覽器(ip為192.168.2.33)與伺服器的互動過程: 1)No1:瀏覽器(192
HTTP協議中POST、GET、HEAD、PUT等請求方法以及一些常見錯誤
HTTP請求方法: 常用方法: Get\Post\Head (1)Get方法. 取回請求URL標誌的任何資訊,在瀏覽器的位址列中輸入網址的方式訪問網頁時,瀏覽器採用GET方法向伺服器獲取資源。 (2)Post方法.為請求報文準備資料,即要求被請求伺服器接受附在請求訊息
斐迅面試記錄—Http協議中的Header
art apple -c etag cookie md5 頭信息 一點 zip HTTP Request的Header信息 1、HTTP請求方式 如下表: 說明: 主要使用到“GET”和“POST”。 實例: POST /test/tupian/cm HTTP/1.1
HTTP協議中的通用頭及其功能
通用頭一、Connection指示客戶端與服務器在進行HTTP通信時如何處理TCP連接,如果Connection的值為close,則表示本次HTTP請求響應後結束TCP連接;如果Connection的值為Keep-Alive(HTTP1.1下為默認),則表示TCP連接一直有效二、DateDate通用頭域表明消
http協議中get和post的區別
httpHttp定義了與服務器交互的不同方法,最基本的方法有4種,分別是GET,POST,PUT,DELETEURL全稱是資源描述符,我們可以這樣認 為:一個URL地址,它用於描述一個網絡上的資源,而HTTP中的GET,POST,PUT,DELETE就對應著對這個資源的查 ,改 ,增 ,刪 4個操作。到這裏,
005_關於HTTP協議中的保持連接
註意 o-c 一段 apach user 一段時間 enc 關閉連接 1.5 緣起 中午在群裏討論,用ab測試 一臺只提供靜態文件服務, 不與其他任何系統交互的時候,為什麽也會產生大量的TIME WAIT狀態的。 首先,我們可以簡單的理解,在TCP連接的兩端,誰主動斷開連接
HTTP 協議中 URI 和 URL 有什麽區別?
路徑 西湖區 AI www. str 我們 tar ofo 不能 URI = Universal Resource IdentifierURL = Universal Resource Locator 統一資源標誌符URI就是在某一規則下能把一個資源獨一無二地標識出來。拿
HTTP協議中request報文請求方法和狀態響應碼
cti keep lang one com location 部分 AC url 一個HTTP請求報文由4部分組成: 請求行(request line) 請求頭部(header) 空行 請求數據 下圖給出了請求報文的一般格式:
Http協議中GET和POST請求方法的區別
出場 請求參數 底層 情況下 -type 過去 firefox url sym GET和POST是HTTP請求的兩種基本方法,要說它們的區別,接觸過WEB開發的人都能說出一二。 最直觀的區別就是GET把參數包含在URL中,POST通過request body傳遞
HTTP協議中GET和POST兩種基本請求方法的區別
GET和POST兩種基本請求方法的區別 GET和POST是HTTP請求的兩種基本方法,要說它們的區別,接觸過WEB開發的人都能說出一二。 最直觀的區別就是GET把引數包含在URL中,POST通過request body傳遞引數。 你可能自己寫過無數個G
HTTP 協議中的 Transfer-Encoding和Content-Length區別
原文出處 Transfer-Encoding,是一個 HTTP 頭部欄位,字面意思是「傳輸編碼」。實際上,HTTP 協議中還有另外一個頭部與編碼有關:Content-Encoding(內容編碼)。Content-Encoding 通常用於對實體內容進行壓縮編碼,目的是優化傳輸,例如用 gzi
http協議中,“get”和“post”的區別是什麼?
http協議中,“get”和“post”的區別是什麼? GET和POST是HTTP請求的兩種基本方法,要說它們的區別,接觸過WEB開發的人都能說出一二。 最直觀的區別就是GET把引數包含在URL中,POST通過request body傳遞引數。 你可能自己寫過無數個GET和POST請
轉載 -- HTTP 協議中的 Transfer-Encoding
Jerry Qu -- 牛x的作者 https://imququ.com/post/transfer-encoding-header-in-http.html#comments HTTP 協議中的 Transfer-Encoding 文章目錄 Persiste
轉載 -- HTTP 協議中的 Content-Encoding
Jerry Qu -- 牛x的作者 HTTP 協議中的 Content-Encoding https://imququ.com/post/content-encoding-header-in-http.html 如何壓縮 HTTP 請求正文 https://imququ.com/po
HTTP協議中的請求方法
請求報文中方法的列表 方法 描述 是否包含主體 GET 從伺服器獲取一份文件 否 HEAD 只從伺服器獲取文件的首部 否 P
HTTP協議中PUT和POST使用區別
摘要: PUT是idempotent的方法,而POST不是。 有的觀點認為,應該用POST來建立一個資源,用PUT來更新一個資源;有的觀點認為,應該用PUT來建立一個資源,用POST來更新一個資源;還有的觀點認為可以用PUT和POST中任何一個來做建立或者更新
HTTP 協議中的 Content-Encoding
提醒:本文最後更新於 948 天前,文中所描述的資訊可能已發生改變,請謹慎使用。 Accept-Encoding 和 Content-Encoding 是 HTTP 中用來對「採用何種編碼格式傳輸正文」進行協定的一對頭部欄位。它的工作原理是這樣:瀏覽器傳送請求時,通過 Accept-Encoding
HTTP協議中的Tranfer-Encoding:chunked編碼解析
當不能預先確定報文體的長度時,不可能在頭中包含Content-Length域來指明報文體長度,此時就需要通過Transfer-Encoding域來確定報文體長度。 通常情況下,Transfer-Encoding域的值應當為chunked,表明採用chunked編碼方式來進行報文體的傳輸。chun
談談HTTP協議中的短輪詢、長輪詢、長連線和短連線
--------------------- 作者:左瀟龍 來源:CSDN 原文:https://blog.csdn.net/zuoxiaolong8810/article/details/65441709 版權宣告:本文為博主原創文章,轉載請附上博文連結!