启用 HTTP/2 的 ALPN

什么是 ALPN?

为网站添加 HTTP/2 支持 这篇博文中,我们启用了 HTTP/2,但是并没有开启 ALPN (Application-Layer Protocol Negotiation)。虽然可以访问,但是在请求的时候还是绕了路子。

未开启 ALPN,加载 HTTP/2 网页的过程是这样的:

  • TLS 握手
  • 浏览器通过 HTTP/1.1 发送 "Upgrade:h2c" header
  • 服务器返回 101,切换至 HTTP/2
  • 通过 HTTP/2 通讯

如果开启了 ALPN,过程会简化很多:

  • 在 TLS 握手的报文中,浏览器表示支持的协议,服务器回复我可以支持 HTTP/2
  • 通过 HTTP/2 通讯

可见,开启 ALPN 还是有必要的。但是开启 ALPN 需要至少 OpenSSL 1.0.2+,所以还得重新编译一下。

编译 Nginx

在 Ubuntu 上打包安装新版 OpenSSL 这篇博文中,我们安装了最新版的 OpenSSL,接下来只要重新编译 Nginx 就好了。

保险起见,首先查看一下已有 Nginx 版本的编译参数:

nginx -V

记下参数,以后编译时会用到。


下载最新版 Nginx 源码,目前最新版是 1.9.12。如果你没有添加过 Nginx 的软件源:

wget http://nginx.org/download/nginx-1.9.12.tar.gz
tar xvf nginx-1.9.12.tar.gz

如果你添加了 Nginx 官方 mainline 软件源,可以直接通过

apt-get source nginx

下载源码,而不必 wget


进入目录:

cd nginx-1.9.12

记得刚刚记下的编译参数吗,带上它们执行 configure,我的是这样的:

./configure --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/etc/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-http_ssl_module --with-http_realip_module --with-http_addition_module --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_random_index_module --with-http_secure_link_module --with-http_stub_status_module --with-http_auth_request_module --with-http_xslt_module=dynamic --with-http_image_filter_module=dynamic --with-http_geoip_module=dynamic --with-threads --with-stream --with-stream_ssl_module --with-http_slice_module --with-mail --with-mail_ssl_module --with-file-aio --with-http_v2_module --with-cc-opt='-g -O2 -fstack-protector --param=ssp-buffer-size=4 -Wformat -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2' --with-ld-opt='-Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,--as-needed' --with-ipv6

可能会出现一些依赖问题,我整理了一下我自己缺的依赖,可能没有普适性。反正缺啥装啥:

sudo apt-get install libpcre3 libpcre3-dev libxml2 libxml2-dev libxslt-dev libgd2-xpm-dev geoip-database libgeoip-dev

再带上参数执行一次 configure 命令,基本上没问题就行了。开始编译:

make
sudo checkinstall

时间比较长,推荐用 Clang 编译器以节省时间,可以喝杯茶。喝完茶后差不多也该编译好了,打出的 deb 包在同目录下。系统中自己编译的新 Nginx 可以通过 dpkg 来管理,非常方便。


重启 Nginx:

sudo service nginx restart

测试是否成功开启 ALPN,以这个博客为例:

echo | openssl s_client -alpn h2 -connect niconiconi.xyz:443 | grep ALPN

如果有显示 ALPN protocol: h2 字样,说明 ALPN 工作正常。否则会显示 No ALPN negotiated


Enjoy!