閱讀867 返回首頁    go 阿裏雲 go 技術社區[雲棲]


為什麼 Chrome 又不支持我的 HTTP/2 網站了?

昨晚偶爾清理 Chrome 插件時發現我的 “HTTP/2 and SPDY indicator”插件好像好久沒亮了。這個插件在你訪問到一個支持 HTTP/2 (或之前的 SPDY 協議)的網站時會點亮,而我明明記得之前專門讓https://linux.cn/ 支持了 HTTP/2 。

我的第一反應是不是這個插件有問題了?於是打開 Chrome 調試工具,然後發現,真的是請求和響應都是 HTTP/1.1 哎!

經過一番研究,原來是從 Chrome 51 開始,在 2016 年 5 月 31 日之前,對支持 NPN 協商協議的 HTTP/2 網站還會采用 HTTP/2 訪問;而之後就隻支持 ALPN 協商協議的 HTTP/2 網站了——而目前 ALPN 協議僅被鮮少有發行版支持  openssl-1.0.2 支持。

發生了什麼?

服務器端

我們知道,最初的 Web 訪問協議是 HTTP/1,包括以前的 HTTP/1.0 和現在大部分網站采用的 HTTP/1.1(HTTP/0.9 是試驗性協議,已經廢棄)。但是隨著 Web 應用越來越複雜,之前的 HTTP/1.x 協議就看起來不能滿足日益龐雜的 Web 服務需求了。比如說,明文請求、請求複用等問題。因此,穀歌就開發了一個新的傳輸層協議,名為 SPDY。由於這個新的協議用了的人都說好,因此穀歌就把這個協議提交到了 IETF,然後大家覺得,SPDY 這名字不好聽(SPDY 是穀歌的注冊商標),就幹脆叫 HTTP/2 吧!

SPDY 協議是基於 SSL/TLS 的,穀歌開發了一個名為下一代協議協商Next Protocol Negotiation(NPN)的 SSL/TLS 擴展,用於在客戶端連接服務器時協商是否采用 HTTP/2 協議。SPDY 協議是由 Web 服務器所實現支持的,而 NPN 則是由 OpenSSL 等 SSL 實現支持的。

但是,隨著 SPDY 被提交到 IETF,然後變成了 HTTP/2 協議,穀歌也放棄了 SPDY 的開發,全力投入到了 HTTP/2 的開發中,之前所采用 NPN 也被一種新的協商協議 ALPN ——應用層協議協商Application-Layer Protocol Negotiation所替代。NPN 和 ALPN 是不兼容的,它們的主要不同是:

  • NPN 是服務器發送所支持的協議列表,由客戶端進行選擇。而 ALPN 則是客戶端發送該列表,由服務端選擇。
  • 在 NPN 中,最終的選擇結果是在 Change Cipher Spec 之後發送給服務端的,也就是說是被加密了的。而在 ALPN 中,所有的協商都是明文的。

這樣做的好處主要是安全性方麵的考慮,但是這造成了一個問題就是,NPN 已經廣泛地被 OpenSSL 支持,而 ALPN 則目前隻有最新的 openssl-1.0.2 才支持。當前的幾個主流 Linux 發行版的 OpenSSL 版本以及支持的協商協議如下:

Linux 發行版 OpenSSL 版本 所支持的協商協議
CentOS/Oracle Linux/RHEL 5.10+ 0.9.8e 不支持
CentOS/Oracle Linux/RHEL 6.5+, 7.0+ 1.0.1e NPN
Ubuntu 12.04 LTS 1.0.1 NPN
Ubuntu 14.04 LTS 1.0.1f NPN

Ubuntu 16.04 LTS

1.0.2g

ALPN 和 NPN

Debian 7.0 1.0.1e NPN
Debian 8.0 1.0.1k NPN

從上麵我們可以看到,基本上所有的服務器級的 Linux 發行版都不支持 OpenSSL 及 ALPN,唯一支持的 Ubuntu 16.04 LTS 顯然用的不會很多。不要小看這  0.0.1 的版本差異,對於別的軟件來說這 0.0.1 的差異基本上可以忽略,但是對於 OpenSSL 來說,那就是兩個版本代際。OpenSSL 是個相當底層的庫,很多重要的軟件都依賴於它,因此各個發行版在升級 OpenSSL 時采用的態度是相當保守,比如我們可以看看 CentOS 係統中有哪些軟件使用了 OpenSSL:


  1. $ lsof | grep libssl | awk '{print $1}' | sort | uniq
  2. anvil
  3. fail2ban
  4. gdbus
  5. gmain
  6. httpd
  7. postfix
  8. mysqld
  9. NetworkManager
  10. nginx
  11. php-fpm
  12. puppet
  13. sshd
  14. sudo
  15. tuned
  16. zabbix_agent

