当サイトを HTTP/2 対応

公開日

このサイトは2017年1月にさくらのVPS(vps.sakura.ad.jp)を契約して HTTPS 化しましたが、当時は CentOS 7 の yum からインストールできる OpenSSL のバージョンが 1.0.1 だったため、 HTTP/2 対応ができませんでした(ソースからのインストールはなるべくやりたくないので)。

先日、ようやく OpenSSL 1.0.2 がインストールできるようになったので、 Apache と PHP の環境を再整備のうえ、 HTTP/2 に対応しました。

前提条件(というか対応前の環境)は以下のとおり。

  • Apache 2.4、PHP 7.1 をインストール済
  • PHP はモジュール版で動作
  • TLS 接続対応済み(Let's Encrypt を使用)

これらの環境構築については、「さくらのVPS」に移転した時に書いたさくらVPSでサイトをSSL/TLS対応(CentOS 7 + Apache 2.4 + PHP 7.1)を参照ください。

今回行う手順は以下のとおり。

  1. Apache をアップデート
  2. PHP をモジュール版から FastCGI に変更
  3. Apache の動作モードを prefork から event に変更
  4. Apache の設定ファイルに HTTP/2 の記述を追加

Apache のアップデート

Apache 2.4 は IUS のリポジトリからインストールするようにしていたので、次のようなオプションを指定してアップデート。

yum --disablerepo=base,extras,updates --enablerepo=ius update httpd

これで2017年11月1日現在、 CentOS 7 では Apache 2.4.28 + OpenSSL 1.0.2k にアップデートされます。

HTTP/2 導入準備(Apache 設定変更)

Apache の ChangeLog を見ると、2017年7月にリリースされた 2.4.27 でこんな変更がされたようです。

COMPATIBILITY: mod_http2: Disable and give warning when using Prefork.

The server will continue to run, but HTTP/2 will no longer be negotiated.

Complete ChangeLog for 2.4(www.apache.org)

prefork モード(httpd.apache.org)では HTTP/2 は使えなくなったようなので、これを worker か event に変更する必要があります。

ところが、そうするとモジュール版で動作させている PHP が使えなくなってしまいますので、先に PHP を CGI 動作に変更します。

FastCGI 導入

PHP を CGI で動作させるにあたり、 FastCGI の仕組みを導入します。以前は mod_fcgid と mod_fastcgi の2種類がありましたが、現在後者は公式サイトが閉鎖されてしまったようで、mod_fcgid(httpd.apache.org)しか選択肢がありません。

yum -y install mod_fcgid

次に公式ドキュメントのコード例(httpd.apache.org)に倣い、PHPのラッパースクリプトファイル( /usr/local/bin/php-wrapper )を作成します。

#!/bin/sh

PHP_FCGI_MAX_REQUESTS=10000
export PHP_FCGI_MAX_REQUESTS

exec /usr/bin/php-cgi

このファイルに対し、 apache ユーザーに実効権限を与えます。

chown apache /usr/local/bin/php-wrapper

chmod 744 /usr/local/bin/php-wrapper

続いて、 Apache の設定ファイル(httpd.conf や vhost.conf など)に以下の記述を追加します。

FcgidMaxRequestLen 104857600
FcgidMaxRequestsPerProcess 10000

<Directory "/var/www/w0s.jp">
  AddHandler fcgid-script .php
  Options ExecCGI FollowSymLinks
  FcgidWrapper /usr/local/bin/php-wrapper .php
</Directory>

実際には他にも DocumentRoot などいろいろな設定を書いています。上記は今回追加した mod_fcgid 関連の記述のみを抜き出したものとなります。

FcgidMaxRequestLen は HTTP リクエストの最大長を指定するもので、デフォルト値は 131072(128KiB)(httpd.apache.org)なのですが、ファイルのアップロード機能があるとこれでは足りないことが多く、その場合は 500 Internal Server Error が発生してしまいます[1]。このサイトでは動画ファイルをアップロードすることもあるので、大きめに 104857600(100MiB)を指定しました。

FcgidMaxRequestsPerProcess は FastCGI アプリケーションが処理するリクエスト数の上限値です。ラッパースクリプトの中で指定した PHP_FCGI_MAX_REQUESTS と同じか、それよりも小さい自然数を指定します。

またモジュール版の設定ファイルは不要になるので、以下のファイルの拡張子を .conf.bak 等に変更して読み込まれないようにします。

  • /etc/httpd/conf.d/php.conf
  • /etc/httpd/conf.modules.d/15-php.conf

Apache の動作を prefork → event に変更

MPMに関する設定ファイル /etc/httpd/conf.modules.d/00-mpm.conf を編集します。

LoadModule mpm_prefork_module modules/mod_mpm_prefork.so
↓
#LoadModule mpm_prefork_module modules/mod_mpm_prefork.so
#LoadModule mpm_event_module modules/mod_mpm_event.so
↓
LoadModule mpm_event_module modules/mod_mpm_event.so

単純に読み込んでいるモジュールファイルを mod_mpm_prefork.so から mod_mpm_event.so に変更するだけですね。

いったん確認

ここまで行ったら Apache を再起動。

systemctl restart httpd.service

念のため、

apachectl -V | grep MPM

で Server MPM: event になっていることを確認(変更前は "prefork" )。

PHP に関しては phpinfo() して、 Server API が "CGI/FastCGI" になっていればOK(変更前は "Apache 2.0 Handler" )。

HTTP/2 設定

いよいよ HTTP/2 の設定を行います。といっても設定ファイルにProtocolsディレクティブ(httpd.apache.org)の記述を書き足すだけです。

Protocols h2 http/1.1

いくつかのブログ記事を拝見すると ProtocolsHonorOrder On の記述も併せて行っている方もいるようですが、これはデフォルト値が "on"(httpd.apache.org)なので、通常はわざわざ記述する必要はありません。

ブラウザで確認

Apache を再起動し、実際にブラウザでアクセスしてレスポンスヘッダーを見ると HTTP/2 通信が行われていることが確認できました。

オリジナル画像
図1:Chrome 62 の DevTools で通信状況を見たところ

また、 HTTP/2 に対応していないブラウザでは従来どおり HTTP/1.1 通信が行われます。

オリジナル画像
図2:IE 10 の F12 開発者ツールでレスポンスヘッダーを見たところ
  • [1]Apache のエラーログには「mod_fcgid: HTTP request length 〓〓〓 (so far) exceeds MaxRequestLen (131072)」が書き込まれます。