为什么我们应该尽快支持 ALPN?

昨天有位朋友在微信上发过来一个链接:The day Google Chrome disables HTTP/2 for nearly everyone: May 31st, 2016中文翻译)。看标题这篇文章说的是 Google Chrome 即将大面积禁用 HTTP/2,这究竟是怎么回事呢?本文为你揭晓答案。

先来回顾一下,浏览器在访问 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 和 ALPN。

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--

提醒:本文最后更新于 201 天前,文中所描述的信息可能已发生改变,请谨慎使用。

专题「HTTP/2 相关」的其他文章 »

Comments