HTTP 協(xié)議詳解

理解 HTTP 協(xié)議對構(gòu)建網(wǎng)絡(luò)應(yīng)用是一個非逞吩辏基礎(chǔ)的要求,比如爬蟲類程序枯芬,必須深入理解 Request 和 Resonse 各首部信息(當(dāng)然宵荒,這個前提是建立在對方站點完全遵循協(xié)議)。若是網(wǎng)站類程序苔悦,更需要理解這些含義轩褐,因為客戶端往往就是瀏覽器,一般來講都會嚴格遵循協(xié)議玖详。參考:httpwg(全面權(quán)威)把介、MDN勤讽、wiki協(xié)議

一拗踢、基礎(chǔ)(雜項)首部

Request 首部

請求主機:端口
Host: domain.com:8080

請求來源的 主機:端口 和 頁面URL
Origin: http://domain.org:8080
Referer: http://domain.org:8080/page

連接信息:是否允許復(fù)用 TCP 連接:HTTP 是建立在 TCP 之上的脚牍,告知本次請求結(jié)束后,是否關(guān)閉 TCP巢墅,還是在請求下一個頁面時直接復(fù)用 TCP 通道诸狭,若復(fù)用,還可以設(shè)置保持連接時長砂缩、最大復(fù)用次數(shù)作谚。
該首部在使用 HTTP /2 協(xié)議時不會(得)發(fā)送,總是 keep-alive庵芭。
Connection: close
Connection: keep-alive
Keep-Alive: timeout=5, max=1000

請求報文的創(chuàng)建時間,一般即為當(dāng)前時間雀监,幾乎沒有客戶端會發(fā)送該首部
Date: Wed, 21 Oct 2015 07:28:00 GMT

客戶端信息双吆,可能是瀏覽器/App/蜘蛛等任何可用信息
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows Phone OS 7.5; Trident/5.0; IEMobile/9.0)

比如爬蟲程序,若請求較為頻繁会前,可攜帶該首部好乐,便于對方網(wǎng)站聯(lián)系你
From: webmaster@example.org

通知服務(wù)端,客戶端即將傳輸大文件瓦宜,希望服務(wù)端返回 100 響應(yīng)蔚万,以便客戶端繼續(xù);
沒有瀏覽器會使用這個临庇,但如 cURL 等會使用反璃,服務(wù)端可以響應(yīng):同意(100)、拒絕(4XX)
Expect: 100-continue

是否禁止追蹤 (君子協(xié)定假夺,需要看服務(wù)端什么態(tài)度)
DNT: 0 (允許) 或 DNT: 1 (禁止)

Response 首部

連接信息淮蜈,參見 Request 首部說明
Connection: close | keep-alive
Keep-Alive: timeout=5, max=1000

報文的創(chuàng)建時間,根據(jù) RFC 7231已卷,除非是服務(wù)器時鐘不準確梧田,都必須發(fā)送該首部
Date: Wed, 21 Oct 2015 07:28:00 GMT

在無法處理 Request method 時發(fā)送 405 響應(yīng),通過該首部告知客戶端可處理的 method (可以為空)
Allow: GET, POST, HEAD

網(wǎng)站維護侧蘸,當(dāng) Response 為 503 或 301 響應(yīng)時裁眯,可告知再次可訪問的時間;對于瀏覽器客戶端用處不大讳癌,但對于蜘蛛等自動程序穿稳,該首部有一定意義。
Retry-After: Wed, 21 Oct 2015 07:28:00 GMT
Retry-After: 120

處理請求的軟件或者產(chǎn)品(或組件產(chǎn)品)的名稱
Server: Apache/2.4.1 (Unix)

服務(wù)端處理請求的各種調(diào)試信息析桥,生產(chǎn)版切勿發(fā)送司草,有安全隱患
Server-Timing: cpu;dur=2.4, total;dur=123.4

服務(wù)端應(yīng)用程序信息艰垂,生產(chǎn)版切勿發(fā)送,有安全隱患
X-Powered-By: PHP/7.2

語義上與 Link 相同埋虹,目前仍處于 草案 階段猜憎,感覺這個不實用,前后端分離搞了這么多年搔课,這個豈不是開倒車了胰柑。但可能對于某些全局通用的 Link 有那么一丟丟作用,可作為通用的缺省 Link爬泥。另外對于追求極致體驗的柬讨,可以幫助瀏覽器更快(先于頁面或同時加載)請求 CSS 、JS 資源袍啡,快速完成頁面渲染踩官,相當(dāng)于在頁面加載完成前,預(yù)加載所需資源境输。
Link: <favicon.ico>; rel="icon", <main.css>; rel="stylesheet"

來源信息控制蔗牡,可禁止跟蹤(在當(dāng)前 Response 頁面打開外鏈不發(fā)送 referrer);一般不使用 header 發(fā)送嗅剖,而是通過頁面的 meta 等設(shè)定(詳情)辩越。https -> http 跳轉(zhuǎn)默認不發(fā)送,http 若是廣告主信粮,可能就需要設(shè)置該首部讓瀏覽器發(fā)送以便對方統(tǒng)計黔攒。
Referrer-Policy: no-referrer | unsafe-url | .....

通常為一些 Css / Js 資源,指明 SourceMap 地址用于幫助在瀏覽器調(diào)試强缘,但由于資源本事直接支持設(shè)置 SourceMap督惰,所以該首部很不常用。非標準協(xié)議欺旧,但 支持度 還不錯姑丑。
SourceMap: /path/to/file.js.map

針對瀏覽器的,設(shè)置允許 調(diào)試 加載信息的域名辞友。比如當(dāng)前 response 為一張圖片栅哀,嵌入到其他網(wǎng)站,那么就可以使用該首部來 允許/禁止 該站點調(diào)試称龙。
Timing-Allow-Origin: *
Timing-Allow-Origin: https://domain.com

針對瀏覽器的留拾,加載頁面實體時就開始預(yù)讀取圖片、靜態(tài)資源鲫尊、甚至是鏈接的 DNS 以加快渲染痴柔;該指令也可通過 meta 設(shè)定:參見
X-DNS-Prefetch-Control: on / X-DNS-Prefetch-Control: off

針對 IE 瀏覽器,等同于 <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
X-UA-Compatible: IE=Edge,chrome=1

告知跟蹤情況疫向,詳情
Tk: N

二咳蔚、HTTP 認證

Response: 未認證請求豪嚎,服務(wù)端響應(yīng) 401,并告知認證方式
WWW-Authenticate: <type> realm=<realm>

Request: 客戶端攜帶認證首部進行訪問
Authorization: <type> <credentials>

Response: 服務(wù)端為代理服務(wù)器谈火,且也需要認證侈询;響應(yīng) 407,告知客戶端認證方式
Proxy-Authenticate: <type> realm=<realm>

Request: 同樣的糯耍,客戶端攜帶代理服務(wù)器的認證信息
Proxy-Authorization: <type> <credentials>

邏輯較為簡單扔字,更多信息可 參考

三、內(nèi)容協(xié)商

內(nèi)容格式: Accept & Accept-Charset / Content-Type & Accept-Patch

