更快更安全的 HTTP 2

如果說有一項技術可以讓你的網站瀏覽起來更快、更安全、SEO 加分,而且網站工程師不需要改 code 就可以全站使用。更重要的是,還不用額外花錢,天底下有這麼好的事情嗎?

話說 HTTP 通訊協定是全球資訊網(WWW)的基礎,是瀏覽器和網站伺服器之間的通訊協定。而HTTP/2是 HTTP/1.1 從 1999 年發布以來,十六年來的最重要的一次升級,這次升級的主要目的,就是為了改善瀏覽器的網頁下載速度(Page Load Time)。

 

眼見為憑,以下是一些測試網站,比較 HTTP/1.1 和 HTTP/2 的速度差異,這在圖多的情況最為明顯:

 

講起 HTTP/2 的歷史,筆者在兩年前曾經在 WebConf 大會上介紹 SPDY這個通訊協定。SPDY 是 Google 狹著 Chrome 瀏覽器龐大市佔率,在 2009-2016 年所進行的大規模網路實驗,在獲得了顯著的成功之後,進一步將 SPDY 推向網路標準,終於在 2015/5 正式發布了。目前包括 Chrome、Safari、Firefox、IE/Edge 瀏覽器都已經支援(詳見Can I Use?),而你平常瀏覽的 Yahoo、Facebook、Google、ALPHA Camp Blog 等等一線大廠網站,也都已經支援上 HTTP/2。在不知不覺中,HTTP/2 已經進入使用者的生活。

因此,現在也是時候全面普及,身為網站主你不可不知道 HTTP/2。

HTTP/2 改了什麼?

HTTP/2 在語意上完全向下相容,它改進的是底層通訊封包的實作。Web 開發者熟悉的 HTTP 操作如 GET/POST 操作、HTTP Status Code 和各種 HTTP Headers 都沒有改變,完全與 HTTP 1.1 兼容。因此伺服器端要使用 HTTP/2,完全不需要修改 HTML/CSS/JavaScript 網頁和你的後端程式,只要升級和設定 Web 伺服器軟體,加上支援 HTTP/2 的瀏覽器,就可以使用這個超快的通訊協定。


http2-1

要了解 HTTP/2 施展了什麼魔法,我們先來回顧一下現今網頁載入速度的挑戰是什麼。

隨著網頁內容越來越複雜,造成了要完成一個網頁載入(Page Load)的動作,除了要下載 HTML 之外,還需要下載 CSS 檔案、JavaScript 檔案、各種圖片檔案,零零總總加起來需要已經多達上百個對伺服器的 Request 請求資源,大大影響了網頁載入的速度。在這一秒鐘幾十萬上下的時代,Amazon 的網頁載入時間每多一秒,該公司的年度營收就減少 16 億美元、Google 的搜尋時間每多 0.4 秒,每天的搜尋次數就會減少 8 百萬網頁、KISSmetric 分析報告指出超過 4 秒 Bounce Rate 增加 25%。人的思緒在等 1 秒後就開始飄移,如果需要等 10 秒,就會感覺這東西是不是壞了。

這個問題的最大原因,在於 HTTP/1.1 有一個非常大的缺陷是每個對伺服器的 Request 資源請求,都必須佔用一個網路連線(TCP connection),傳完一個檔案才能再傳下一個,瀏覽器無法同時下載。因此在 HTTP/1.1 時代,瀏覽器為了加速下載的時間,只好同時允許六個網路連線(TCP connection)併發去連接伺服器,好可以達成同時下載六個資源。但是極限也是如此了,並不是說無限制增加網路連線就可以解決這個瓶頸,因為每一次的網路連線,都必須經過三次握手的初始網路連線程序,而且每次初始連線因為流量控制的關係,一開始的網路封包會傳輸比較慢,後來才逐漸加快。

也因為 HTTP/1.1 的這個限制,當今 Web 開發者針對網站效能優化時,發展出了各種奇技淫巧來加快網頁下載速度,稍候我們會提到這些在新的 HTTP/2 技術下,完全是多餘的。

