從 HTTP 瞎逼逼到 HTTP/2

我真的是在瞎逼逼,由於光是 HTTP,就足夠講一塊磚頭了。並且,HTTP 只是協議棧的應用層的其中一個協議:),不過其餘協議都不在本文討論範圍以內。若有疏漏,請指正。html

概述:HTTP vs HTTPS vs HTTP/2 vs SSL vs TLS:這些都是啥子跟啥子?

不少縮寫詞被用於描述客戶端與服務器端交流的過程。這些詞常常被不熟悉內部原理的人混淆。html5

HTTP (Hypertext Transfer Protocol) 是客戶端和服務器端都必須實現的基本交流協議。它涉及到請求 (requests),響應 (responses),會話 (sessions),緩存 (caching),認證 ( authentication) 以及更多。在這個協議以及 HTML (Hypertext Markup Language) 上的工做,開始於 1989 年,由在 CERN 的 Sir Tim Berners-Lee 和他的團隊主持。這個協議的第一個官方版本 (HTTP 1.0) 發佈於 1996 年,隨後在 1997 年發佈瞭如今普遍使用的版本 (HTTP 1.1)。webpack

這個協議在瀏覽器和服務器間傳輸明文 (clear) 信息,容許經過傳輸信息的網絡查看傳輸的信息。這會產生安全的擔心,因此 HTTP Secure (HTTPS) 被引入了。HTTPS 容許客戶端和服務器端先創建一個加密的通道,而後經過這個通道傳輸明文信息,有效地防止了信息的竊聽。git

加密通道是經過 Transport Layer Security (TLS) 協議建立的,這個協議之前叫 Secure Socket Layer (SSL)。這兩個術語常常互換着使用。SSL 3.0 正在被 TLS 1.0 替代。SSL 是 Netscape 開發的協議,而 TLS 是一個 IETF 標準。在寫這篇文章的時候,全部版本的 SSL (1.0, 2.0, 3.0) 都因爲許多的安全問題而被廢棄,使用它們將會在現代瀏覽器中產生警告,TLS 版本 (1.0, 1.1, 1.2) 正在使用中,1.3 版本如今仍是草案。github

因此,在大約 1996 或 1997 的某個時間,咱們有了互聯網的當前穩定版本 (有或者沒有 SSL/TLS 的 HTTP 1.1),這個 HTTP 版本仍然驅動着如今絕大多數網站。之前,HTTP 用於非敏感通訊,好比閱讀新聞,而 HTTPS 用於敏感通訊,好比認證和電子商務。然而,對隱私權的關注不斷增長,一些瀏覽器如 Google Chrome 如今會標記 HTTP 網站 爲 「not private」,將在將來針對 HTTP 引入警告。web

HTTP 協議的下一代升級版 —— HTTP/2,於 2015 年發佈,正在被愈來愈多的網站所採納。這個協議增長了新的特點,如 壓縮 (compression),多路複用 (multiplexing),請求優先級 (prioritization of requests)等,用於減小等待時間、提高性能和安全性。ajax

在 HTTP 1.1 版本,安全鏈接是可選的,你可使用 HTTP 或 HTTPS,但在 HTTP/2,安全鏈接幾乎是強制的,即便標準定義了有 TLS 的 HTTP/2 和沒有 TLS 的 HTTP/2,但大部分瀏覽器提供商已經聲明它們將只支持擁有 TLS 的 HTTP/2。瀏覽器

HTTP

HTTP 的基本性質

簡單

HTTP 的消息被設計爲簡單可讀。這一點只要你打開瀏覽器控制檯看看請求和響應頭就知道了。緩存

可擴展

除了規範規定的那些 header,咱們會看到不少非官方規定的 header,這些擴展的 header 能夠用來實現新的功能。安全

無狀態

這意味着即便你對服務器發起過一次請求,當你再發起一次請求時,服務器也不知道你拜訪過它,基本的 HTTP 請求中是不含狀態信息的。所幸咱們能夠增長 cookie 的頭部擴展來記錄狀態信息。

使用可靠鏈接

HTTP 依賴於運輸層的 TCP 協議進行消息傳遞,而 TCP 是可靠的。

狀態碼

狀態碼的第一個數字表明當前響應類型

  • 1xx - 信息,請求已被服務器接收,繼續處理

  • 2xx - 成功,請求已成功被服務器接收、理解、並接受

  • 3xx - 重定向,須要後續操做才能完成這一請求

  • 4xx - 客戶端錯誤,請求含有詞法錯誤或者沒法被執行

  • 5xx - 服務器端錯誤,服務器在處理某個正確請求時發生錯誤

HTTP 的請求方法

