Edited at

nginxをHTTP/2対応にする方法(Chrome 51以降でも有効にする)

More than 1 year has passed since last update.

CentOS6.7上にnginxでSSL設定する際にHTTP/2も設定してみましたが、Google ChromeではHTTP/2が有効にならずとても大変だったので今回の手順を記録します。

追記 2016/09/05 nginx stable(1.10以下)でHTTP/2を利用する場合、iOS SafariでPostする場合にエラーが発生する場合があります。後述参照。

追記 2016/09/30 mainlineバージョンをSRPMからビルドできました。(コメントありがとうございます。)

追記 2017/12/02 nginx側の記事でNPN/ALPN対応表(引用)更新しました。

追記 2017/12/02 各種ブラウザのNPN非対応を追記しました。


Google ChromeでHTTP/2が有効にならない理由

Google Chrome51(2016/4)で SPDY/3.1,NPNが削除され ALPNのみ対応 になりました。



Remove Support for SPDY/3.1

Remove TLS Next Protocol Negotiation (NPN)



API Deprecations and Removals in Chrome 51 | Web Updates - Google Developers

CentOS6.7にyumでnginxをインストールした場合、NPNのみ対応しておりALPNは対応していません。ChromeはALPNに対応していないサーバーに接続すると、HTTP/2接続をあきらめHTTP/1.1にフォールバックします。

(Firefox48.0.2はNPMに対応しているので、HTTP/2で接続されます。)


NPN・ALPNとは?

ブラウザとWEBサーバーの間で接続方式を決定する手順です。ALPNではクライアントがHTTP/2・SPDY・HTTP/1.1等の接続方式リストをサーバーに提示し、その中からサーバーが接続方式を選択します。




TLS上でのプロトコルネゴシエーションの仕組み、NPNとALPN - あすのかぜ


追記)各種ブラウザのNPN非対応状況(2017年)

NPNに対応していないブラウザです。


However, by mid‑2017 all of the most popular browser vendors except Safari (version 10) had dropped support for NPN, starting with the following versions (see the Protocol Details section on the page for each browser at Qualys SSL Labs): >

Chrome 51

Edge 12

Firefox 53

Internet Explorer 11

Opera 38

Supporting HTTP/2 for Google Chrome Users | NGINX



nginxでALPNに対応する方法

nginxは1.9.5でHTTP/2に対応しています。


The ngx_http_v2_module module (1.9.5) provides support for HTTP/2 and supersedes the ngx_http_spdy_module module.




Module ngx_http_v2_module

ただし、nginxはTLS接続にOpenSSLを利用しており、OpenSSLは 1.0.2以上でALPNに対応しています。

OpenSSL 1.0.2以上を利用すればよいのですが、現時点でのLinuxディストリビューションのほとんどはALPN未対応のOpenSSL1.0.1系です。

2016年時点


The table summarizes operating system support for ALPN and NPN.

Operating System
OpenSSL Version
ALPN and NPN Support

CentOS/Oracle Linux/RHEL 5.10+
0.9.8e
Neither

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

Debian 7.0
1.0.1e
NPN

Debian 8.0
1.0.1k
NPN



Supporting HTTP/2 for Google Chrome Users | NGINX

2017年9月時点


The table summarizes Linux operating system support for ALPN and NPN as of September 2017.

Operating System
OpenSSL Version
ALPN/NPN Support

CentOS/Oracle Linux/RHEL 6.5+, 7.0–7.3
1.0.1e
NPN

CentOS/Oracle Linux/RHEL 7.4+
1.0.2k
ALPN and NPN

Debian 7.0
1.0.1e
NPN

Debian 8.0
1.0.1k
NPN

Debian 9.0
1.1.0f
ALPN and 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 and NPN

Supporting HTTP/2 for Google Chrome Users | NGINX

https://www.nginx.com/blog/supporting-http2-google-chrome-users/

試しに nginx -V で確認してみると、利用しているのはOpenSSL1.0.1系です。

# nginx -V

nginx version: nginx/1.10.1
built by gcc 4.4.7 20120313 (Red Hat 4.4.7-16) (GCC)
built with OpenSSL 1.0.1e-fips 11 Feb 2013

OSにインストールされたOpenSSLをバージョンアップすると、OpenSSLを利用している他のプログラムに影響が出そうで困難そうです。

そのためnginxをソースからビルドし、--with-opensslでOpenSSL 1.0.2を静的リンクします

注 OpenSSLやnginxのアップデートがあった場合は再度コンパイルする必要があります。


The NGINX sources support OpenSSL 1.0.2 (and the forthcoming 1.1.0 release), so another option is to recompile NGINX from source and link against a private OpenSSL 1.0.2 build by including the --with-openssl argument.




Supporting HTTP/2 for Google Chrome Users | NGINX


nginxソースビルド

nginxのソース一式からビルドする手順です。