那麼,HTTP/2 是如何改良的呢?它採用的解法包括:

  • 只需要單一網路連線(Single TCP connection),就可以連接網站伺服器,下載所有需要的資源。大大節省 HTTP/1.1 需要一直建立多個網路連線時的啟動時間浪費。
  • 連線多工(Multiplexing),在單一網路連線上,就可以同時傳輸多個 HTTP Request 和 Response,併發請求 CSS/JS/Images 等等資源。它的原理是將 Requests/Responses 都拆碎成小 frames 進行傳輸,而這些 frames 是可以交錯的,因此檔案再多也不怕,不會發生佔用網路連線(TCP connection)的情況。這就是為什麼在圖檔多的情況下,HTTP/2 特別有優勢。
  • 優先權設計(Prioritization),伺服器可以決定例如 CSS 或 JavaScript 檔案,哪些要優先傳送。
  • Header 壓縮,在 HTTP/1.1 的 Headers 其實是沒有壓縮的,大小佔了約 200 bytes 到 2KB 不等,而且同一瀏覽器的每個 Requests 其實絕大部份的 Headers 都是重複的。HTTP/2 用了HPACK壓縮技術,大大減少每次都要重複傳輸一樣的 Headers。
  • Binary 二進位的封包結構設計,對伺服器和瀏覽器來說,可以更快的解析這些資料。冷知識:在 HTTP/1.1 定義了四種解析訊息的方式,在 HTTP/2 只需要一種。
  • 伺服器主動推送資源(Server Push),允許伺服器除了 HTML 之外,連同需要的 CSS/JavaScript/Images 檔案,主動推到瀏覽器的快取之中。不過,這個功能比較有爭議,一來他需要 Web 開發者額外描述有哪些檔案需要隨著 HTML 一起推送給瀏覽器,不是 Web 伺服器升級 HTTP/2 就自動會有。二來它不管瀏覽器是不是已經有快取這個資源,都會推送而造成頻寬浪費。因此實務上筆者認為可以改用瀏覽器的Prefetch功能,讓客戶端的瀏覽器自己處理即可。

透過這些技術,讓瀏覽器的網頁下載時間大大降低。而我們網站主需要做的,就是升級 Web 伺服器到支援 HTTP/2。

對網站開發者會有什麼影響嗎?

剛才提到 Web 開發者在 HTTP/1.1 限制下,為了改善網頁下載速度發展出了各種奇技淫巧,其中因為 HTTP/1.1 一個 HTTP Requests 請求,就會佔用一個網路連線,所以有很多招數都是想減少 Requests 的數量,讓我們來看看有哪些:

  • 合併圖片(Image Sprites),為了減少瀏覽器發送 Requests 的數量,就把很多小圖(例如Icon)合併成一張大圖下載,然後透過 CSS 樣式去切出其中一個小圖。這一招用起來其實很麻煩,因為每次新增小圖或修改,整張大圖都要重新產生過。
  • 合併 CSS 和 JavaScript 檔案,也是為了減少瀏覽器發送 Requests 的數量。但是開發的時候一定會拆成不同檔案才比較好維護,而最後佈署到伺服器時,需要額外去進行把檔案合併的動作。
  • 內插 CSS、JavaScript 或圖片,也是為了減少瀏覽器發送 Requests 的數量,就把原本應該獨立的檔案,直接內插到 HTML 裡面。圖片會用 Base64 編碼成純文字後置入。但這招會破壞瀏覽器快取機制,本來是可以單獨快取這些靜態資源的,內插後反而沒有快取了,而且圖片實際大小會變大浪費頻寬。
  • Domain 切分(Domain Sharding),瀏覽器針對同一個網址只能開六個網路連線,為了突破這個限制,網站者可能會拆多個子網域,用不同網址來下載圖片。另外也因為拆分不同 Domain 的關係,可以讓瀏覽器的 Cookie 不會送到這些次要網域,減少一點頻寬浪費。

以上這些技巧,都會讓網站開發和佈署增加不少額外的麻煩,而在 HTTP/2 通訊協定下,都已經變得不需要了 (歡呼!)

需要 HTTPS 加密連線

為了能夠順利讓瀏覽器能相容現存的 HTTP 網站(使用通訊埠 Port 80),所以 HTTP/2 選擇佈署在 HTTPS (使用通訊埠 Port 443) 上,因此在安裝 HTTP/2 的步驟中,要先擁有 TLS/SSL 安全性憑證。另一方面,這也是全面推廣網站安全的契機,除了有更快速度,同時也要求了更好的網路安全。