較少用的方法

  • OPTIONS:請求服務器告知其支持的各類功能。能夠詢問服務器一般支持哪些方法,或者對某些特殊資源支持哪些方法

  • HEAD:向服務器發出指定資源的請求,但服務器在響應中只返回首部,不會返回實體的主體即資源的 body 部分

  • PUT:向服務器寫入資源

  • DELETE:請服務器刪除請求 URL 所指定的資源

  • TRACE:HTTP 請求在傳輸的過程當中是可能會被修改的。這個方法容許客戶端在最終將請求發送給服務器時,看看它變成了什麼樣子。主要用於測試或診斷

  • CONNECT:這個方法將請求的鏈接轉換爲透明的 TCP/IP 隧道,一般用於加快基於 SSL 加密的通訊

  • PATCH:這個方法請求對資源進行局部修改

經常使用方法

GET && POST

GET 是最經常使用的方法,用於請求服務器發送某個資源。POST 方法用於向服務器發送數據,注意這和 PUT 的寫入資源是不一樣的,寫入的資源是存儲在服務器的,而發送的數據會發送到其餘地方去處理,這可能會致使新資源的建立或已存在資源的更新,POST 一般用於支持 HTML 表單。

這兩個方法有什麼區別?

冪等
在數學中,冪等有兩種主要含義:

  • 在某二元運算下,冪等元素是指被本身重複運算的結果等於它本身的元素。例如 0 * 0 仍然爲 0,因此 0 在乘法下是冪等的。

  • 某一元運算爲冪等的時,其做用在任一元素兩次後會和其做用一次的結果相同,例如 恆等函數 f(x) = x 就是冪等的。

在計算機網絡中,假如在不考慮諸如錯誤或者過時等問題的狀況下,若干次請求的反作用與單次請求相同或者根本沒有反作用,那麼這些請求方法就可以被視做是「冪等」的。

咱們知道,GET 用於表單數據提交時,表單數據會被編碼在請求的 URI 上,這樣極不安全,由於請求的地址可能在情趣到達目的地以前被打印,而後被第三方看到。而 POST 提交的數據是放在請求體中的,比 GET 提交數據安全,固然,沒有 HTTPS,POST 提交的數據也能被看到。

可是最主要的區別,應該爲以下三點:

  • 安全性(safe): GET 請求是安全的,這裏的安全是指 GET 方法只獲取信息而不作其餘操做。而 POST 請求則是不安全的,由於它會更新或建立資源。

  • 冪等性(idempotent): GET 請求是冪等的,冪等就是上面所說的若干次請求的反作用與單次請求相同,而 POST 請求則是不冪等的。

  • 可緩存性(cacheable): GET 請求的響應是可緩存的,POST 請求的響應通常是不可緩存的,除非設置了合適的 Cache-Control 或 Expires 頭部字段。

HTTPS

爲何優先要使用 HTTPS?

有三個主要緣由:

  • 機密性 (Confidentiality)

這能保護通訊的雙方在公共媒介中免遭信息竊聽。若是沒有 HTTPS,當你經過 WiFi 接入點網上購物時,WiFi 接入點的運營者就能看到你的信用卡之類的隱私信息。

  • 數據完整性 (Integrity)

這能保證信息能不受改變地完整到達目的地。好比,WiFi 能夠在咱們的網站上增長廣告,下降圖片質量或者改變文章的內容。HTTPS 能確保網站不被更改。

  • 身份驗證 (Authentication)

這能保證網站不會是假冒的,域名是哪一個,站點就是哪一個。好比,一些運行 WiFi 接入點的猥瑣之人能夠把瀏覽器導向一些假冒的網站。HTTPS 能確保example.com就是真的example.com,有些安全證書甚至要檢查網站的合法身份,讓你知道yourbank.comYourBank, Inc,即 YourBank 公司。

使用 HTTP 頭部字段保護你的 web 應用

禁用祕密信息的緩存

瀏覽器默認緩存 HTTP 的 GET 請求的響應,若是使用的是共享我的電腦 (網吧不就是麼?),那麼猥瑣之人就能經過訪問瀏覽器的緩存獲得他人的隱私信息。因此咱們要在返回隱私信息時使用三個響應頭字段來禁用客戶端的緩存:

  • Cache-Control: no-cache, no-store, max-age=0, must-revalidate
    cache-control 字段用來指定在此次的請求/響應鏈中的全部緩存機制都必須遵照的指令。上面的三個指令告訴客戶端和代理在使用緩存前必須送請求到服務器進行驗證,不要儲存響應,在請求發生以後的 0 秒後緩存即過時,在使用緩存的響應前檢查其狀態並禁止使用過時的響應。

  • Pragma: no-cache
    這個字段用於向後兼容 HTTP 1.0,確保老客戶端能不緩存響應。

  • Expires: -1
    這個字段用於指定響應過時的時間戳。指定爲-1的話,客戶端就會當即把響應當作過時的,這就避免了緩存。

