天天看點

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

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

經過一番研究,原來是從 chrome 51 開始,在 2016 年 5 月 31 日之前,對支援 npn 協商協定的 http/2 網站還會采用 http/2 通路;而之後就隻支援 alpn 協商協定的 http/2 網站了——而目前 alpn 協定僅被鮮少有發行版支援  openssl-1.0.2 支援。

<a target="_blank"></a>

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

ubuntu 14.04 lts

1.0.1f

ubuntu 16.04 lts

1.0.2g

alpn 和 npn

debian 7.0

debian 8.0

1.0.1k

從上面我們可以看到,基本上所有的伺服器級的 linux 發行版都不支援 openssl 及 alpn,唯一支援的 ubuntu 16.04 lts 顯然用的不會很多。不要小看這  0.0.1 的版本差異,對于别的軟體來說這 0.0.1 的差異基本上可以忽略,但是對于 openssl 來說,那就是兩個版本代際。openssl 是個相當底層的庫,很多重要的軟體都依賴于它,是以各個發行版在更新 openssl 時采用的态度是相當保守,比如我們可以看看 centos 系統中有哪些軟體使用了 openssl:

<code>$ lsof | grep libssl | awk '{print $1}' | sort | uniq</code>

<code>anvil</code>

<code>fail2ban</code>

<code>gdbus</code>

<code>gmain</code>

<code>httpd</code>

<code>postfix</code>

<code>mysqld</code>

<code>networkmanager</code>

<code>nginx</code>

<code>php-fpm</code>

<code>puppet</code>

<code>sshd</code>

<code>sudo</code>

<code>tuned</code>

<code>zabbix_agent</code>

沒有經過足夠的測試,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 51 開始,谷歌就去掉了對 spdy 的支援,不過這不是個事,因為不但使用 spdy 的 web 伺服器比較少,而且從 spdy 更新到 http/2 也很簡單,這方面 nginx、apache 等伺服器的配置都很簡單。

但不幸的是,在 chrome 51 中,谷歌也去掉了對 npn 的支援!如果你的 web 伺服器使用的是 openssl-1.0.2 以下的版本,不支援 alpn 協商,那麼 chrome 51 及以後版本就會以 http/1 協定通路你的網站。

而在這種情況下,你原本支援 http/2 的網站通過連接配接複用等 http/2 所提供的新特性,在 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:

<code># wget https://www.openssl.org/source/openssl-1.0.2j.tar.gz</code>

<code># tar -zxvf openssl-1.0.2j.tar.gz</code>

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

<code>./configure --with-openssl=../openssl-1.0.2j --with-http_v2_module --with-http_ssl_module</code>

配置并編譯之後,你可以用 <code>nginx -v</code>來看一下你的 nginx 中的 openssl 版本。

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

除了自己編譯之外,如果你的系統環境中已經有了容器支援,你還可以在容器中運作一個 ubuntu 16.04 lts,并将 web 伺服器運作在其中。

原文釋出時間為:2017-11-06

本文來自雲栖社群合作夥伴“linux中國”