使用 HTTPS 加密連線的好處有:

  • 保護瀏覽器和伺服器之間的傳輸,不會被別人修改。這不一定指惡意的駭客中間人攻擊,也可能只是想插入廣告。例如有些咖啡店的免費 Wifi 會在網頁上面插入廣告。但是如果是 HTTPS 網站就沒辦法了。
  • 保護了使用者的隱私安全,不會被人竊聽。特別是需要登入的網站,沒有 HTTPS 加密,非常容易就可以被人竊取帳號密碼。
  • Google 把 HTTPS 的網站當作SEO 加分
  • 很多新的瀏覽器功能也開始把 HTTPS 當作必要條件,例如Geolocation 定位ServiceWorker等等功能
  • Apple 宣布2016 年底,所有 iOS app 的網路連線都必須用加密連線 HTTPS,不可以用 HTTP 了。

綜合以上,HTTPS 加密連線可說是當今應用程式網站必備的基本安全需求。

如何安裝 HTTP/2

第一步:取得 TLS/SSL 安全性憑證

首先要取得 TLS/SSL 安全性憑證檔案。這個憑證是不能自己簽發的,這樣瀏覽器會有警告畫面:

http2-2

早先 TLS/SSL 安全性憑證一定是向憑證機構購買,這樣瀏覽器才會認得你是合法的憑證(這些瀏覽器在出廠時就已經設定好要相信哪些憑證機構),例如 alphacamp.co 是在NameCheap這家代理商購買的。好消息是,為了推廣 HTTPS 安全連線,去年由許多大公司以及各大非營利團體共同贊助推出Let's Encrypt這個服務,免費發佈 TLS/SSL 憑證。中文使用說明可以參考letsencrypt.tw/,或是用certbot這個工具。不過因為它的使用方式,跟一般我們在憑證機構網站上購買的步驟不太一樣,需要在主機上下指令來取得憑證,所以也有人設計了一個線上工具SSL For Free可以在線上就完成憑證申請。

第二步:設定 Web 伺服器

接著設定 Web 伺服器。Apache 可以使用mod_http2,而 Nginx 在1.10 stable後就內建支援了(建議用 mainline branch 的版本,也就是 1.11)。設定 SSL 的設定檔可以參考Mozilla SSL Configuration Generator的建議,其中除了設定你憑證的檔案位置之外,還可以讓伺服器打開 HSTS 這個 HTTP Header,告訴瀏覽器這個網站只用 HTTPS 加密連線,不要用 HTTP,這樣使用者就不會不小心用到 HTTP 不安全的連線了。但是要注意一旦啟用,期限內就沒有辦法讓使用者改回用 HTTP 囉,建議可以在最後全站檢查沒問題後再啟用。另外還有一點要注意,就是伺服器上的 OpenSSL 函式庫需要升級到 1.0.2g,你可以在編譯 Nginx 時特別搭配這個版本,或是乾脆使用較新的 Ubuntu Linux 16.04 伺服器作業系統,詳情請參考Supporting HTTP/2 for Google Chrome Users這篇文章的說明。

如果上述的兩個步驟還是太麻煩,或是你沒有管理伺服器的經驗,筆者推薦可以使用CloudFlare這個網站加速服務(CDN),它有免費方案提供 TLS/SSL 憑證以及 HTTP/2 連線。也就是說你的網站伺服器可以什麼都不用改,只需要修改 DNS 讓使用者的連線會先經過 CloudFlare 伺服器即可。使用者到 CloudFlare 的這一段傳輸,就會是有加密的 HTTP/2 連線。CloudFlare 在全球各地都有節點,今年四月也終於在台北佈點了,所以網路速度非常好。

第三步:替換 HTTP 網址為 HTTPS

我們需要將所有 HTTP 網址都換成 HTTPS 網址。網頁上所有連到自己網站的超連結、圖片、JavaScript 和 CSS 等等網址,都必須修改成 https:// 開頭。而所有外部系統中你的網址,例如 Facebook 廣告、Google Analytics 和其他網站分析追蹤工具,都必須把網址改成 https:// 開頭。

第四步:最後檢查

先檢查 TLS/SSL 安全性憑證安裝正確,可以使用SSL Server Test這個線上工具。接著檢查 HTTP/2 有啟用,使用 Chrome 瀏覽器在網址輸入觀察有哪些網站在使用 HTTP/2,或是你可以安裝HTTP/2 and SPDY Indicator。線上測試工具則可以用HTTP/2 Test。更多工具可以參考Tools for debugging, testing and using HTTP/2

第五步:全面啟用