請在須要保護隱私時才禁用緩存,不然你的應用加載速度將大打折扣。

強制 HTTPS

HTTPS 的重要性我在前面已經說了,要強制 HTTPS,咱們要使用 HTTP Strict Transport Security (HSTS) 頭部字段,具體能夠設置爲Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

max-age是指示瀏覽器爲當前域名緩存這個頭部字段指定秒數,這裏是緩存一年。includeSubDomains指示瀏覽器將 HSTS 應用於當前域名的全部子域名。preload強制瀏覽器老是安全地加載你的 web 應用,甚至在你第一次訪問網站且未收到響應前,這個特性是經過將一個安全預加載的域名的列表硬編碼到瀏覽器代碼中實現的,要使用這個特性你須要在 HSTS Preload List Submission 註冊你的域名,固然啦,使用這個指令你得確保你的應用能只使用 HTTPS。

容許 XSS 過濾

XSS 即跨站腳本攻擊,是很廣泛也很簡單的網絡攻擊手法。咱們會使用X-XSS-Protection: 1; mode=blockX-XSS-Protection這個字段指示瀏覽器是否爲當前頁面開啓瀏覽器內建的 XSS 過濾機制並覆蓋瀏覽器自己的相關設置。1表示容許過濾器,反之爲0mode=block指示瀏覽器在檢測到 XSS 攻擊後禁止加載整個頁面。

控制 Framing

一個 iframe 是一個能夠容許在一個父 web 應用中嵌套一個 web 應用的 DOM 元素。這個元素可讓點擊劫持 ( clickjacking) 更容易。點擊劫持能夠欺騙用戶點擊非用戶本意的東西。爲了阻止這種攻擊,咱們使用X-Frame-Options: SAMEORIGINX-Frame-Options這個字段指示瀏覽器是否容許你的 web 應用嵌套在另外一個 web 應用中,SAMEORIGIN 表示你的應用能夠在同域名頁面的 frame 中展現。注意當在 Content-Security-Policy 字段中指定了 frame-ancestors 時,則 X-Frame-Options 被忽略,見 這裏

明確白名單

CSP (Content Security Policy) 定義了一個很是強大的基於瀏覽器的安全機制,容許對你的 web 應用中的資源加載和腳本執行加以控制。有了它,你能夠將容許腳本加載、ajax 調用、圖片和樣式表加載的域列入白名單,能夠容許或禁止內聯腳本和動態腳本等。咱們能夠經過簡單設置Content-Security-Policy: script-src 'self'來容許同源的腳本加載以及阻止動態腳本和內聯腳本執行。Content-Security-Policy 是個比較複雜的頭部字段,詳細的配置能夠在這裏看到。

阻止 Content-Type 嗅探

爲了使用戶體驗更連貫,不少瀏覽器實現了一個叫 「Content-Type 嗅探」 或 「MIME 嗅探」 的功能,這個功能使瀏覽器能經過實際資源的比特位檢測 HTTP 中響應資源的類型而不理會響應頭中的Content-Type字段聲明的資源類型。但這會致使 MIME 混淆攻擊 (MIME confusion attack)。因而乎,咱們要使用X-Content-Type-Options: nosniff,指示瀏覽器在處理獲取的資源時不要使用嗅探。

HTTP/2

多數主流瀏覽器已經在2015年末支持了該協議。此外,根據 W3Techs 的數據,在2017年5月,在排名前一千萬的網站中,有13.7% 支持了HTTP/2。這真真是極吼滴!有圖有真相。

http2 support

HTTP/2 相比 HTTP 1.x 的主要區別

是二進制的而非文本的

有別於 HTTP/1.1 中的明文請求,HTTP/2 將一個 TCP 鏈接分爲若干個流 (stream),每一個流中能夠傳輸若干消息 (message),每一個消息由若干最小的二進制幀 (frame) 組成。這樣更利於高效地解析,並且不容易出錯,畢竟 HTTP/1.x 的 header 中有空白行、大小寫、換行、空行之類的規定。

是徹底多路複用而非按順序和阻塞的

HTTP/1.x 有一個 head-of-line blocking 的問題,它會讓一個鏈接一次只能發送一個請求。多路複用容許多個請求和響應消息同時發出,甚至能夠混合一個消息的一部分和另外一個消息。

只開一個鏈接用於併發的請求

HTTP/1.x 中爲了加載資源會同時打開多個 TCP 鏈接,每一個鏈接在響應時又都會發送大量數據,存在中間網絡 (intervening network) 緩衝區溢出的危險,致使網絡阻塞和重發 ( retransmits)。並且,使用那麼多的 TCP 鏈接也是一種大量佔用網絡資源的行爲。