ビルドツール・依存関係ライブラリをインストールします。(root権限で行っています。)

# yum groupinstall  "Development Tools"

# yum install pcre pcre-devel zlib zlib-devel

openssl-1.0.2をダウンロード・展開します。

# curl -L -O https://www.openssl.org/source/openssl-1.0.2h.tar.gz

# tar xvfz openssl-1.0.2h.tar.gz

nginx-1.10.1をダウンロード・展開します。

# curl -L -O https://nginx.org/download/nginx-1.10.1.tar.gz

# tar xvfz nginx-1.10.1.tar.gz

./configureします。

末尾の 「 --with-openssl=../openssl-1.0.2h/ 」 でOpenSSL一式のフォルダを指定します。

# cd nginx-1.10.1

# ./configure \
--prefix=/etc/nginx \
--sbin-path=/usr/sbin/nginx \
--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-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-ipv6 \
--with-openssl=../openssl-1.0.2h/

make、インストールします。

# make 

# make install

nginx -V で確認すると、 OpenSSL 1.0.2系になっています。

# nginx -V

nginx version: nginx/1.10.1
built by gcc 4.4.7 20120313 (Red Hat 4.4.7-17) (GCC)
built with OpenSSL 1.0.2h 3 May 2016
TLS SNI support enabled

参考)

- Configureオプションはnginx.orgに記載されている内容を参考にしています。


nginx: Linux packages Configure Arguments



  • 起動スクリプトは以下にあります。(動作未確認)




Red Hat NGINX Init Script | NGINX


  • logrotate等は別途設定が必要です。


nginxのSRPMビルド

運用サーバーにセットアップするにはやはりrpmパッケージの方が便利です。nginxではSourceRPMも配布されているので、rpmパッケージを再作成可能です。

(最終的には virtualPC上のCentOSでrpmパッケージを作成し、サーバーにはrpmパッケージを適用しました。)

以下、nginxのrpmパッケージの作成手順です。

ビルドツール・依存関係ライブラリをインストールします。

※ GeoIP-develは epel から配布されるのでepel リポジトリを追加します。

# rpm -ivh http://ftp.riken.jp/Linux/fedora/epel/6/i386/epel-release-6-8.noarch.rpm

# yum install rpm-build  

# yum groupinstall "Development Tools"

# yum install zlib-devel pcre-devel perl-devel perl-ExtUtils-Embed GeoIP-devel libxslt-devel gd-devel 

rpmビルドはroot権限で行うと危険らしいので、builderというユーザーを作成します。

useradd builder  

builderユーザーに切り替えます。

su builder -

.rpmmacros (rpmbuildの設定ファイル)を作成します。

cd 

echo '%_topdir %(echo $HOME)/rpmbuild' > ~/.rpmmacros

nginxのSRPMを取得し展開します。

curl -L -O http://nginx.org/packages/centos/6/SRPMS/nginx-1.10.1-1.el6.ngx.src.rpm

rpm -ivh nginx-1.10.1-1.el6.ngx.src.rpm

※ nginx1.11系(mainline)を使う場合、http://nginx.org/packages/mainline/centos/6/SRPMS/ からSRPMSがダウンロードできます。

curl -L -O http://nginx.org/packages/mainline/centos/6/SRPMS/nginx-1.11.4-1.el6.ngx.src.rpm

rpm -ivh nginx-1.11.4-1.el6.ngx.src.rpm

※ nginxのソースを適用してバージョンを変える場合は、該当のソース一式をダウンロードしておきます。

例ではnginx-1.11.3のソースコード一式を使う場合

cd rpmbuild/SOURCES

curl -L -O https://nginx.org/download/nginx-1.11.3.tar.gz

nginx.specを編集します。(★の箇所を編集します。)


~/rpmbuild/SPECS/nginx.spec

%if 0%{?rhel}  == 6

%define _group System Environment/Daemons
%define with_http2 1
Requires(pre): shadow-utils
Requires: initscripts >= 8.36
Requires(post): chkconfig

opensslをコメントに
# Requires: openssl >= 1.0.1
# BuildRequires: openssl-devel >= 1.0.1

BuildRequires: perl-devel
BuildRequires: perl-ExtUtils-Embed
BuildRequires: GeoIP-devel
%endif

中略