檢查沒有問題後,我們可以設定 Web 伺服器只允許 HTTPS 連線,所有連線到 HTTP 的情求,全部轉址(301 Redirect)到 HTTPS 連線,並且打開 HSTS 這個 HTTP Header 強制使用者瀏覽器只能用 HTTPS 連線。

最後的成果就是會有一個綠色的 Icon 出現在網址列:

http2-3

導入的困難點是?

看起來好像很簡單,就是設定就可以了,有沒有什麼困難點呢?筆者認為處理 TLS/SSL 安全性憑證只是小麻煩而已,更何況如果用CloudFlare連申請憑證的步驟都不需要。真正會麻煩的地方,恐怕還是從 HTTP 改成 HTTPS 的這個過程會比較辛苦。如果你本來就不是全站走 HTTPS 通訊協定,一旦從 HTTP 換成 HTTPS,就很容易碰到Mixed content混合內容的瀏覽器警告,意思是說你在加密的 HTTPS 的網頁之中,使用到非加密的 HTTP 的資源,這個資源可能是 JavaScript、CSS、圖片或是 iframe (不包括超連結)。

如果只是圖片和影片用到非加密的 HTTP 網址,那麼瀏覽器的綠色安全鑰匙 Icon 就會不見:

http2-4

但如果是 CSS 或 JavaScript 的話,那瀏覽器就根本不載入了,你的網頁就會破版無法正常運作。

因此,為了不要讓瀏覽器出現這個警告,讓所有平台下的瀏覽器下都可以正常運作,你得將 HTTPS 網頁中的所有資源,都一一檢查將 HTTP 替換成 HTTPS。如果當初是用相對網址就沒事,但是如果有用到絕對網址 http:// 開頭的,例如直接連結使用其他網站的資源,那改起來可能就會很麻煩費事,而且容易百密一疏。另外,也要教育訓練一下相關編輯網頁的同事,不然一不小心綠色安全鑰匙 Icon 又不見了。

如果是歷史大站,這樣升級可能是真的改不完,一個過度的方案是使用upgrade-insecure-requests幫忙,透過在 Web 伺服器上設定這個 HTTP header,瀏覽器就會自動將網頁上的 HTTP 資源自動替換成 HTTPS 網址、站內的超連結也會自動替換成 HTTPS。不過這個 HTTP header 需要瀏覽器支持,支援度可以參考Can I Use?

關於 Mixed content,Google Developer 網站上也有一篇完整的教學Fixing mixed Content

另外還有一些自己不可解的問題,例如使用到第三方服務的資源,例如 iframe 或 javascript 的 widget ,而該服務不夠進步(請寫信給對方抱怨,附上這篇文章的超連結) 竟然沒有提供 HTTPS 連線,這樣就沒辦法整合進你的網頁了,除非你自己搞個代理伺服器,但這也太麻煩了,不如換一家更好的第三方服務。

以上向大家介紹 HTTP/2 通訊協定,希望有越來越多的網站都開始使用 HTTP/2,讓我們的網路環境更快更安全。

原作者補充:

1. http/2.0 標準最終沒有要求 https,而是目前的瀏覽器實做都要求 https,所以才會變成現在的 http/2.0 都需要跑 https ,這可能跟當初 SPDY 是強制要求 https 有關,畢竟大部分的 http/2.0 規格都是從 spdy 演變來的,一方面是為了安全,一方面可能是降低成本直接參考拿 spdy 的實做來修改。

2. 關於 http/2.0 的內建支援,nginx 應該是從 v1.9.5 就開始了(https://www.peterdavehello.org/2015/09/nginx-now-supports-http2-0/) (mod_h2 則是 Apache v2.4.17 後內建)

3. openssl 的問題其實是因為 Chrome v51 之後已經拔掉了 NPN (Next Protocol Negotiation) ,必須 改用 ALPN(Application-Layer Protocol Negotiation),而 ALPN 的支援是在 openssl v1.0.2+ 的版本開始的,其實不需要到 v1.0.2g,另外就是 Firefox 目前都還是支援 NPN ,使用 openssl v1.0.1 編譯出來的 nginx 仍然是支援 http/2.0 的,只是因為沒有 ALPN 的關係,Chrome v51 之後的版本就沒辦法使用 http/2.0 了

 

 

原文 https://ihower.tw/blog/archives/8489

(本文 crossposting 於ALPHACamp Blog)