沒有經過足夠的測試,Linux 發行版是不會在產品級(服務器級)的環境中隨便升級的。為了解決舊版本(1.0.1)中的安全問題,他們寧可將新的版本(1.0.2)中安全修複移植回舊版本,也不會升級到有新功能的新版本(1.0.2),這就是你見到了各種 1.0.1e、1.0.1k 這樣的版本號的原因。

當然,你可以自己編譯一個最新 OpenSSL 替代你係統中的 openssl-1.0.1,但是我想你不會這樣做的,是吧?

順便提一句,NPN 和 ALPN 可以並存,但是會客戶端會優先選擇 ALPN。

瀏覽器端(Chrome)

從 Chrome 51 開始,穀歌就去掉了對 SPDY 的支持,不過這不是個事,因為不但使用 SPDY 的 Web 服務器比較少,而且從 SPDY 升級到 HTTP/2 也很簡單,這方麵 Nginx、Apache 等服務器的配置都很簡單。

但不幸的是,在 Chrome 51 中,穀歌也去掉了對 NPN 的支持!如果你的 Web 服務器使用的是 openssl-1.0.2 以下的版本,不支持 ALPN 協商,那麼 Chrome 51 及以後版本就會以 HTTP/1 協議訪問你的網站。

穀歌對放棄 NPN 支持做了一個簡短的解釋,但是不管怎麼說,NPN 協議在 Chrome 51 之後的版本不會再次回來了。而另一方麵,OpenSSL 在 2016 年 12 月 31 日之後也不會繼續發布 openssl-1.0.1 係列的新版本了,安全修複到此為止。

而在這種情況下,你原本支持 HTTP/2 的網站通過連接複用等 HTTP/2 所提供的新特性,在 Chrome 下訪問取得了不錯的體驗,而現在又跌回了之前的殘舊狀態。

怎麼辦呢?

有幾種辦法:

換瀏覽器

山不來就我,我去就山。Chrome 51+ 不支持帶 NPN 的 HTTP/2 網站,作為瀏覽者,你可以使用其它的瀏覽器,比如 Safari、Edge 之類的。這樣,你就可以用新的協議來訪問世界上那 10% 支持 HTTP/2 的 Web 服務器了。

但是,作為服務器運營者,你卻不能忽視高達 50% 以上的 Chrome 用戶

換服務器

如上麵所示,Ubuntu 16.04 LTS 是目前唯一官方支持 openssl-1.0.2 的 Linux 發行版,如果你一直采用 Ubuntu 做服務器,考慮一下升級吧。LTS 版本的支持期長達五年。

當然,在產品環境中,即便你是 Ubuntu 服務器,更新版本也是一件重大事宜,宜慎思之。

重新編譯

既然換服務器不是一個好的選擇,那你還有一個方案,就是使用新的 openssl-1.0.2 源代碼重新編譯你的 Web 服務器,比如 nginx。

下麵我簡單介紹一下如何用 openssl-1.0.2 來編譯 nginx。(1.0.2 係列的最新版本是 1.0.2j,當然你要非用 1.1.0,我也無話可說……)

首先下載並解壓 openssl-1.0.2j:


  1. # wget https://www.openssl.org/source/openssl-1.0.2j.tar.gz
  2. # tar -zxvf openssl-1.0.2j.tar.gz

然後在編譯 nginx 的時候使用 --with-openssl=../openssl-1.0.2j 選項以及你的其它選項:


  1. ./configure --with-openssl=../openssl-1.0.2j --with-http_v2_module --with-http_ssl_module

配置並編譯之後,你可以用 nginx -V來看一下你的 nginx 中的 OpenSSL 版本。

這種自行編譯的好處是靈活性高,但是你需要隨時注意各個組件是否有嚴重的安全漏洞,並在出了修複版本之後重新編譯。

容器

除了自己編譯之外,如果你的係統環境中已經有了容器支持,你還可以在容器中運行一個 Ubuntu 16.04 LTS,並將 Web 服務器運行在其中。

原文發布時間為:2017-11-06

本文來自雲棲社區合作夥伴“Linux中國”

最後更新:2017-06-05 14:32:17

  上一篇:go  C++ 程序員 Protocol Buffers 基礎指南
  下一篇:go  《JavaScript開發框架權威指南》——第2章 Grunt 2.1安裝Grunt