壓縮頭部

在大型網站中,一個頁面每每要請求大量資源並獲得相應,算上那些往返的話,那麼頭部就會佔據至關大的開銷,因此壓縮頭部的好處便變得顯而易見了。

容許服務器主動推送資源給客戶端

在 HTTP/1.x 中,當瀏覽器請求了一個頁面,服務器發送了 HTML 頁面的響應,而後服務器須要等待瀏覽器解析了 HTML 文件後再發起嵌入在 HTML 頁面中的多個資源的請求,想一想都以爲慢。而服務器端推送避免了這種往返的延遲,服務器會主動推送它認爲的客戶端會須要緩存的資源。要當心的是,這個功能濫用的話,會損害性能。

如何擁抱 HTTP/2?

HTTP/2 是向後兼容 HTTP/1.1 的,因此徹底不須要擔憂現有的網站會發生什麼問題。然而,不少對於 HTTP/1.1 來講是最佳實踐的技巧卻會在使用 HTTP/2 時下降性能。再者,從 HTTP/1.1 轉向 HTTP/2 勢必是漫長的,由於服務器端升級容易,但只要一日絕大多數人仍然使用着老舊甚至是史前瀏覽器,客戶端就會是扎心窩的痛。咱們須要作些過渡時期的事。

轉向 TLS

第一要務是讓你的網站運行在安全鏈接上,由於廠商大佬們已經說過了:咱們哥們幾個只支持有 TLS 的 HTTP/2,那些不支持 TLS 的能夠歇菜了。因此國內大片的 HTTP 網站們要好好考慮轉向 HTTPS 的事了。

雪碧圖再也不老是最佳的選擇

在 HTTP/1.1 中,對於瀏覽器來講,獲取一個巨大的圖片文件比請求多個小圖片文件高效,這是由於多個請求會互相排隊,增長加載時間。一般的作法是把多個小圖片轉成一個雪碧圖。

轉成一個雪碧圖就只須要一個 HTTP 請求了。可是咧,若是一個頁面中只使用到雪碧圖中的一個圖標,仍然下載整個雪碧圖就顯得不是很好了。HTTP/2 擁有多路複用的能力,因此多個請求排隊的事已經不存在了,不少時候單個地請求圖片將是更好的選擇。固然,須要使用全部圖標時,雪碧圖仍然是須要的。

使用 Data URI 內嵌圖片是一種阻礙

在 HTTP/1.1 中,爲了減小 HTTP 請求,你能夠把圖片直接以 Data URI 的形式嵌入 CSS 或 HTML 文件中。由於轉成的字符編碼會很長,天然增長了 CSS 或 HTML 文件的大小。在 HTTP/2 中,HTTP 請求變得廉價,這種 「最佳實踐」 便變成了一種妨礙。

合併 CSS 文件和 JavaScript 文件未必好

咱們在應用的構建階段,一般會合並那些小的 CSS 和 JavaScript 文件,本意是減小 HTTP 請求。可是在 HTTP/2 中,HTTP 請求是廉價的,合併便再也不顯得有優點。可能更糟糕的是,若是你由於合併引入了不少非本頁面所需的文件,反而拖慢了加載速度,固然,你是能夠經過 webpack 來配置相應頁面所需的相應文件的,但是別忘了,合併文件是要引入大量處理合並的代碼的,在 HTTP/2 廉價的請求中,這些多餘的處理代碼看來有點多餘,止增笑耳。

域名分片可能會坑你

在 HTTP/1.1 中,每一個域名能打開的鏈接是受到限制的,大概爲 6 - 8 個,具體取決於瀏覽器實現。若是你實在要加載大量資源,其中一個方法就是從多個域名中獲取資源,這就是域名分片 (domain sharding)。這個方法能實現更好的加載時間,也有能致使問題,固然,準備這種服務就會有額外的開支。HTTP/2 則移除了域名分片的需求,在 HTTP/2 下,瀏覽器只爲每一個域名打開一個鏈接,可是人家有多路複用嘛,併發請求的數量根本不受限制 (固然也能夠經過 SETTINGS_MAX_CONCURRENT_STREAMS 來限制)。並且,域名分片在 HTTP/2 中還會損害性能,由於它建立了額外的 TCP 鏈接,妨礙 HTTP/2 爲資源進行優先級排序。

當前對於 HTTP/1.1 的最佳實踐是限制域名分片爲 2 個域名。好消息是爲了減小工做並在 HTTP/1.1 和 HTTP/2 都能達到很好的效果,有方法可讓 HTTP/2 合併你的鏈接,具體方法見 Google 家的 slide 26

實現

可喜的是,如今已經有大量的 HTTP/2 的服務器端實現了,涵蓋了多種語言,參見
Implementations

Reference

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息