為什麼我們應該儘快支援 ALPN?
提醒:本文最後更新於 916 天前,文中所描述的資訊可能已發生改變,請謹慎使用。
先來回顧一下,瀏覽器在訪問 HTTPS 網站時,如何得知服務端是否支援 HTTP/2?答案是藉助 HTTP/2 的協議協商機制:在 HTTP/2 Over HTTP 中,可以使用 HTTP 的 Upgrade 機制進行協商;而對於 HTTP/2 Over TLS,可以使用 TLS 的 NPN 或 ALPN 擴充套件來完成協商。HTTP/2 的這兩種協商方式,不瞭解的同學請看《談談 HTTP/2 的協議協商機制》,這裡不再贅述。
當前所有瀏覽器,都只支援 HTTP/2 Over TLS。也就是說,瀏覽器和服務端都支援 NPN 或 ALPN 協商,是用上 HTTP/2 的大前提
NPN(Next Protocol Negotiation,下一代協議協商),是一個 TLS 擴充套件,由 Google 在開發 SPDY 協議時提出。隨著 SPDY 被 HTTP/2 取代,NPN 也被修訂為 ALPN(Application Layer Protocol Negotiation,應用層協議協商)。二者目標一致,但實現細節不一樣,相互不相容。以下是它們主要差別:
- NPN 是服務端傳送所支援的 HTTP 協議列表,由客戶端選擇;而 ALPN 是客戶端傳送所支援的 HTTP 協議列表,由服務端選擇;
- NPN 的協商結果是在 Change Cipher Spec 之後加密傳送給服務端;而 ALPN 的協商結果是通過 Server Hello 明文發給客戶端;
大部分 Web Server 都依賴 OpenSSL 庫提供 HTTPS 服務,對於它們來說,是否支援 NPN 或 ALPN 完全取決於使用的 OpenSSL 版本。通常,如果在編譯時不特意指定 OpenSSL 目錄,Web Server 會使用作業系統內建的 OpenSSL 庫。
OpenSSL 1.0.2 才開始支援 ALPN,當前主流伺服器作業系統基本都沒有內建這個版本。以下是一份粗略的統計(via):
作業系統 | 內建 OpenSSL 版本 |
---|---|
CentOS 5 | 0.9.8e |
CentOS 6 | 1.0.1e |
CentOS 7 | 1.0.1e |
Ubuntu 14.04 LTS | 1.0.1f |
Ubuntu 16.04 LTS | 1.0.2g |
Debian 7 (Wheezy) | 1.0.1e |
Debian 8 (Jessie) | 1.0.1k |
這份表格列舉的系統中,只有最近剛釋出的 Ubuntu 16.04 才內建了支援 ALPN 的 OpenSSL 1.0.2。也就是說,直接使用系統 OpenSSL 庫的 Web Server,極有可能不支援 ALPN 擴充套件。
去年,Google 在 Chrome 47 中移除了對 NPN 的支援,只支援 ALPN,但很快就引發一大批 HTTP/2 網站的抱怨。最終,Google 不得不在接下來的版本中又重新啟用了 NPN。
半年後的今天,Google 又一次決定在 Chrome 51 中移除 NPN,預計 5 月底釋出。這一次恐怕不會再反悔了。
本文開頭那篇文章的作者悲觀地認為:現在 OpenSSL 1.0.2 的普及程度仍然太低,Chrome 現在去掉對 NPN 的支援,仍然會導致一大批不支援 ALPN 的 HTTP/2 網站最終無法協商到 HTTP/2,只能降級使用 HTTP/1.1。
對此,我的觀點是,該來的總會來,既然不能改變結果,不如早做準備。
通過 OpenSSL 命令列工具,可以快速檢視自己的 HTTP/2 服務是否支援 ALPN 擴充套件:
openssl s_client -alpn h2 -servername imququ.com -connect imququ.com:443 < /dev/null | grep 'ALPN'
如果提示 unknown option -alpn
,說明本地的 OpenSSL 版本太低(可通過 openssl version
檢視),請升級到 1.0.2+。如果不方便升級,也可以使用 Qualys SSL Labs's SSL Server Test 這個線上工具來測試。
如果結果包含 ALPN protocol: h2
,說明服務端支援 ALPN,不受 Chrome 51+ 去掉 NPN 的影響。
如果結果包含 No ALPN negotiated
,說明服務端不支援 ALPN,在 Chrome 51+ 中無法協商到 HTTP/2,需要儘快升級。
由於 OpenSSL 是系統基礎庫,大量其他軟體都對它有依賴,如果直接升級系統自帶的 OpenSSL,很容易引發各種問題。更為穩妥的做法是在編譯 Web Server 時自己指定 OpenSSL 的位置。例如,我在《本部落格 Nginx 配置之完整篇》這篇文章中提供的 Nginx 編譯步驟中,就通過 --with-openssl
指定了新版 OpenSSL 原始碼路徑,這樣編譯出來的 Nginx 就會用上最新的 OpenSSL 庫。
如果你在用 LibreSSL 做為 Web Server 的 SSL 庫,需要升級到 2.1.3+ 才支援 ALPN。
OkHttp 是一個 Android 下比較常用的 HTTP 客戶端,支援 HTTP 和 HTTP/2 協議。它在一年前也移除了對 NPN 的支援,所以支援 HTTP/2 但不支援 ALPN 的網站,在 OkHttp 客戶端中會降級到 HTTP/1.1。
Chrome 51 這次也會徹底移除對 SPDY 的支援,如果你的網站還只支援 SPDY,趁這個機會直接升級到 HTTP/2 + ALPN 吧。
--EOF--
發表於 2016-05-18 22:58:52 ,並被新增「 HTTP/2 、 Chrome 、 HTTPS 、 ALPN 」標籤 。檢視本文 Markdown 版本 »
提醒:本文最後更新於 916 天前,文中所描述的資訊可能已發生改變,請謹慎使用。