Request: 表明客戶端可處理的格式温技,可能的單個革为、多個 或 任意
Accept: text/html
Accept: text/html, application/xhtml+xml, image/*, application/xml;q=0.9, */*;q=0.8
Accept: */*

Request: 表明客戶端可處理的字符集
Accept-Charset: utf-8, iso-8859-1;q=0.5

Response: 指明響應(yīng)資源的 格式;字符集 (若 Response 可滿足客戶端需求)
Content-Type: text/html
Content-Type: text/html; charset=utf-8

Response: 若無法滿足客戶端需求,返回 406 或 415
HTTP 406 (告知無法返回所支持的格式)
HTTP 415 (無法滿足需求舵鳞,同時告知服務(wù)端可響應(yīng)的格式震檩、字符集)
Accept-Patch: application/example, text/example
Accept-Patch: text/example;charset=utf-8

一般情況下,服務(wù)端對格式協(xié)商不太準守協(xié)議系任,無論客戶端是否可處理恳蹲,總是任性的返回資源格式/字符集×├模客戶端若無法處理響應(yīng)格式,往往表現(xiàn)為 “保存為文件”贺奠。
若嚴格準守協(xié)議霜旧,一般也是返回 406;即使返回 415 并告知支持格式儡率,客戶端仍然無法進行修正挂据。因為客戶端通常已經(jīng)在 Accept 中已告知了所有可處理的格式,即使通過 Accept-Patch 告知也無濟于事儿普。
但該首部仍然有一定意義崎逃,比如對于 API 接口,可根據(jù)請求返回 json 或 xml 以加強接口健壯性眉孩。

內(nèi)容語言:Accept-Language / Content-Language & Content-Location

Request: 客戶端可處理的語言
Accept-Language: zh-CN,zh;q=0.9,ca;q=0.8,en;q=0.7,zh-TW;q=0.6,ja;q=0.5,cs;q=0.4,ko;q=0.3

Response: 當(dāng)前資源包含的語言
Content-Language: de, en

對于支持多語言的網(wǎng)站可通過這兩個首部判斷返回个绍。
不建議依賴該 header , 最好設(shè)計其他邏輯由用戶自主設(shè)置語言浪汪,比如通過 url path 或 cookie 等作為依賴巴柿。

對于同一個 url 多次訪問可能返回不同內(nèi)容(比如多語言頁面)∷涝猓可對展示不同語言的頁面設(shè)置一個實際 Content-Location广恢。 當(dāng)兩次訪問同一個 URL,但返回的 Content-Location 不同呀潭,客戶端就不會使用上次的緩存內(nèi)容钉迷。比如訪問 index.html至非,則可以根據(jù)當(dāng)前語言設(shè)置首部
Content-Location: /index_zh.html
Content-Location: /index_en.html
所以:再次不建議同一個 url 展示多語言,有很多不易處理的邏輯糠聪。

內(nèi)容壓縮:Accept-Encoding / Content-Encoding

Request: 客戶端可處理的壓縮算法
Accept-Encoding: gzip, deflate, br

Response: 響應(yīng)使用的壓縮算法
Content-Encoding: gzip

內(nèi)容傳輸:Accept-Ranges & Content-Disposition / If-Range & Range / Content-Length & Content-Range

Response: 對于首次請求荒椭,服務(wù)端應(yīng)返回是否支持的斷點傳輸,none 就是不支持
Accept-Ranges: none(缺始霞铡)
Accept-Ranges: bytes

Response: 希望客戶端如何處理: 使用默認方式打開內(nèi)容 (inline) 或 保存為文件 (attachment)
Content-Disposition: inline (缺蚀辽薄)
Content-Disposition: attachment
Content-Disposition: attachment; filename="filename.jpg"

Request: 若允許斷點下載,通過 If-Range (或 If-Match, 不推薦) 設(shè)置獲取到的 Etag (推薦) 或 Last-Modified夭苗,以便服務(wù)端判斷該文件在上次傳輸之后是否發(fā)生變動信卡,能否續(xù)傳。
If-Range: "33a64df551425fcc55e4d42a148795d9f25f89d4"
If-Range: Wed, 21 Oct 2015 07:28:00 GMT

Request: 告知本次請求所需內(nèi)容范圍题造,可能為多段傍菇。start-end (end 為空代表請求到結(jié)束為止)
Range: bytes=200-1000, 2000-6576, 19000-

Response: 返回內(nèi)容總長度,無論是否為續(xù)傳界赔,該值總應(yīng)該返回
Content-Length: <length>

Response: 對于斷點續(xù)傳髓介,根據(jù) Request Range 返回本次傳輸數(shù)據(jù)的起始范圍/總長度
Accept-Ranges: bytes (仍需返回該首部)
Content-Range: bytes 200-1000/67589
Content-Range: bytes 200-1000/*

內(nèi)容分塊: TE / Transfer-Encoding & Trailer

HTTP 協(xié)議支持 TCP 通道復(fù)用,這意味著一個 TCP 通道可處理多次 HTTP 的數(shù)據(jù)傳輸墩剖,所以需要知道每次 HTTP 實體資源的長度以便區(qū)分杖们、避免混淆,也就是為什么 Response 必須要指明 Content-Length袜腥。但有時候 Response 實體是動態(tài)生成的见擦,需要完整生成后才能知道長度,較為耗時羹令,更好的方案是邊生成鲤屡、邊傳輸?shù)姆謮K傳輸。

Request: 告知客戶端希望分塊傳輸使用的壓縮算法
TE: trailers, gzip;q=0.8, deflate;q=0.5
常用 compress / deflate / gzip福侈,還有一個特殊的 trailers酒来; 這里是“希望”,而不是“必須”肪凛、“僅支持”堰汉。客戶端支持的壓縮算法由 Accept-Encoding 提供显拜,實際上衡奥,很少有瀏覽器會發(fā)送該報文。

Response: 返回 分塊傳輸 所用的壓縮算法
Transfer-Encoding: chunked (告知要分塊傳輸远荠,并未壓縮)
Transfer-Encoding: gzip, chunked (告知要分塊傳輸矮固,且使用了 gzip 壓縮)

Response: 告知分塊數(shù)據(jù)中攜帶額外的 元信息
Trailer: header-names
服務(wù)端若想傳輸該報文,前提是客戶端發(fā)送報文中包含 TE: trailers(表示客戶端可以處理元信息),但由于瀏覽器基本都不會發(fā)送 TE報文档址,所以 Trailer 報文很少用到盹兢。

相對于 Content-EncodingTransfer-Encoding 的特點 :

  • 必須包含 chunked守伸,當(dāng)使用壓縮算法時绎秒,“務(wù)必”將 chunked 放到最后
  • 采用分塊傳輸時,Response 不應(yīng)該發(fā)送 Content-Length 報文
  • Transfer-Encoding 針對的是分塊數(shù)據(jù)使用的壓縮算法尼摹,分塊數(shù)據(jù)在經(jīng)過代理服務(wù)器時见芹,可以被解碼并重新使用其他算法再壓縮,并同時修改 Transfer-Encoding 報文分發(fā)給下級蠢涝;而 Content-Encoding 是針對完整實體的壓縮算法玄呛,也就是說,客戶端在獲取完整實體后和二,根據(jù)該報文整體解碼徘铝,代理服務(wù)器中途不得修改。
    ● 但考慮到分塊傳輸一般是在不方便獲取完整實體時才使用的手段惯吕,并且二次壓縮并不能獲得較大收益惕它,還會增加服務(wù)器負擔(dān),所以不應(yīng)該有兩個報文同時存在
    ● 該結(jié)論僅根據(jù)文檔得出废登,需實際驗證淹魄,事實上不同客戶端對此的理解可能有偏差。
  • 與斷點傳輸不同之處在于:分塊傳輸是在一次 HTTP 通信中將消息實體分塊傳輸堡距;斷點傳輸?shù)拿恳淮蝹鬏敹际且粋€完整的 HTTP 通信揭北,比如客戶端暫停下載后重新開始,或多進程發(fā)送請求分段下載吏颖,最后合并。所以斷點傳輸按照正常請求響應(yīng)恨樟,使用 Accept-Encoding / Content-Encoding 協(xié)商機制即可半醉。

內(nèi)容上傳:Content-*

Request: 比如 POST 或 PUT 操作,會發(fā)送實體內(nèi)容給服務(wù)端劝术,以下首部也可用在請求的報文中
Content-Type: multipart/form-data; boundary=**
Content-Length: <length>
Content-Encoding: gzip
Content-Language: zh

通常缩多,在瀏覽器中,POST 請求會自動設(shè)置實體報文养晋,一般只有 Content-TypeContent-Length衬吆,這對于常見的表單提交已夠用。但如果需要上傳大文件绳泉,是無法簡單通過 HTTP 協(xié)議進行優(yōu)化的逊抡,需要前后端程序?qū)崿F(xiàn)相關(guān)邏輯,一般有以下優(yōu)化方向:

  1. 若需要斷點上傳零酪,可考慮將 Request 實體分片傳輸(分片后可并發(fā)同時傳輸提升效率)冒嫡,這需要服務(wù)端程序配合拇勃,不再是協(xié)議范圍內(nèi)的知識了⌒⒘瑁可 參閱
  2. 瀏覽器默認是不會對 Request 實體進行壓縮的方咆,可考慮使用如 pako 這樣的 JS 擴展來壓縮傳輸實體以減少傳輸流量、提升速度蟀架。并使用 Content-Encoding 報頭標明壓縮算法瓣赂。

Request: 在 MDN 將分塊傳輸首部歸到了 Response header,但根據(jù) RFC 7230 的描述來看片拍,Request 消息實體也是可以分塊傳輸?shù)幕图?dāng)然,瀏覽器無法直接支持該操作穆碎,需自行通過程序處理牙勘。
Transfer-Encoding:chunked

內(nèi)容摘要:Content-MD5 / Want-Digest & Digest

可使用在 Request 和 Response,用于對方校驗接收到的實體內(nèi)容是否完整所禀。
Content-MD5: <hex_digest>

該首部曾經(jīng)屬于標準協(xié)議方面,但 rfc7231 [page 92] 移除了該首部,目前瀏覽器客戶端已不會針對 Response 的 Content-MD5 進行驗證色徘,但這并不妨礙代理服務(wù)器進行識別驗證恭金,也能用于設(shè)計分片斷點上傳、分塊傳輸?shù)臄?shù)據(jù)一致性校驗褂策。

新的 草案 設(shè)計了一組新的報文用于校驗消息實體
Request: 客戶端希望服務(wù)端使用摘要算法
Want-Digest: SHA-256;q=0.3, sha;q=1
Response: 服務(wù)端返回的實體消息摘要
Digest: sha-256=X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=

  • Want-Digest 只能用于 Request横腿,Digest 按協(xié)議可用于 Request 或 Response
  • Digest 支持的算法可參見 草案,摘要值使用 Base64 格式斤寂。
  • Digest 僅針對消息實體耿焊,計算摘要時不能包括報文。且是針對完整消息遍搞,即使對于斷點傳輸罗侯,摘要也應(yīng)該是完整實體消息的摘要。該報文也可用于無實體消息響應(yīng)溪猿,比如 Method Header 的請求钩杰,依然可以提前返回摘要信息。
  • 吐槽:為啥不用 Accept-DigestContent-Digest 與實體首部保持一致性呢诊县?

內(nèi)容跳轉(zhuǎn): Location

Response: 將頁面重新定向至的地址讲弄。
Location: <url>

30X 跳轉(zhuǎn)響應(yīng):

  • 301 Moved Permanently:永久遷移,舊地址不在使用依痊,搜索引擎應(yīng)將索引改為新地址避除,不再爬取舊地址
  • 302 Found:資源移動,舊地址仍可使用。搜索引擎應(yīng)仍索引舊地址驹饺,但內(nèi)容從新地址抓取钳枕。
  • 303 See Other :PUT 或 POST 操作后,沒有轉(zhuǎn)向新資源赏壹,而是轉(zhuǎn)向一個過渡頁(如消息確認鱼炒、上傳進度頁)
  • 307 Temporary Redirect:臨時移動,日后應(yīng)仍訪問舊地址以確認是否恢復(fù)
  • 308 Permanent Redirect:永久遷移蝌借,且保持 method (如 301 在跳轉(zhuǎn)時會將 POST 改為 GET昔瞧;308不能這樣)
  • 305 / 306 已不再(不推薦)使用;304 為內(nèi)容緩存可用時的響應(yīng)(見下面)菩佑,不用于跳轉(zhuǎn)自晰。

201 Created:該響應(yīng)也能攜帶 Location 首部,跳轉(zhuǎn)到新建成功的資源地址稍坯。

Response: 刷新 / 跳轉(zhuǎn)
Refresh: <seconds>[; url=<url>]

這不是一個非標準首部酬荞,RFC 標準未規(guī)定過該首部,但瀏覽器實際上基本都支持瞧哟。作用就是在 seconds 秒之后刷新混巧,若指定 url,則是跳轉(zhuǎn)到指定地址勤揩。其效果等同于 <meta http-equiv="Refresh" value="0; url=//domain.com/" />咧党,推薦使用 meta 方式實現(xiàn)目的,該 meta 的 支持度 非常好

四陨亡、內(nèi)容緩存

緩存時長: Cache-Control & Expires

Response: 告知客戶端資源緩存的最大時長傍衡,過期后應(yīng)該重新請求。(其中 max-age 優(yōu)先級更高)
Cache-Control: max-age=36000, s-maxage=700 (緩存時長)
Expires: Wed, 21 Oct 2015 07:28:00 GMT (直接告知過期時間负蠕;HTTP/1.0 定義蛙埂,已不推薦使用)

Response: 若使用 Expires 設(shè)置緩存時長,另外牽涉下面兩個首部
Date: Wed, 21 Oct 2015 07:28:00 GMT
Age: 300

說明:

  • 代理服務(wù)器緩存時長優(yōu)先級 s-maxage > max-age遮糖,客戶端會忽略 s-maxage箱残; 若沒有 Cache-Control,客戶端止吁、代理服務(wù)器都應(yīng)根據(jù) Expires 進行緩存。
  • 若使用 Expires燎悍,強烈建議設(shè)置 Date (報文創(chuàng)建時間敬惦,一般即為服務(wù)器的當(dāng)前時間,對于代理服務(wù)器谈山,應(yīng)該緩存資源時的時間)俄删, 客戶端可使用 Expires - Date = maxAge 計算出緩存實際生命長度,若沒有 Date,將使用客戶端時鐘畴椰,但客戶端時鐘可能不準確臊诊,便無法準確計算。
  • 對于代理服務(wù)器斜脂,建議設(shè)置 Age (告知資源已在代理服務(wù)器上已存活的時長抓艳,不設(shè)置則認為是 0),若未設(shè)置 Age帚戳,客戶端可使用 client_now - Date = Age 計算緩存實際已消耗的生命長度玷或,客戶端的最終對資源可緩存時長為maxAge - Age,這樣便可在過期后進行了校驗了片任。但鑒于對客戶端時鐘的不信任偏友,建議直接返回 Age 首部。
  • 推薦使用 Cache-Control: max-age 直接設(shè)置对供,但建議同時返回 Expires 位他、DateAge 以兼容不支持 Cache-Control 的客戶端产场。更詳細說明可參見 RFC 2616

在未過期時間內(nèi)鹅髓,客戶端都不應(yīng)重新請求服務(wù)端,而是直接使用緩存涝动。如果使用瀏覽器驗證的話:

  • 訪問資源迈勋,直接刷新,每次都會請求(在 Chrome 中已可看到 304 響應(yīng)醋粟,注意不要勾選開發(fā)者工具的 "Disable cache")靡菇;測試方法:打開新的 Tab 直接訪問,就可以看到米愿,使用的是緩存厦凤。
  • 也可以使用內(nèi)嵌元素,如 img育苟,設(shè)置其 max-age较鼓,將其嵌套在另外一個頁面進行測試,除非是 ctrl + F5 強制刷新违柏,普通刷新會直接使用緩存博烂。
  • 設(shè)置緩存時長特別適用于靜態(tài)資源,現(xiàn)在很少有直接使用原 URL 替換靜態(tài)資源的漱竖,一般都是新建靜態(tài)資源替換禽篱,那么就可以設(shè)置盡可能長的 max-age 來利用緩存策略。
  • 對于非靜態(tài)資源馍惹,這種方式無法讓用戶獲得及時的更新躺率,可設(shè)置 max-age=0玛界,即讓客戶端緩存,但每次都要請求服務(wù)端進行校驗悼吱,校驗通過慎框,僅響應(yīng) 304 即可,無需發(fā)送實體后添,減少數(shù)據(jù)傳輸笨枯。

緩存策略:Cache-Control & Pragma

用于 Response 首部

Cache-Control:no-store 客戶端、代理服務(wù)器都不能緩存吕朵,設(shè)置該首部后猎醇,即使 Response 返回了指紋(如 Etag 或 Last-Modified),客戶端也不緩存努溃,下次請求也不會攜帶指紋硫嘶。可用于隨時會變動的頁面梧税,如首頁沦疾、時間線頁面等。

Cache-Control:no-cache 該名稱非常具有迷惑性第队,其實際作用并不是不能緩存哮塞,而是客戶端、代理服務(wù)器都可緩存凳谦,但每次都需要進行校驗(前提是 Response 報文包含驗證指紋)忆畅,相當(dāng)于 Cache-Control:max-age=0

Cache-Control:private 最終客戶端可緩存,但代理服務(wù)器不能緩存尸执,另外家凯,對于多用戶瀏覽器、或多用戶系統(tǒng)如失,不同用戶之間也不能共用緩存绊诲。比如用在登錄后才能看到的頁面。

Cache-Control:public 客戶端褪贵、代理服務(wù)器都可緩存掂之,廣泛用于靜態(tài)資源(如圖片等)

Cache-control: must-revalidate 基本相當(dāng)于 public (都可緩存),但緩存過期后必須校驗脆丁,且確保資源新鮮后才能返回內(nèi)容世舰;若校驗失敗,應(yīng)返回 4xx 或 5xx槽卫。而 public 則不然冯乘,比如碰到源服務(wù)器宕機,未能校驗成功晒夹,有可能使用過期的緩存裆馒,代理服務(wù)器還應(yīng)發(fā)送 warning 報頭提醒。

Cache-Control:proxy-revalidate 相當(dāng)于代理服務(wù)器使用 must-revalidate 策略丐怯,客戶端使用 public 策略

Cache-Control:no-transform 針對代理服務(wù)器喷好,不能對緩存內(nèi)容進行轉(zhuǎn)換,比如為節(jié)省流量读跷,有些代理服務(wù)器可能會對圖像格式進行轉(zhuǎn)換梗搅。

Cache-Control:immutable 針對客戶端,比如上面舉例中 刷新 或 ctrl+F5 強刷效览,設(shè)置該值是告訴瀏覽器即使在強刷情況下无切,也無需校驗,仍使用未過期緩存丐枉。該值有兼容性問題哆键,并不是所有瀏覽器都已實現(xiàn),但對于確定永遠不會發(fā)生變化的靜態(tài)資源可返回該首部瘦锹,對于不支持的瀏覽器也不會有什么副作用籍嘹。

還有兩個試驗性的,并發(fā)所有瀏覽器都支持弯院。
Cache-Control:stale-while-revalidate=<seconds> 當(dāng)驗證時 - 過期緩存有效辱士。如果響應(yīng)是一個較大的資源,可以使用該值听绳∷痰猓客戶端在緩存過期時可直接使用過期緩存,異步獲取新鮮資源椅挣。但如果過期時長超過了指定秒數(shù)头岔,客戶端不可直接使用過期資源,必須重新校驗贴妻。
Cache-Control:stale-if-error=<seconds> 當(dāng)發(fā)生錯誤時 - 過期緩存有效切油。與上面的類似

  • 若未設(shè)置,大部分瀏覽器會按照 private 來處理名惩。
  • 以上值并不是互斥的澎胡,可設(shè)置多個,比如 Cache-Control: proxy-revalidate, no-transform娩鹉;
  • 另外攻谁,瀏覽器總是會傾向于更嚴格的緩存策略,比如 public, private 則使用 private 弯予; public, no-store 則使用 no-store 戚宦; no-cache, max-age=100 則會自動忽略 max-age
  • 對于不希望客戶端緩存的锈嫩,可設(shè)置為 Cache-Control: no-store, no-cache, must-revalidate受楼,這樣可避免某一個值不被客戶端支持垦搬,只要客戶端支持任意一個值,瀏覽器就可以收到二次請求艳汽。

Cache-Control 也可用于 Request 報頭猴贰,這些報頭一般是針對代理服務(wù)器而言的,源服務(wù)器大部分情況下無需針對該報頭做特殊處理河狐,可用值如下:
Cache-Control:no-store 必須返回新鮮資源米绕,即代理服務(wù)器、源服務(wù)器都不應(yīng)該返回 304馋艺,而應(yīng)該返回 200 并傳輸實體內(nèi)容栅干。
Cache-Control:no-cache 可以返回304,但代理服務(wù)器必須校驗是否最新(源服務(wù)器無論有沒有該報頭總應(yīng)該這么做)
Cache-Control:no-transform 告知代理服務(wù)器捐祠,不要轉(zhuǎn)換格式
Cache-Control:only-if-cached 針對代理服務(wù)器碱鳞,若已緩存,不要進行校驗雏赦,請直接返回劫笙。看語義星岗,若未緩存填大,應(yīng)該返回 4xx 響應(yīng)。應(yīng)該很少有客戶端有這么變態(tài)的要求俏橘,可能適合用在一些數(shù)據(jù)丟失允华,嘗試從代理服務(wù)器恢復(fù)的場景。

Cache-Control:max-age=<seconds> 代理服務(wù)器緩存時長若超過 max-age 的話寥掐,請校驗新鮮度靴寂。所以 max-age=0 等價于 no-cache,必須進行新鮮度校驗召耘。
Cache-Control:min-fresh=<seconds> 代理服務(wù)器的緩存剩余生命長度不得低于該秒數(shù)
Cache-Control:max-stale[=<seconds>] 客戶端可接受過期緩存【若設(shè)置秒數(shù)百炬,表示過期時長不能超過該秒數(shù)】

Pragma 首部,可同時用于 Request 和 Response
這是一個 HTTP/1.0 版本時規(guī)定的首部污它,僅有一個 no-cache 值 (RFC 7234)剖踊,當(dāng)與 Cache-Control 同時出現(xiàn)時,后者優(yōu)先

Request: 請代理服務(wù)器返回新鮮資源衫贬,不要緩存
Pragma: no-cache

Response: 請客戶端不要緩存資源
Pragma: no-cache

指紋驗證:ETag / If-Match & If-None-Match

Response: 返回資源的標識符
Etag: "c0bea9ae76c87756d20d0dd9012f8a52" (包括引號德澈,強驗證)
Etag: W/"0815" (弱驗證,W必須大寫)

強弱與否固惯,對于客戶端可能沒有意義梆造,只是方便服務(wù)端知道。以便在下次驗證時做區(qū)分:對于強驗證葬毫,哪怕一個字節(jié)發(fā)生變化镇辉,都應(yīng)返回新資源屡穗;對于弱驗證,只有主體內(nèi)容變化忽肛,才需返回新資源(比如一個頁面內(nèi)容無變化鸡捐,僅某一個廣告發(fā)生變化,可能就無需重新返回)

Request: 若客戶端緩存了上次 Response 資源麻裁,下次請求會攜帶 etag,有兩種攜帶方式

  1. 緩存驗證(較為常用)
    If-None-Match: "bfc13a64729c4290ef5b2c2730249c88ca92d82d"
    If-None-Match: W/"67ab43", "54ed21", "7892dd"
    If-None-Match: *

客戶端攜帶之前緩存的標識符發(fā)送請求:

  • 若服務(wù)端無法匹配標識符(None-Match = true)源祈,返回正常的 200 響應(yīng)煎源,并發(fā)送最新 Etag;
  • 若夠匹配到(None-Match = false)香缺,返回 304 (并攜帶原本可能出現(xiàn)在 200 響應(yīng)中的首部:Cache-Control手销、Content-Location、Date图张、ETag锋拖、Expires 和 Vary ),無需發(fā)送實體祸轮,大大減少了數(shù)據(jù)傳輸
  1. 避免“空中碰撞”
    If-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"
    If-Match: W/"67ab43", "54ed21", "7892dd"
    If-Match: *

比如編輯頁面兽埃,訪問頁面后獲取到 etag,在更新時攜帶獲取到的 etag:

  • 若匹配成功(Match=true)适袜,說明該資源沒有被其他操作二次改動過柄错,更新成功,返回正常的 200苦酱、新的 etag售貌;
  • 若匹配失敗(Match=false)疫萤,說明在提交前已被其他用戶修改颂跨,返回 412 響應(yīng)

時間驗證:Last-Modified / If-Modified-Since & If-Unmodified-Since

Response: 返回資源的最后修改時間
Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT

Request: 若客戶端緩存了上次 Response 資源,下次請求可使用最后修改時間驗證

緩存驗證
If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT
若服務(wù)端文件修改過(Modified=true)扯饶,返回新的資源內(nèi)容恒削,正常 200 響應(yīng);若未修改過(Modified=false)帝际,則返回 304 響應(yīng)蔓同。

避免“空中碰撞”
If-Unmodified-Since: Wed, 21 Oct 2015 07:28:00 GMT
更新內(nèi)容時,需保證資源未修改(Unmodified=true)蹲诀,若驗證通過斑粱,才會返回正常 200 響應(yīng),否則返回 412 響應(yīng)脯爪。

對比一下就會發(fā)現(xiàn),Modified 與 etag 二者邏輯是完全相同的恨旱;實際上 Modified 首部出現(xiàn)較早 (HTTP/1.0)隶糕,etag 首部出現(xiàn)較晚 (HTTP/1.1),是用來替代 Modified 的涌矢;顯而易見,etag 驗證更加穩(wěn)定準確快骗,畢竟修改時間的改變并不代表內(nèi)容一定發(fā)生變化娜庇,所以 推薦使用 etag;若二者同時出現(xiàn)方篮,etag 優(yōu)先名秀,當(dāng)前所有瀏覽器都已支持 etag。
但如果需要兼容舊版本瀏覽器藕溅,則建議二者同時發(fā)送匕得;如果想減少報文大小或服務(wù)端不方便計算 etag,也可以僅發(fā)送 Modified巾表,實際上汁掠,即使在今天,仍有不少大公司僅發(fā)送 Modified 首部集币。

緩存條件:Vary

Response: 緩存條件:若服務(wù)端在 Request 報文不同時考阱,返回的內(nèi)容不同。必須在 Response 告知客戶端下次請求惠猿,只有在全部或指定的 Request header 完全相同時羔砾,才能直接使用緩存,否則必須發(fā)送請求驗證資源是否更新或直接重新請求資源偶妖。
Vary: *
Vary: User-Agent, Content-Type, ...

另外關(guān)于緩存的幾點總結(jié)

  • 服務(wù)端僅設(shè)置了 “驗證首部(指紋或時間)”報文姜凄,客戶端每次都會重新發(fā)送請求,服務(wù)端可返回 304 或 響應(yīng) 200 重新發(fā)送資源實體趾访。
  • 服務(wù)端僅設(shè)置了 “緩存時長”态秧,客戶端在緩存到期后會重新發(fā)送請求,由于沒有 “驗證首部”扼鞋,即使服務(wù)端資源沒有任何變化申鱼,也無法驗證、無法返回 304云头,只能響應(yīng) 200捐友,重新發(fā)送資源實體。
  • 二者都設(shè)置了溃槐,客戶端在緩存到期后會重新發(fā)送請求匣砖,服務(wù)端可返回 304 或 200。

私人信息:Cookie

Response: 設(shè)置cookie,可多次發(fā)送
Set-Cookie: sessionid=38afes7a8; HttpOnly; Path=/
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly

Request: 攜帶已設(shè)置 cookie
Cookie: sessionid=38afes7a8; id=a3fWa

緩存清除:Clear-Site-Data

Response: 告知要清除的緩存猴鲫,可參見:Clear-Site-Data
Clear-Site-Data: "cache", "cookies", "storage", "executionContexts"
Clear-Site-Data: "*"

五对人、內(nèi)容安全

以下報文相對而言都比較新,有些甚至還尚在實驗中拂共,并不是所有瀏覽器都支持牺弄,并且有些不僅報文可以實現(xiàn),也可以通過其他方式宜狐。關(guān)于安全性的問題都比較復(fù)雜势告,下面每一個首部可能都值得、或者說需要一篇單獨的文章才能講清楚抚恒,這里僅列一個大概

Resonse: Content-Security-Policy 培慌、Content-Security-Policy-Report-Only
用于降低 XSS 風(fēng)險,也可使用 meta 方式柑爸,可 參見,特別適合 UGC 類型盒音,比如 facebook表鳍、instagram 就設(shè)置了該報文

Response: X-XSS-Protection
另外一個降低 XSS 風(fēng)險的報文:參見

Response: Cross-Origin-Opener-PolicyCross-Origin-Embedder-Policy祥诽、Cross-Origin-Resource-Policy
用于降低跨域資源或釣魚造成的攻擊譬圣,可參考 XS-Leaks跨域隔離雄坪、跨域策略

Response: Origin-Isolation
來源隔離機制: 參見

Response: Feature-Policy
在直接訪問或 Iframe 訪問時厘熟,啟用/禁用 瀏覽器的一些特性:參見

Response: X-Content-Type-Options
nosniff: 禁止瀏覽器對 css/js 進行自動嗅探,降低 XSS 風(fēng)險:參見

Response: X-Frame-Options
確保網(wǎng)站沒有被嵌入到別人的站點里面维哈,可參考 Clickjacking攻防

Response: X-Download-Options: noope
針對 IE 瀏覽器的報文绳姨,下載文件的彈出框不顯示“打開”選項 (僅保留“保存”選項),避免釣魚攻擊阔挠,也可通過 meta 設(shè)置飘庄,參見

Response: X-Permitted-Cross-Domain-Policies
針對 crossdomain.xml (允許跨域策略文件)的報文,主要是如 flash/Silverlight/Flex 等嵌入式舊技術(shù)购撼,現(xiàn)在很少用的到跪削,如有需要請自行找資料

六、客戶端信息

以下為一些針對現(xiàn)代瀏覽器迂求,尤其是為不同尺寸終端設(shè)計的報文碾盐,并未獲得廣泛良好的 支持,目前仍屬于 草案揩局,需謹慎依賴毫玖。(吐槽一下,google 對這種收集客戶端信息的特別上心,參與提交草案孕豹,且 chrome 基本實現(xiàn)涩盾,蘋果就比較反感這種有隱私顧慮的,估計 Safari 肯定不上心励背。另外 一例

Response: 期望客戶端請求攜帶的信息春霍、以及該配置的有效時長
Accept-CH: <list of client hints>,如 Accept-CH: DPR, Viewport-Width
Accept-CH-Lifetime: <age>叶眉, 如 Accept-CH-Lifetime: 86400

Request: 根據(jù)服務(wù)端響應(yīng)址儒,攜帶其所需報文,這里列舉一些衅疙,可能有會有更多
DPR: 1.0 客戶端設(shè)備的像素比
Device-Memory: 1 客戶端設(shè)備內(nèi)存的近似大小
Viewport-Width: 667 布局視口寬度
Width: 240 物理像素寬度

服務(wù)端可根據(jù)這些報文莲趣,針對不同的設(shè)備參數(shù)返回不同的響應(yīng),但大部分情況饱溢,在前端使用 JS 處理可能會更好一點喧伞。之所以設(shè)計出這種報文,可能是為了充分利用客戶端緩存功能绩郎,以便盡可能減少流量傳輸潘鲫。所以這里就不得不說一下,若出于此目的肋杖,切記 Response 使用 Via 指明緩存有效的報文字段溉仑,如:
Via: DPR, Viewport-Width

Request:客戶端在網(wǎng)絡(luò)狀況不好或設(shè)備性能不足時,可使用該報文希望服務(wù)端發(fā)送簡潔版本的響應(yīng)状植,以減少加載時長浊竟、降低對設(shè)備的性能需求。
Save-Data: on (需要) 或 Save-Data: off (不需要)

七津畸、代理服務(wù)器相關(guān)

如果不是編寫代理軟件振定,以下報文只有 “Forwarded” 需要在應(yīng)用程序中關(guān)心。其他報文通常是代理軟件處理肉拓,如 Nginx / Apache 等網(wǎng)關(guān)應(yīng)用吩案、云廠商提供的負載均衡產(chǎn)品等。但了解這些報文帝簇,也能更好的使應(yīng)用程序與代理軟件交互徘郭。

Request & Response: Cache-Control
有些值是專門針對代理服務(wù)器的,參見上面緩存策略章節(jié)說明

Response: 報文的創(chuàng)建時間丧肴,已緩存時長残揉,參見上面緩存時長章節(jié)說明
Date: Wed, 21 Oct 2015 07:28:00 GMT
Age: 300

Request & Response: 代理服務(wù)器信息,請求/響應(yīng)中均可使用芋浮,可用于分析請求鏈抱环、防止循環(huán)請求等
Via: [ <protocol-name> "/" ] <protocol-version> <host> [ ":" <port> ] 如:Via: 1.1 cdn.com / Via: HTTP/1.1 cdn.com:8080
或 (pseudonym 為內(nèi)部代號)
Via: [ <protocol-name> "/" ] <protocol-version> <pseudonym> 如:Via: 1.1 Name / Via: HTTP/1.1 Node

Response: 警告報文壳快,返回給客戶端或下一級代理服務(wù)器不新鮮的緩存資源,同時發(fā)出警告镇草。
Warning: <warn-code> <warn-agent> <warn-text> [<warn-date>]
該首部可多次發(fā)送眶痰;也可用于 Request,但較為少見


Request: 代理服務(wù)器在收到客戶端請求梯啤,向源服務(wù)器轉(zhuǎn)發(fā)請求時竖伯,可能會丟失一些信息,所以一些應(yīng)用程序或 CDN廠商 自創(chuàng)了一些 header 首部來擬補這些損失因宇,目前有部分首部已納入到協(xié)議標準七婴。
Forwarded: by=<identifier>; for=<identifier>; host=<host>; proto=<http|https>

但一些舊程序仍然在使用原來的首部,所以源服務(wù)端也應(yīng)該認真對待

Request: 在進入該級代理服務(wù)器前察滑,請求 ip 鏈打厘,第一個即為實際客戶端的 IP
X-Forwarded-For: 2001:db8:85a3:8d3:1319:8a2e:370:7348, 70.41.3.18, 150.172.238.178 (常見)
X-ProxyUser-Ip: ip, ip

Request: 客戶端實際請求的 host,比如是 CDN 節(jié)點 HOST贺辰,源服務(wù)器自身的 HOST 一般與此不同户盯。
X-Forwarded-Host: id42.cdn.com

Request: 客戶端請求實際的 scheme,源服務(wù)器可能是 http 服務(wù)器饲化,https 在代理服務(wù)器層完成.
X-Forwarded-Proto: https (常見)
X-Forwarded-Protocol: https
X-Url-Scheme: https
X-Forwarded-Ssl: on
Front-End-Https: on

標準首部 Forwarded 其實就是合并版先舷,其中 for 格式如下,ipv6 會使用方括號括起來
Forwarded: for=192.0.2.43, for="[2001:db8:cafe::17]"; host=id42.cdn.com; proto=https

Forwarded by 介紹 : The "by" parameter is used to disclose the interface where the request came in to the proxy server滓侍,看樣子好像是當(dāng)前代理服務(wù)器的上一級請求的 IP (可能是客戶端或代理服務(wù)器),這樣子其實是與 for 重復(fù)了牲芋,該值的介紹有點模糊撩笆,根據(jù)實際情況而定。

注意:由于該值十分容易偽造缸浦,所以對于源服務(wù)器而言夕冲,應(yīng)該僅信任確定是代理服務(wù)器發(fā)送的值。


Request: 可以看到裂逐,代理服務(wù)器可能有多個歹鱼,這對于客戶端,就需要一直等候卜高,所以這里有一個新 草案 報文弥姻,按照協(xié)議,每經(jīng)一級代理服務(wù)器掺涛,該值就減小 1庭敦,當(dāng)減小至 0 時,不應(yīng)繼續(xù)向上級請求薪缆,而是直接返回秧廉。另外有一個關(guān)于該報文的 問題
Max-Forwards:3

八、跨域請求

若是符合以下條件的簡單請求:

  1. Method 為 : GET、POST疼电、HEAD
  2. Request Header 為安全首部:如 Accept嚼锄,Accept-Language 等

無論是否跨域,瀏覽器都將直接發(fā)送請求蔽豺,根據(jù)返回的報文決定是攔截区丑,還是將內(nèi)容返回給請求方

Response: 允許跨域查詢的域名,可使用 “*” 允許所有域名跨域
Access-Control-Allow-Origin: <origin> / Access-Control-Allow-Origin: *
(指定域名需包括 scheme茫虽,如: Access-Control-Allow-Origin: https://domain.com

Response: 是否允許 Request 包含認證信息刊苍,比如 cookie / authorization headers / TLS client certificates
Access-Control-Allow-Credentials: true
(如果 Response 包含該報文,Access-Control-Allow-Origin 不能使用 “*” 通配符濒析,必須指定域名)
(如果 Response 不含該報文正什,而 Request 又發(fā)送了認證信息,瀏覽器會攔截響應(yīng)号杏,即客戶端無法獲得資源)

Response: 允許暴漏給外部的響應(yīng)首部婴氮,默認僅暴漏 簡單首部
Access-Control-Expose-Headers: <header-name>, <header-name>, ...


非簡單請求,客戶端會首先使用 OPTIONS Method 預(yù)檢請求

Request: 告知即將使用何種 Method 發(fā)送請求
Access-Control-Request-Method: PUT

Request: 告知會攜帶的非安全 header 首部
Access-Control-Request-Headers: <header-name>, <header-name>, ...

Response: 返回允許的 Method
Access-Control-Allow-Methods: <method>, <method>, ...

Response: 返回允許 Request 使用的非安全 header 首部
Access-Control-Allow-Headers: *Access-Control-Allow-Headers: <header-name>[, <header-name>]*

Response: 其他所需信息(參見上面簡單請求的說明)
Access-Control-Allow-Origin
Access-Control-Allow-Credentials
Access-Control-Expose-Headers

Response: 預(yù)檢配置的有效時長盾致,即在指定秒數(shù)內(nèi)主经,無需再次預(yù)檢,直接使用本次返回的結(jié)果庭惜。
Access-Control-Max-Age: 86400


Request: 在使用 XMLHttpRequest 執(zhí)行 Ajax 請求時罩驻,瀏覽器可能會攜帶該首部,但并不是一定护赊,該首部并不屬于協(xié)議惠遏,所以服務(wù)端不能武斷的使用該首部判斷是否為 Ajax 請求。
X-Requested-With: XMLHttpRequest


Request: “Sec-Fetch” 請求元數(shù)據(jù)系列骏啰,該首部由瀏覽器添加(僅在 HTTPS 下發(fā)送)节吮,代理服務(wù)器等不得更改,不屬于協(xié)議判耕,仍處于 實驗階段透绩;
Sec-Fetch-Dest / Sec-Fetch-Mode / Sec-Fetch-Site / Sec-Fetch-User
服務(wù)端可利用這些報文判斷訪問合法性,可參見:MDN壁熄、詳解帚豪,但由于 HTTP 頭可以偽造,跨域安全性問題不應(yīng)依賴該報文草丧。

九志鞍、HTTPS / WebSocket 等

HTTP 協(xié)議版本:從 HTTP的發(fā)展 來看,最主要的為 1.0 / 1.1 / 2方仿。簡單介紹下版本的選擇機制:

  • 客戶端直接使用 HTTPS 請求固棚,Nginx 等服務(wù)器軟件可使用的 ALPN 協(xié)商讓客戶端直接使用 HTTP/2 協(xié)議统翩,應(yīng)用層無感。
  • 若服務(wù)端 ALPN 協(xié)商告知不支持 HTTP/2 或 客戶端使用 HTTP 請求:當(dāng)下此洲,瀏覽器都默認使用 HTTP / 1.1厂汗;但不排除有其他客戶端會使用 HTTP/1.0
  • 若客戶端使用 HTTP/1.0 請求,則服務(wù)端應(yīng)最好是僅發(fā)送 1.0 支持的首部(不支持的報文會被客戶端忽略)呜师,且不應(yīng)依賴在 1.1 才有的請求首部娶桦。主要區(qū)別:
    • Host:Request 不發(fā)送 Host 首部,這個最蛋疼的一個問題汁汗。同一個 IP 可能會綁定多個域名衷畦,若 Request 不發(fā)送 Host,就沒辦法確定請求的是哪一個網(wǎng)站知牌。所以如果需要支持這種請求祈争,就需要給域名單獨分配一個 IP,比如百度角寸;好在這種請求現(xiàn)在越來越少了菩混,對于不發(fā)送 Host 的請求,一般返回 400扁藕,比如知乎沮峡。
    • Connection / Keep-Alive:1.0 不支持 TCP 復(fù)用,一次請求結(jié)束后立即關(guān)閉 TCP 通道亿柑,Request 不會發(fā)送該首部邢疙,Response 也不應(yīng)返回該首部,所以 Response 也可以不發(fā)送 Content-Length 首部望薄。
    • Accept-*:1.0 還未引入?yún)f(xié)商機制疟游,請求報文不會包括相關(guān)首部。但服務(wù)端應(yīng)返回 Content-* 首部告知格式式矫、語言等,且需注意役耕,1.0 雖支持消息壓縮傳輸采转,但壓縮算法 支持 與 1.1 并不同。
    • Transfer-Encoding:1.0 不支持分塊傳輸
    • Cache-Control / ETag:1.0 不支持這種緩存策略的報文瞬痘,僅支持 Pragma / Last-Modified
    • 最后:1.0 與 1.1 并不是完全隔絕的兩種東西故慈,事實上,在 1.1 標準確定之前框全,好多 1.0 客戶端已會發(fā)送 Host首部察绷、支持復(fù)用 TCP(發(fā)送 Connection 首部),所以服務(wù)端對于 1.0/1.1 請求以收到的首部為準津辩。
  • 服務(wù)端的 Response 協(xié)議版本不應(yīng)大于 Request 所使用的版本拆撼,否則客戶端可能無法處理容劳。
  • ALPN 協(xié)商可以讓客戶端直接使用 HTTP/2,但其實對于 HTTP/1.1 請求闸度,還可通過 Upgrade 首部協(xié)商升級(瀏覽器通常不會做竭贩,服務(wù)端對于這種請求一般是 301 到 支持 HTTP/2 的 https 地址以完成升級)。

Request: 請求可使用 HTTP/1.1 連接莺禁,之后發(fā)送首部協(xié)商升級留量。該用法僅支持 1.1,1.0 還未設(shè)計該機制哟冬,2 不支持協(xié)商(用意應(yīng)該是不能降級楼熄,后續(xù)版本的協(xié)商方式也不是通過首部)
Connection: Upgrade
Upgrade: h2c

HTTP Method
HTTP/1.0 協(xié)議只定義了 GET、HEAD浩峡、POST可岂;其他的一些 Method 是在 1.1 甚至是補充協(xié)議中定義的。對于一些嚴格驗證 Method 的服務(wù)端红符,比如一些 REST API 服務(wù)端青柄,那么在無法發(fā)送 PUT 等 Method 的客戶端通常會使用 POST 發(fā)送,但通過首部來指明 Method 語義预侯,常用的有以下兩個
X-METHOD-OVERRIDE: PUT
X-HTTP-METHOD-OVERRIDE: PUT

HTTP/2 相關(guān)
該版本進行了大量優(yōu)化致开,但大部分細節(jié)都是在 TCP 通信部分,主要由諸如 Nginx 等服務(wù)器軟件完成萎馅。對于網(wǎng)絡(luò)應(yīng)用程序而言双戳,并無太大差異,但對于以前奉為圭臬的一些網(wǎng)絡(luò)優(yōu)化手段糜芳,則發(fā)生了較大變化飒货,這里簡單提一下:

  • 域名散列:之前為了提高并發(fā)吞吐,會采用多域名策略峭竣,在 http/2 這樣做塘辅,反而會降低性能。這主要是 http/2 使用了多路復(fù)用技術(shù)皆撩,域名少反而更有優(yōu)勢
  • 資源合并:原因與上面相同扣墩,http/2 并不介意同一個頁面請求更多資源,因為并不會創(chuàng)建更多的 TCP 通道扛吞,資源合并反而因為緩存更容易失效成為劣勢呻惕,想像一下,任何一個小文件的改動都會導(dǎo)致整個合并資源需要更新滥比。
  • 資源內(nèi)聯(lián):之前為了讓頁面加載即渲染亚脆,可能將 css 直接內(nèi)置到頁面中,但這樣卻無法讓客戶端緩存樣式資源盲泛、也會讓不同頁面無法使用同一個資源緩存濒持。

推薦方式

  • 盡可能給響應(yīng)添加 etag 報文键耕,利用緩存
  • 多域名解析到一個 ip,充分利用 http/2 的多路復(fù)用
  • 多域名共用一個證書弥喉,減少證書驗證的時間消耗

http/2 作為一個協(xié)議郁竟,本事是可以支持 HTTP 的,但瀏覽器廠商都沒有去實現(xiàn)由境,僅支持 HTTPS棚亩。另外, http/2 也新增了一些功能:

Request & Response: 推送虏杰,該功能也是在 Nginx 層面完成的讥蟆,只需要配置即可,以下兩個報文仍處于 草案纺阔,如果不是服務(wù)器程序開發(fā)者瘸彤,也無需關(guān)心。
Accept-Push-Policy / Push-Policy
對于應(yīng)用程序開發(fā)者而言笛钝,若不想在服務(wù)器軟件內(nèi)配置质况,Link 首部其實可以作為推送的代替品(參見最上面基礎(chǔ)首部的介紹),但推送并不是一定有益玻靡,可參考 這篇 文章適場景而用结榄。

Response: 備選服務(wù)(草案),支持度 還不錯
Alt-Svc: <service-list>; ma=<max-age>; persist=1, <service-list>; ma=<max-age>; persist=1

  • 通過改報文可提供一個或多個備選服務(wù)囤捻,設(shè)置不同的通信協(xié)議 / 服務(wù)端IP臼朗,瀏覽器會緩存這些配置;
  • max-age 內(nèi)蝎土,若服務(wù)端不可用视哑,瀏覽器會嘗試使用這些備選服務(wù)發(fā)出請求,比如 google 就設(shè)置了該報文誊涯。
  • 另外還有一個 persist 值挡毅,是否在網(wǎng)絡(luò)發(fā)生變化(如:wifi 變 4g)時,仍保持備選服務(wù)的有效性暴构,默認為否跪呈。
  • 想像一下,是不是可以用國內(nèi)服務(wù)器給未備案的域名加速呢丹壕,感覺云廠商也會防堵這個庆械。不過薇溃,該報文對于提高服務(wù)可用性還是用處不小的菌赖,可嘗試一下。另外沐序,該技術(shù)是不是可以在一定程度上預(yù)防 DNS 投毒琉用。

Request: 若客戶端使用備選服務(wù)發(fā)出請求堕绩,會攜帶該報文告知所選用的主機
Alt-Used: uri-host [ ":" port ]

HTTPS 相關(guān):
互聯(lián)網(wǎng)已經(jīng)基本進入了 HTTPS 時代(無論是使用 HTTP/1.1 還是 HTTP/2,現(xiàn)在絕大多數(shù)網(wǎng)站都開啟了 HTTPS)邑时,但由于要對舊世界兼容奴紧,所以仍然支持 HTTP。這對不少場景造成了一些問題晶丘,表現(xiàn)尤為嚴重就是流量劫持黍氮、中間人攻擊,下面幾個報文主要是為了降低這種問題帶來的危害浅浮。

Response: HSTS(HTTP Strict Transport Security沫浆,RFC6797),嚴格使用安全協(xié)議傳輸
Strict-Transport-Security: max-age=<expire-time>; includeSubDomains; preload

  • 啟用 HTTPS 后滚秩,訪問 HTTP 一般會 301 到 HTTPS专执,但在跳轉(zhuǎn)前仍然為 HTTP 響應(yīng),面臨著被劫持的風(fēng)險郁油。跳轉(zhuǎn)后的 HTTPS 可發(fā)送該報頭(該報頭僅能用在 HTTPS 響應(yīng)中)本股,那么在 max-age時間內(nèi),用戶再次訪問 HTTP桐腌,瀏覽器不會發(fā)送請求拄显,而是直接轉(zhuǎn)而請求 HTTPS×ú簦可選:includeSubDomains凿叠,子域名是否也強制啟用 https,啟用該項嚼吞,子域名首次被劫持風(fēng)險也消除了盒件。
  • 可以看到,使用該報文也無法避免首次被劫持的風(fēng)險舱禽,所有 google 搞了一份清單炒刁,收錄那些永久采用 HTTPS 的域名,這樣首次訪問也直接跳轉(zhuǎn)誊稚,目前各瀏覽器廠商基本都支持了該清單翔始。添加 preload 的意思就是同意加入該清單,但并不是添加了就一定會被收錄里伯,還有有一些其他 條件城瞎。在確定永久采用 HTTPS 前,不要添加該值疾瓮,否則以后想使用 HTTP 時域名就廢了脖镀,目前不曉得怎么從清單刪除。

Request: 客戶端支持優(yōu)先使用 HTTPS 加載資源
Upgrade-Insecure-Requests: 1

  • HTTPS 響應(yīng)頁面中可能會包含 HTTP 資源狼电。若客戶端請求發(fā)送該報文蜒灰,則表明客戶端支持無痛替換頁面內(nèi)所有 HTTP 資源為 HTTPS 資源(是直接替換弦蹂,不會訪問 HTTP 了)。服務(wù)端可通過 Content-Security-Policy 首部告知客戶端該如何處理(參閱“內(nèi)容安全”章節(jié)强窖,提到了該報文):block-all-mixed-content (不加載 HTTP 資源)凸椿、upgrade-insecure-requests (升級 HTTP 為 HTTPS)
  • 瀏覽器廠商除了避免服務(wù)端被劫持,也要為用戶考慮翅溺,所以為了提高大家替換為 HTTPS 的積極性脑漫,如果 HTTPS 頁面包含 HTTP 資源,會提醒用戶該網(wǎng)站不安全(不同瀏覽器的提示方式也不盡相同)咙崎。如果是程序開發(fā)商窿撬,不知道程序被實際使用時是否會采用 HTTPS,內(nèi)嵌資源可以使用無協(xié)議 URL ( //domian.com)叙凡,瀏覽器會自動使用其所在頁面的協(xié)議劈伴。否則就要排查一切非 HTTPS 資源,包括 css/js/圖片/字體/異步接口握爷、甚至是 form action.
  • 思考題:有些 APP 會在手機內(nèi)建一個 127.0.0.1 的內(nèi)網(wǎng) server跛璧,以便于自家的網(wǎng)絡(luò)產(chǎn)品使用,那么在瀏覽器內(nèi)訪問該接口是否會觸發(fā)不安全提示新啼、該如何處理追城。

Response: 發(fā)送證書透明度檢測報告 (草案
Expect-CT: max-age=<age>; enforce; report-uri="<uri>"

  • HTTPS 主要是為了加密傳輸,但也不是不可 解密燥撞。對于網(wǎng)絡(luò)應(yīng)用而言座柱,避免中間人獲取傳輸數(shù)據(jù),最重要的就是保護好證書私鑰物舒。但這仍然不能保證完全安全色洞,比如著名的 DigiNotar 事件,如果 CA 機構(gòu)出現(xiàn)問題冠胯,對于使用其證書的公司很難第一時間得到預(yù)警火诸,消除影響。
  • 于是有了 certificate transparency 用于監(jiān)測審計證書透明度荠察,以便第一時間消除影響置蜀。該機構(gòu)推進證書廠商支持 CT,好消息是現(xiàn)在幾乎所有證書頒發(fā)機構(gòu)悉盆,包括 Let's Encrypt 都已支持 CT盯荤。網(wǎng)站運維人員可以在 googlecrt.sh 搜索查看證書的透明度報告焕盟。
  • 由 Google 主導(dǎo)的 RFC 6962 則是為了更進一步秋秤,服務(wù)端可通過 report-uri 提供一個 URL,瀏覽器收到該報文后,會在每個 max-age 時間后發(fā)送一份透明度檢測報告到該 URL航缀,以便管理員可以更及時應(yīng)對。若指定為 enforce堰怨,瀏覽器會在不符合 CT 安全性要求的情況下拒絕建立連接(慎用)昭躺。

Response: HPKP 指令報文
Public-Key-Pins 迷雪、Public-Key-Pins-Report-Only
這是 chrome 在 CT 規(guī)范之前嘗試使用的一種證書驗證方式,目前已移除 chrome,應(yīng)該不會再有瀏覽器支持了渤早,就不再細說了。

WebSocket 相關(guān)
還記得上面說的 Upgrade 首部吧狸窘,該首部作用就是將 HTTP/1.1 升級為其他協(xié)議阴幌,瀏覽器利用該特性新增了 WebSocket 協(xié)議以支持雙向通信。

Request: 客戶端發(fā)出請求
Connection: Upgrade
Upgrade: websocket (通知服務(wù)端要升級為 websocket 協(xié)議)
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== (客戶端標識)
Sec-WebSocket-Version: 13 (使用的 websocket 協(xié)議版本)
Sec-WebSocket-Protocol: soap, wamp (不常用: 使用的 websocket 子協(xié)議)
Sec-WebSocket-Extensions: permessage-deflate (不常用: 客戶端支持的 websocket 擴展)
Origin: https://domian.com (其他任意 HTTP 首部)

Response: 服務(wù)端響應(yīng)
Sec-WebSocket-Accept: s3p= (由于 Sec-WebSocket-Key 計算的值蒋困,返回給客戶端驗證)

以上便完成了 HTTP 到 WebSocket 的升級盾似。當(dāng)然,升級過程對客戶端應(yīng)用程序開發(fā)者而言一般是透明的雪标,即無需了解具體細節(jié)零院,比如瀏覽器提供了 相關(guān)API,微信小程序提供的 相關(guān)API村刨,只需要直接使用 API 操作即可告抄,無需關(guān)心通信是如何建立的。

但如果是需要以程序方式使用客戶端嵌牺,或創(chuàng)建服務(wù)端打洼,則需要自行處理協(xié)議,服務(wù)器軟件一般不提供逆粹∧即可參考 RFC 6455RFC 8441 標準,另外可以在 iana 查閱已注冊的 websocket 子協(xié)議和擴展僻弹。具體的消息通信協(xié)議這里不做展開酝锅,網(wǎng)絡(luò)上有很多資料。并且還有很多已經(jīng)封裝好奢方、可以直接使用的 websocket 庫搔扁,有興趣的話可以自己寫一個,其實并不難蟋字。

值得在這里一提的是:若想在不支持 websocket 的客戶端完成雙工通信稿蹲,可考慮服務(wù)端提供一個 SSE 保持消息推送,客戶端使用 AJAX 或 Fetch 等手段鹊奖、通過 HTTP 協(xié)議向服務(wù)器發(fā)送信息苛聘。

Service Workers
一種可以構(gòu)建離線應(yīng)用的技術(shù),可參考:漸進式 Web 應(yīng)用PWA 應(yīng)用實戰(zhàn)设哗、Service Workers

這里的篇幅不足以解釋的更清楚唱捣,僅是為了列舉一個在這種技術(shù)下的的首部
Response: 設(shè)置離線服務(wù)的控制范圍
Service-Worker-Allowed: <path>

DNS over HTTPS (DoH
RFC 8484 發(fā)布標準,該技術(shù)與網(wǎng)絡(luò)開發(fā)者沒什么關(guān)系网梢。僅針對客戶端開發(fā)者震缭,一般指瀏覽器,但也包括如 cURL 等軟件战虏,目的是作為 DNS 的一種補充拣宰,保護 DNS 查詢時的用戶隱私,但也會帶來一些 風(fēng)險烦感,有興趣的可以看一下巡社。

其他 : 以下都不屬于標準協(xié)議,列舉幾個有意思的

Response: 這是 WordPress 定義的一個首部手趣,用于不同博客間的引用通知晌该,如果是開發(fā)博客類程序,可了解一下绿渣,這里有一篇 文章 對此作了說明
X-Pingback: <url>

Response: 針對搜索引擎一個首部气笙,告知其索引規(guī)則,可 參閱
X-Robots-Tag: googlebot: nofollow

Response: 告知錯誤報告的發(fā)送地怯晕,也許可以用在自動測試的相關(guān)程序上潜圃,詳情 參閱
NEL: { "report_to": "name_of_reporting_group", "max_age": 12345, "include_subdomains": false, "success_fraction": 0.0, "failure_fraction": 1.0 }

十、結(jié)尾

以上主要介紹 HTTP 報文舟茶、內(nèi)容傳輸方面的谭期,其實還有較為重要的 HTTP 請求方法 (擴展:IANA)和 HTTP 響應(yīng)代碼 (擴展:IANA),因為比較簡單吧凉,就不再展開隧出。

HTTP / 3 馬上也要來了,好消息是對于網(wǎng)絡(luò)應(yīng)用而言阀捅,并無特別需要注意的地方胀瞪,終于可以松一口氣了∷潜桑可以看到凄诞,HTTP 協(xié)議越來越復(fù)雜了,就在現(xiàn)在忍级,還有各種 草案 在不斷的提出帆谍、等候納入標準,IANA 還整理一份 Header 清單轴咱;伴隨著瀏覽器支持的接口越來越多汛蝙,提供的基礎(chǔ)能力越來越豐富烈涮,估計以后會越來越多。

不知道跟 google 有沒有關(guān)系窖剑,反正感覺 Chrome 占據(jù)絕對份額后坚洽,WEB 發(fā)展就越來越快了。google 自然有絕對的利益驅(qū)動這件事西土,如果大家都不搞 APP讶舰,全部以 WEB 提供服務(wù),google 恐怕做夢都會笑醒翠储。但也要小心 Chrome 屠龍少年變惡龍,成為新時代的 IE6橡疼,比如 名場面援所,若是 google 無法推動某項技術(shù)成為標準,借助 Chrome 也能搞成既定標準欣除,所以對于網(wǎng)絡(luò)應(yīng)用開發(fā)者住拭,建議大家最好是盡可能讓自己的產(chǎn)品兼容 Firefox 等其他瀏覽器。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末历帚,一起剝皮案震驚了整個濱河市滔岳,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌挽牢,老刑警劉巖谱煤,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異禽拔,居然都是意外死亡刘离,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門睹栖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來硫惕,“玉大人,你說我怎么就攤上這事野来∧粘” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵曼氛,是天一觀的道長豁辉。 經(jīng)常有香客問我,道長舀患,這世上最難降的妖魔是什么秋忙? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮构舟,結(jié)果婚禮上灰追,老公的妹妹穿的比我還像新娘堵幽。我一直安慰自己,他們只是感情好弹澎,可當(dāng)我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布朴下。 她就那樣靜靜地躺著,像睡著了一般苦蒿。 火紅的嫁衣襯著肌膚如雪殴胧。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天佩迟,我揣著相機與錄音团滥,去河邊找鬼。 笑死报强,一個胖子當(dāng)著我的面吹牛灸姊,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播秉溉,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼力惯,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了召嘶?” 一聲冷哼從身側(cè)響起父晶,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎弄跌,沒想到半個月后甲喝,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡铛只,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年俺猿,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片格仲。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡押袍,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出凯肋,到底是詐尸還是另有隱情谊惭,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布侮东,位于F島的核電站圈盔,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏悄雅。R本人自食惡果不足惜驱敲,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望宽闲。 院中可真熱鬧众眨,春花似錦握牧、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至狈定,卻和暖如春颂龙,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背纽什。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工措嵌, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人芦缰。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓企巢,卻偏偏與公主長得像,于是被迫代替她去往敵國和親饺藤。 傳聞我的和親對象是個殘疾皇子包斑,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容