%define COMMON_CONFIGURE_ARGS $(echo "\
--prefix=%{_sysconfdir}/nginx \
--sbin-path=%{_sbindir}/nginx \
--modules-path=%{_libdir}/nginx/modules \
--conf-path=%{_sysconfdir}/nginx/nginx.conf \
--error-log-path=%{_localstatedir}/log/nginx/error.log \
--http-log-path=%{_localstatedir}/log/nginx/access.log \
--pid-path=%{_localstatedir}/run/nginx.pid \
--lock-path=%{_localstatedir}/run/nginx.lock \
--http-client-body-temp-path=%{_localstatedir}/cache/nginx/client_temp \
--http-proxy-temp-path=%{_localstatedir}/cache/nginx/proxy_temp \
--http-fastcgi-temp-path=%{_localstatedir}/cache/nginx/fastcgi_temp \
--http-uwsgi-temp-path=%{_localstatedir}/cache/nginx/uwsgi_temp \
--http-scgi-temp-path=%{_localstatedir}/cache/nginx/scgi_temp \
--user=%{nginx_user} \
--group=%{nginx_group} \
--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-http_perl_module=dynamic \
--add-dynamic-module=njs-%{module_njs_shaid}/nginx \
--with-threads \
--with-stream \
--with-stream_ssl_module \
--with-http_slice_module \
--with-mail \
--with-mail_ssl_module \
--with-file-aio \
--with-ipv6 \
--with-openssl=/home/builder/openssl-1.0.2h/ \ ★追加
%{?with_http2:--with-http_v2_module}")

中略

# end of distribution specific definitions

main_version を変更します。(nginxのソースコードを変更してバージョンを変える場合のみ)
#%define main_version 1.10.1
%define main_version 1.11.3
%define main_release 1%{?dist}.ngx
%define module_xslt_version %{main_version}
%define module_xslt_release 1%{?dist}.ngx
%define module_geoip_version %{main_version}
%define module_geoip_release 1%{?dist}.ngx


ビルドします。(オプションについては注釈参照。 1 2

rpmbuild -bb rpmbuild/SPECS/nginx.spec -D 'debug_package %{nil}'

成功すると rpmファイルが作成されます。

rpmbuild/RPMS/x86_64/nginx-1.10.1-1.el6.ngx.x86_64.rpm

作成したrpmファイルをインストール先のサーバーでインストールします

rpm -ihv nginx-1.10.1-1.el6.ngx.x86_64.rpm

nginxの default.confを編集します。(SSL証明書の設定と、HTTP/2の設定)


/etc/nginx/conf.d/default.conf

server {

server_name XXXX.com;
listen 80;
listen 443 ssl http2;
ssl_certificate /etc/pki/tls/certs/XXXX.crt;
ssl_certificate_key /etc/pki/tls/private/XXXX.private.key;





nginxを起動します。

/etc/init.d/nginx start


HTTP/2の接続確認

SSLで接続して、HTTP/2が有効になっているか確認します。developper toolでprotocol列を表示すると’h2’となっています。

http2enable.png

またChrome拡張 HTTP/2 and SPDY indicator でも確認できます。

chrome_http.png


追記) Mac OS Xでの注意事項(Windowsでも同様のことがある可能性あり。)

使っているウイルス対策ソフトによってはHTTP/2が使えない場合がありました。

Mac OS XでHTTP/2が有効にならない


追記) iOS SafariでPOSTする際にエラーが発生する。

iOSのSafariでAjaxPostする際にnginx + HTTP/2だとブラウザで次のようなエラーが発生することがありました。

(nginx側のログには記録されません。)


javascript

Failed to load resource: request body stream exhausted

Failed to load resource: Could not connect to the server.

次のnginxのissueが関係しているようです。

MS IE/Edge, Safari, iOS applicationsが影響を受けるようです。



  1. Some clients (notably MS IE/Edge, Safari, iOS applications) show an error
    or even crash if a stream is rejected;


対策として考えられる方法です。


  • nginxのソースにパッチを当てる(未確認です

  • nginx1.11系(mainline)を使う (やってみました。)


nginx1.11系(mainline)を使う

現在のところ(2016.09.06) nginx-1.11.0にこの修正が入っているようです。

HTTP/2: implemented preread buffer for request body (closes #959). …

HTTP/2にはその後も修正が入っています。(segfaultも入っている)

nginx-1.11.3を試してみたところ、iOS SaferiからHTTP/2でPOST時にエラーは出なかったです。

nginx-1.11.3でrpmを作る方法は「nginxのSRPMビルド」に追記しています。


参考サイト・感想

単にnginxの設定でhttp2と記述するだけのつもりだったのでとても苦労しました。Apache2.4系でも同様のことがあるのかもしれません。

参考にさせていただいたサイト、素晴らしい情報をありがとうございます。


注釈





  1. -bbオプションは Build a binary package (after doing the %prep, %build, and %install stages).rpmbuild 



  2. -D 'debug_package %{nil}'は debugパッケージを作らない。未指定だと途中でdebugeditがエラーになった。extracting debug info from /home/builder/rpmbuild/BUILDROOT/nginx-1.10.1-1.el6.ngx.x86_64/usr/lib64/perl5/vendor_perl/auto/nginx/nginx.so /usr/lib/rpm/debugedit: canonicalization unexpectedly shrank by one character error: Bad exit status from /var/tmp/rpm-tmp.Ibuv0o (%install)