理解 HTTP 協(xié)議對構(gòu)建網(wǎng)絡(luò)應(yīng)用是一個非常基礎(chǔ)的要求,比如爬蟲類程序,必須深入理解 Request 和 Resonse 各首部信息(當(dāng)然,這個前提是建立在對方站點(diǎn)完全遵循協(xié)議)。若是網(wǎng)站類程序,更需要理解這些含義,因為客戶端往往就是瀏覽器,一般來講都會嚴(yán)格遵循協(xié)議。參考:httpwg(全面權(quán)威)、MDN、wiki、協(xié)議
一、基礎(chǔ)(雜項)首部
Request 首部
請求主機(jī):端口
Host: domain.com:8080請求來源的 主機(jī):端口 和 頁面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ù)器時鐘不準(zhǔn)確,都必須發(fā)送該首部
Date: Wed, 21 Oct 2015 07:28:00 GMT在無法處理 Request method 時發(fā)送 405 響應(yīng),通過該首部告知客戶端可處理的 method (可以為空)
Allow: GET, POST, HEAD網(wǎng)站維護(hù),當(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)默認(rèn)不發(fā)送,http 若是廣告主,可能就需要設(shè)置該首部讓瀏覽器發(fā)送以便對方統(tǒng)計。
Referrer-Policy: no-referrer | unsafe-url | .....通常為一些 Css / Js 資源,指明 SourceMap 地址用于幫助在瀏覽器調(diào)試,但由于資源本事直接支持設(shè)置 SourceMap,所以該首部很不常用。非標(biāo)準(zhǔn)協(xié)議,但 支持度 還不錯。
SourceMap: /path/to/file.js.map針對瀏覽器的,設(shè)置允許 調(diào)試 加載信息的域名。比如當(dāng)前 response 為一張圖片,嵌入到其他網(wǎng)站,那么就可以使用該首部來 允許/禁止 該站點(diǎn)調(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 認(rèn)證
Response: 未認(rèn)證請求,服務(wù)端響應(yīng) 401,并告知認(rèn)證方式
WWW-Authenticate: <type> realm=<realm>Request: 客戶端攜帶認(rèn)證首部進(jìn)行訪問
Authorization: <type> <credentials>Response: 服務(wù)端為代理服務(wù)器,且也需要認(rèn)證;響應(yīng) 407,告知客戶端認(rèn)證方式
Proxy-Authenticate: <type> realm=<realm>Request: 同樣的,客戶端攜帶代理服務(wù)器的認(rèn)證信息
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.5Response: 指明響應(yīng)資源的 格式;字符集 (若 Response 可滿足客戶端需求)
Content-Type: text/html
Content-Type: text/html; charset=utf-8Response: 若無法滿足客戶端需求,返回 406 或 415
HTTP 406(告知無法返回所支持的格式)
HTTP 415(無法滿足需求,同時告知服務(wù)端可響應(yīng)的格式、字符集)
Accept-Patch: application/example, text/example
Accept-Patch: text/example;charset=utf-8一般情況下,服務(wù)端對格式協(xié)商不太準(zhǔn)守協(xié)議,無論客戶端是否可處理,總是任性的返回資源格式/字符集??蛻舳巳魺o法處理響應(yīng)格式,往往表現(xiàn)為 “保存為文件”。
若嚴(yán)格準(zhǔn)守協(xié)議,一般也是返回 406;即使返回 415 并告知支持格式,客戶端仍然無法進(jìn)行修正。因為客戶端通常已經(jīng)在 Accept 中已告知了所有可處理的格式,即使通過 Accept-Patch 告知也無濟(jì)于事。
但該首部仍然有一定意義,比如對于 API 接口,可根據(jù)請求返回 json 或 xml 以加強(qiáng)接口健壯性。
內(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.3Response: 當(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, brResponse: 響應(yīng)使用的壓縮算法
Content-Encoding: gzip
內(nèi)容傳輸:Accept-Ranges & Content-Disposition / If-Range & Range / Content-Length & Content-Range
Response: 對于首次請求,服務(wù)端應(yīng)返回是否支持的斷點(diǎn)傳輸,none 就是不支持
Accept-Ranges: none(缺?。?br>Accept-Ranges: bytesResponse: 希望客戶端如何處理: 使用默認(rèn)方式打開內(nèi)容 (inline) 或 保存為文件 (attachment)
Content-Disposition: inline(缺省)
Content-Disposition: attachment
Content-Disposition: attachment; filename="filename.jpg"Request: 若允許斷點(diǎn)下載,通過 If-Range (或 If-Match, 不推薦) 設(shè)置獲取到的 Etag (推薦) 或 Last-Modified,以便服務(wù)端判斷該文件在上次傳輸之后是否發(fā)生變動,能否續(xù)傳。
If-Range: "33a64df551425fcc55e4d42a148795d9f25f89d4"
If-Range: Wed, 21 Oct 2015 07:28:00 GMTRequest: 告知本次請求所需內(nèi)容范圍,可能為多段。start-end (end 為空代表請求到結(jié)束為止)
Range: bytes=200-1000, 2000-6576, 19000-Response: 返回內(nèi)容總長度,無論是否為續(xù)傳,該值總應(yīng)該返回
Content-Length: <length>Response: 對于斷點(diǎn)續(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; 這里是“希望”,而不是“必須”、“僅支持”??蛻舳酥С值膲嚎s算法由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-Encoding,Transfer-Encoding的特點(diǎn) :
- 必須包含
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ù)器負(fù)擔(dān),所以不應(yīng)該有兩個報文同時存在
● 該結(jié)論僅根據(jù)文檔得出,需實際驗證,事實上不同客戶端對此的理解可能有偏差。- 與斷點(diǎn)傳輸不同之處在于:分塊傳輸是在一次 HTTP 通信中將消息實體分塊傳輸;斷點(diǎn)傳輸?shù)拿恳淮蝹鬏敹际且粋€完整的 HTTP 通信,比如客戶端暫停下載后重新開始,或多進(jìn)程發(fā)送請求分段下載,最后合并。所以斷點(diǎn)傳輸按照正常請求響應(yīng),使用
Accept-Encoding/Content-Encoding協(xié)商機(jī)制即可。
內(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-Type和Content-Length,這對于常見的表單提交已夠用。但如果需要上傳大文件,是無法簡單通過 HTTP 協(xié)議進(jìn)行優(yōu)化的,需要前后端程序?qū)崿F(xiàn)相關(guān)邏輯,一般有以下優(yōu)化方向:
- 若需要斷點(diǎn)上傳,可考慮將 Request 實體分片傳輸(分片后可并發(fā)同時傳輸提升效率),這需要服務(wù)端程序配合,不再是協(xié)議范圍內(nèi)的知識了???參閱
- 瀏覽器默認(rèn)是不會對 Request 實體進(jìn)行壓縮的,可考慮使用如 pako 這樣的 JS 擴(kuò)展來壓縮傳輸實體以減少傳輸流量、提升速度。并使用
Content-Encoding報頭標(biāo)明壓縮算法。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)屬于標(biāo)準(zhǔn)協(xié)議,但 rfc7231 [page 92] 移除了該首部,目前瀏覽器客戶端已不會針對 Response 的 Content-MD5 進(jìn)行驗證,但這并不妨礙代理服務(wù)器進(jìn)行識別驗證,也能用于設(shè)計分片斷點(diǎn)上傳、分塊傳輸?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 或 ResponseDigest支持的算法可參見 草案,摘要值使用 Base64 格式。Digest僅針對消息實體,計算摘要時不能包括報文。且是針對完整消息,即使對于斷點(diǎn)傳輸,摘要也應(yīng)該是完整實體消息的摘要。該報文也可用于無實體消息響應(yīng),比如 Method Header 的請求,依然可以提前返回摘要信息。- 吐槽:為啥不用
Accept-Digest和Content-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)向一個過渡頁(如消息確認(rèn)、上傳進(jìn)度頁)
- 307 Temporary Redirect:臨時移動,日后應(yīng)仍訪問舊地址以確認(rèn)是否恢復(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>]這不是一個非標(biāo)準(zhǔn)首部,RFC 標(biāo)準(zhǔn)未規(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進(jìn)行緩存。- 若使用
Expires,強(qiáng)烈建議設(shè)置Date(報文創(chuàng)建時間,一般即為服務(wù)器的當(dāng)前時間,對于代理服務(wù)器,應(yīng)該緩存資源時的時間), 客戶端可使用Expires - Date = maxAge計算出緩存實際生命長度,若沒有 Date,將使用客戶端時鐘,但客戶端時鐘可能不準(zhǔn)確,便無法準(zhǔn)確計算。- 對于代理服務(wù)器,建議設(shè)置
Age(告知資源已在代理服務(wù)器上已存活的時長,不設(shè)置則認(rèn)為是 0),若未設(shè)置Age,客戶端可使用client_now - Date = Age計算緩存實際已消耗的生命長度,客戶端的最終對資源可緩存時長為maxAge - Age,這樣便可在過期后進(jìn)行了校驗了。但鑒于對客戶端時鐘的不信任,建議直接返回Age首部。- 推薦使用
Cache-Control: max-age直接設(shè)置,但建議同時返回Expires、Date、Age以兼容不支持Cache-Control的客戶端。更詳細(xì)說明可參見 RFC 2616在未過期時間內(nèi),客戶端都不應(yīng)重新請求服務(wù)端,而是直接使用緩存。如果使用瀏覽器驗證的話:
- 訪問資源,直接刷新,每次都會請求(在 Chrome 中已可看到 304 響應(yīng),注意不要勾選開發(fā)者工具的 "Disable cache");測試方法:打開新的 Tab 直接訪問,就可以看到,使用的是緩存。
- 也可以使用內(nèi)嵌元素,如 img,設(shè)置其 max-age,將其嵌套在另外一個頁面進(jìn)行測試,除非是 ctrl + F5 強(qiáng)制刷新,普通刷新會直接使用緩存。
- 設(shè)置緩存時長特別適用于靜態(tài)資源,現(xiàn)在很少有直接使用原 URL 替換靜態(tài)資源的,一般都是新建靜態(tài)資源替換,那么就可以設(shè)置盡可能長的 max-age 來利用緩存策略。
- 對于非靜態(tài)資源,這種方式無法讓用戶獲得及時的更新,可設(shè)置
max-age=0,即讓客戶端緩存,但每次都要請求服務(wù)端進(jìn)行校驗,校驗通過,僅響應(yīng) 304 即可,無需發(fā)送實體,減少數(shù)據(jù)傳輸。
緩存策略:Cache-Control & Pragma
用于 Response 首部
Cache-Control:no-store客戶端、代理服務(wù)器都不能緩存,設(shè)置該首部后,即使 Response 返回了指紋(如 Etag 或 Last-Modified),客戶端也不緩存,下次請求也不會攜帶指紋??捎糜陔S時會變動的頁面,如首頁、時間線頁面等。
Cache-Control:no-cache該名稱非常具有迷惑性,其實際作用并不是不能緩存,而是客戶端、代理服務(wù)器都可緩存,但每次都需要進(jìn)行校驗(前提是 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ù)器宕機(jī),未能校驗成功,有可能使用過期的緩存,代理服務(wù)器還應(yīng)發(fā)送warning報頭提醒。
Cache-Control:proxy-revalidate相當(dāng)于代理服務(wù)器使用must-revalidate策略,客戶端使用public策略
Cache-Control:no-transform針對代理服務(wù)器,不能對緩存內(nèi)容進(jìn)行轉(zhuǎn)換,比如為節(jié)省流量,有些代理服務(wù)器可能會對圖像格式進(jìn)行轉(zhuǎn)換。
Cache-Control:immutable針對客戶端,比如上面舉例中 刷新 或 ctrl+F5 強(qiáng)刷,設(shè)置該值是告訴瀏覽器即使在強(qiáng)刷情況下,也無需校驗,仍使用未過期緩存。該值有兼容性問題,并不是所有瀏覽器都已實現(xiàn),但對于確定永遠(yuǎ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;- 另外,瀏覽器總是會傾向于更嚴(yán)格的緩存策略,比如
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ù)器,若已緩存,不要進(jìn)行校驗,請直接返回??凑Z義,若未緩存,應(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,必須進(jìn)行新鮮度校驗。
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-cacheResponse: 請客戶端不要緩存資源
Pragma: no-cache
指紋驗證:ETag / If-Match & If-None-Match
Response: 返回資源的標(biāo)識符
Etag: "c0bea9ae76c87756d20d0dd9012f8a52"(包括引號,強(qiáng)驗證)
Etag: W/"0815"(弱驗證,W必須大寫)強(qiáng)弱與否,對于客戶端可能沒有意義,只是方便服務(wù)端知道。以便在下次驗證時做區(qū)分:對于強(qiáng)驗證,哪怕一個字節(jié)發(fā)生變化,都應(yīng)返回新資源;對于弱驗證,只有主體內(nèi)容變化,才需返回新資源(比如一個頁面內(nèi)容無變化,僅某一個廣告發(fā)生變化,可能就無需重新返回)
Request: 若客戶端緩存了上次 Response 資源,下次請求會攜帶
etag,有兩種攜帶方式
- 緩存驗證(較為常用)
If-None-Match: "bfc13a64729c4290ef5b2c2730249c88ca92d82d"
If-None-Match: W/"67ab43", "54ed21", "7892dd"
If-None-Match: *客戶端攜帶之前緩存的標(biāo)識符發(fā)送請求:
- 若服務(wù)端無法匹配標(biāo)識符(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ù)傳輸
- 避免“空中碰撞”
If-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"
If-Match: W/"67ab43", "54ed21", "7892dd"
If-Match: *比如編輯頁面,訪問頁面后獲取到 etag,在更新時攜帶獲取到的 etag:
- 若匹配成功(Match=true),說明該資源沒有被其他操作二次改動過,更新成功,返回正常的 200、新的 etag;
- 若匹配失?。∕atch=false),說明在提交前已被其他用戶修改,返回 412 響應(yīng)
時間驗證:Last-Modified / If-Modified-Since & If-Unmodified-Since
Response: 返回資源的最后修改時間
Last-Modified: Wed, 21 Oct 2015 07:28:00 GMTRequest: 若客戶端緩存了上次 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)定準(zhǔ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)于緩存的幾點(diǎ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; HttpOnlyRequest: 攜帶已設(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ù)雜,下面每一個首部可能都值得、或者說需要一篇單獨(dú)的文章才能講清楚,這里僅列一個大概
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-Policy、Cross-Origin-Embedder-Policy、Cross-Origin-Resource-Policy
用于降低跨域資源或釣魚造成的攻擊,可參考 XS-Leaks、跨域隔離、跨域策略Response:
Origin-Isolation
來源隔離機(jī)制: 參見Response:
Feature-Policy
在直接訪問或 Iframe 訪問時,啟用/禁用 瀏覽器的一些特性:參見Response:
X-Content-Type-Options
nosniff: 禁止瀏覽器對 css/js 進(jìn)行自動嗅探,降低 XSS 風(fēng)險:參見Response:
X-Frame-Options
確保網(wǎng)站沒有被嵌入到別人的站點(diǎn)里面,可參考 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è)計的報文,并未獲得廣泛良好的 支持,目前仍屬于 草案,需謹(jǐn)慎依賴。(吐槽一下,google 對這種收集客戶端信息的特別上心,參與提交草案,且 chrome 基本實現(xiàn),蘋果就比較反感這種有隱私顧慮的,估計 Safari 肯定不上心。另外 一例)
Response: 期望客戶端請求攜帶的信息、以及該配置的有效時長
Accept-CH: <list of client hints>,如Accept-CH: DPR, Viewport-Width
Accept-CH-Lifetime: <age>, 如Accept-CH-Lifetime: 86400Request: 根據(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 處理可能會更好一點(diǎn)。之所以設(shè)計出這種報文,可能是為了充分利用客戶端緩存功能,以便盡可能減少流量傳輸。所以這里就不得不說一下,若出于此目的,切記 Response 使用 Via 指明緩存有效的報文字段,如:
Via: DPR, Viewport-WidthRequest:客戶端在網(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)用、云廠商提供的負(fù)載均衡產(chǎn)品等。但了解這些報文,也能更好的使應(yīng)用程序與代理軟件交互。
Request & Response:
Cache-Control
有些值是專門針對代理服務(wù)器的,參見上面緩存策略章節(jié)說明Response: 報文的創(chuàng)建時間,已緩存時長,參見上面緩存時長章節(jié)說明
Date: Wed, 21 Oct 2015 07:28:00 GMT
Age: 300Request & 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 NodeResponse: 警告報文,返回給客戶端或下一級代理服務(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 首部來擬補(bǔ)這些損失,目前有部分首部已納入到協(xié)議標(biāo)準(zhǔn)。
Forwarded: by=<identifier>; for=<identifier>; host=<host>; proto=<http|https>但一些舊程序仍然在使用原來的首部,所以源服務(wù)端也應(yīng)該認(rèn)真對待
Request: 在進(jìn)入該級代理服務(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, ipRequest: 客戶端實際請求的 host,比如是 CDN 節(jié)點(diǎn) HOST,源服務(wù)器自身的 HOST 一般與此不同。
X-Forwarded-Host: id42.cdn.comRequest: 客戶端請求實際的 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標(biāo)準(zhǔn)首部
Forwarded其實就是合并版,其中for格式如下,ipv6 會使用方括號括起來
Forwarded: for=192.0.2.43, for="[2001:db8:cafe::17]"; host=id42.cdn.com; proto=httpsForwarded
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ù)了,該值的介紹有點(diǎn)模糊,根據(jù)實際情況而定。注意:由于該值十分容易偽造,所以對于源服務(wù)器而言,應(yīng)該僅信任確定是代理服務(wù)器發(fā)送的值。
Request: 可以看到,代理服務(wù)器可能有多個,這對于客戶端,就需要一直等候,所以這里有一個新 草案 報文,按照協(xié)議,每經(jīng)一級代理服務(wù)器,該值就減小 1,當(dāng)減小至 0 時,不應(yīng)繼續(xù)向上級請求,而是直接返回。另外有一個關(guān)于該報文的 問題
Max-Forwards:3
八、跨域請求
若是符合以下條件的簡單請求:
- Method 為 : GET、POST、HEAD
- 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 包含認(rèn)證信息,比如 cookie / authorization headers / TLS client certificates
Access-Control-Allow-Credentials: true
(如果 Response 包含該報文,Access-Control-Allow-Origin不能使用 “*” 通配符,必須指定域名)
(如果 Response 不含該報文,而 Request 又發(fā)送了認(rèn)證信息,瀏覽器會攔截響應(yīng),即客戶端無法獲得資源)Response: 允許暴漏給外部的響應(yīng)首部,默認(rèn)僅暴漏 簡單首部
Access-Control-Expose-Headers: <header-name>, <header-name>, ...
非簡單請求,客戶端會首先使用 OPTIONS Method 預(yù)檢請求
Request: 告知即將使用何種 Method 發(fā)送請求
Access-Control-Request-Method: PUTRequest: 告知會攜帶的非安全 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-HeadersResponse: 預(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。簡單介紹下版本的選擇機(jī)制:
- 客戶端直接使用 HTTPS 請求,Nginx 等服務(wù)器軟件可使用的 ALPN 協(xié)商讓客戶端直接使用 HTTP/2 協(xié)議,應(yīng)用層無感。
- 若服務(wù)端 ALPN 協(xié)商告知不支持 HTTP/2 或 客戶端使用 HTTP 請求:當(dāng)下,瀏覽器都默認(rèn)使用 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)站。所以如果需要支持這種請求,就需要給域名單獨(dú)分配一個 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é)商機(jī)制,請求報文不會包括相關(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 標(biāo)準(zhǔn)確定之前,好多 1.0 客戶端已會發(fā)送
Host首部、支持復(fù)用 TCP(發(fā)送Connection首部),所以服務(wù)端對于 1.0/1.1 請求以收到的首部為準(zhǔn)。- 服務(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è)計該機(jī)制,2 不支持協(xié)商(用意應(yīng)該是不能降級,后續(xù)版本的協(xié)商方式也不是通過首部)
Connection: Upgrade
Upgrade: h2c
HTTP Method
HTTP/1.0 協(xié)議只定義了 GET、HEAD、POST;其他的一些 Method 是在 1.1 甚至是補(bǔ)充協(xié)議中定義的。對于一些嚴(yán)格驗證 Method 的服務(wù)端,比如一些 REST API 服務(wù)端,那么在無法發(fā)送 PUT 等 Method 的客戶端通常會使用 POST 發(fā)送,但通過首部來指明 Method 語義,常用的有以下兩個
X-METHOD-OVERRIDE: PUT
X-HTTP-METHOD-OVERRIDE: PUT
HTTP/2 相關(guān)
該版本進(jìn)行了大量優(yōu)化,但大部分細(xì)節(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ù)的有效性,默認(rèn)為否。- 想像一下,是不是可以用國內(nèi)服務(wù)器給未備案的域名加速呢,感覺云廠商也會防堵這個。不過,該報文對于提高服務(wù)可用性還是用處不小的,可嘗試一下。另外,該技術(shù)是不是可以在一定程度上預(yù)防 DNS 投毒。
Request: 若客戶端使用備選服務(wù)發(fā)出請求,會攜帶該報文告知所選用的主機(jī)
Alt-Used: uri-host [ ":" port ]
HTTPS 相關(guān):
互聯(lián)網(wǎng)已經(jīng)基本進(jìn)入了 HTTPS 時代(無論是使用 HTTP/1.1 還是 HTTP/2,現(xiàn)在絕大多數(shù)網(wǎng)站都開啟了 HTTPS),但由于要對舊世界兼容,所以仍然支持 HTTP。這對不少場景造成了一些問題,表現(xiàn)尤為嚴(yán)重就是流量劫持、中間人攻擊,下面幾個報文主要是為了降低這種問題帶來的危害。Response: HSTS(HTTP Strict Transport Security,RFC6797),嚴(yán)格使用安全協(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??蛇x:includeSubDomains,子域名是否也強(qiáng)制啟用 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 會在手機(jī)內(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ù),最重要的就是保護(hù)好證書私鑰。但這仍然不能保證完全安全,比如著名的 DigiNotar 事件,如果 CA 機(jī)構(gòu)出現(xiàn)問題,對于使用其證書的公司很難第一時間得到預(yù)警,消除影響。
- 于是有了 certificate transparency 用于監(jiān)測審計證書透明度,以便第一時間消除影響。該機(jī)構(gòu)推進(jìn)證書廠商支持 CT,好消息是現(xiàn)在幾乎所有證書頒發(fā)機(jī)構(gòu),包括 Let's Encrypt 都已支持 CT。網(wǎng)站運(yùn)維人員可以在 google、crt.sh 搜索查看證書的透明度報告。
- 由 Google 主導(dǎo)的 RFC 6962 則是為了更進(jìn)一步,服務(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)該不會再有瀏覽器支持了,就不再細(xì)說了。
WebSocket 相關(guān)
還記得上面說的Upgrade首部吧,該首部作用就是將 HTTP/1.1 升級為其他協(xié)議,瀏覽器利用該特性新增了 WebSocket 協(xié)議以支持雙向通信。Request: 客戶端發(fā)出請求
Connection: Upgrade
Upgrade: websocket(通知服務(wù)端要升級為 websocket 協(xié)議)
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==(客戶端標(biāo)識)
Sec-WebSocket-Version: 13(使用的 websocket 協(xié)議版本)
Sec-WebSocket-Protocol: soap, wamp(不常用: 使用的 websocket 子協(xié)議)
Sec-WebSocket-Extensions: permessage-deflate(不常用: 客戶端支持的 websocket 擴(kuò)展)
Origin: https://domian.com(其他任意 HTTP 首部)Response: 服務(wù)端響應(yīng)
Sec-WebSocket-Accept: s3p=(由于 Sec-WebSocket-Key 計算的值,返回給客戶端驗證)以上便完成了 HTTP 到 WebSocket 的升級。當(dāng)然,升級過程對客戶端應(yīng)用程序開發(fā)者而言一般是透明的,即無需了解具體細(xì)節(jié),比如瀏覽器提供了 相關(guān)API,微信小程序提供的 相關(guān)API,只需要直接使用 API 操作即可,無需關(guān)心通信是如何建立的。
但如果是需要以程序方式使用客戶端,或創(chuàng)建服務(wù)端,則需要自行處理協(xié)議,服務(wù)器軟件一般不提供??蓞⒖?RFC 6455 和 RFC 8441 標(biāo)準(zhǔn),另外可以在 iana 查閱已注冊的 websocket 子協(xié)議和擴(kuò)展。具體的消息通信協(xié)議這里不做展開,網(wǎng)絡(luò)上有很多資料。并且還有很多已經(jīng)封裝好、可以直接使用的 websocket 庫,有興趣的話可以自己寫一個,其實并不難。
值得在這里一提的是:若想在不支持 websocket 的客戶端完成雙工通信,可考慮服務(wù)端提供一個 SSE 保持消息推送,客戶端使用 AJAX 或 Fetch 等手段、通過 HTTP 協(xié)議向服務(wù)器發(fā)送信息。
Service Workers
一種可以構(gòu)建離線應(yīng)用的技術(shù),可參考:漸進(jìn)式 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ā)布標(biāo)準(zhǔn),該技術(shù)與網(wǎng)絡(luò)開發(fā)者沒什么關(guān)系。僅針對客戶端開發(fā)者,一般指瀏覽器,但也包括如 cURL 等軟件,目的是作為 DNS 的一種補(bǔ)充,保護(hù) DNS 查詢時的用戶隱私,但也會帶來一些 風(fēng)險,有興趣的可以看一下。
其他 : 以下都不屬于標(biāo)準(zhǔn)協(xié)議,列舉幾個有意思的
Response: 這是 WordPress 定義的一個首部,用于不同博客間的引用通知,如果是開發(fā)博客類程序,可了解一下,這里有一篇 文章 對此作了說明
X-Pingback: <url>Response: 針對搜索引擎一個首部,告知其索引規(guī)則,可 參閱
X-Robots-Tag: googlebot: nofollowResponse: 告知錯誤報告的發(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 請求方法 (擴(kuò)展:IANA)和 HTTP 響應(yīng)代碼 (擴(kuò)展:IANA),因為比較簡單,就不再展開。
HTTP / 3 馬上也要來了,好消息是對于網(wǎng)絡(luò)應(yīng)用而言,并無特別需要注意的地方,終于可以松一口氣了。可以看到,HTTP 協(xié)議越來越復(fù)雜了,就在現(xiàn)在,還有各種 草案 在不斷的提出、等候納入標(biāo)準(zhǔn),IANA 還整理一份 Header 清單;伴隨著瀏覽器支持的接口越來越多,提供的基礎(chǔ)能力越來越豐富,估計以后會越來越多。
不知道跟 google 有沒有關(guān)系,反正感覺 Chrome 占據(jù)絕對份額后,WEB 發(fā)展就越來越快了。google 自然有絕對的利益驅(qū)動這件事,如果大家都不搞 APP,全部以 WEB 提供服務(wù),google 恐怕做夢都會笑醒。但也要小心 Chrome 屠龍少年變惡龍,成為新時代的 IE6,比如 名場面,若是 google 無法推動某項技術(shù)成為標(biāo)準(zhǔn),借助 Chrome 也能搞成既定標(biāo)準(zhǔn),所以對于網(wǎng)絡(luò)應(yīng)用開發(fā)者,建議大家最好是盡可能讓自己的產(chǎn)品兼容 Firefox 等其他瀏覽器。