Nginx+HTTP/2環境でのLet’s Encryptの証明書取得と更新

最近、WordPressサイト(このサイトです)の高速化の一環として、WebサーバのApacheからNginxへの変更と、HTTP/2の対応を行いました。
その環境でのLet’s Encryptでの証明書更新や、新規取得にちょっと詰まったので、やり方をまとめておきます。

HTTP/2とは?

まず、高速化の手法だ!ということで、何となくHTTP/2化したのですが、それが何なのか分かっていなかった・・・というのが、今回、困った原因でした。まず、HTTP/2とは何?という話から始めようと思います。

HTTP/2の説明は、さくらのナレッジのこちらの記事が参考になります。

この記事から分かるのは、HTTP/2は、HTTP/1.1までとは異なり、クライアント〜サーバ間がバイナリでのやり取りをすること、ストリームという概念が導入され、サーバは1つのリクエストに対して、そのレスポンスを返す前に別のリクエストを処理できるという2点ということです。

後者については、今までは複数のサーバプロセスを立ち上げて並列処理したいたところ、1つのプロセス内での並列が可能になったわけで、それだけサーバの処理性能が上がり(待ち時間の無駄がなくなり)、高速処理につながるというわけですね。

また、最近は肥大化の傾向にあるHTTPヘッダのデータがHTTP/2では圧縮して送信されるため、高速化するということもあるようです。

悩んでいたこと

Nginxに新たなドメイン(サブドメイン)での設定を追加し、Let’s Encryptでの新規証明書発行を行おうとしていたのですが、それが上手く行かない。
それなら・・・、ということで、Webブラウザでhttp://aaa.com(新規ドメイン)へのアクセスをしてみると、なんとバイナリファイルがダウンロードされるという謎現象でした。

この謎現象は、訳も分からずにNginxでの設定をこのような設定にしていたから。

server {
listen 80 http2;
server_name aaa.com;
(以下省略)
}

80番ポートでaaa.comを待ち受けていて、それがhttp2になっているのでバイナリでの処理を行おうとする。だから、ブラウザ(Chrome)はバイナリファイルのダウンロードになる。

でも、既にSSL証明書を取得して、httpへのアクセスは問答無用にhttpsにリダイレクトしていたサイトはなぜ大丈夫だったか?というと、https側のserver設定でhttp2を有効にしていてバイナリのやり取りになっても、ブラウザがそれに対応しているから。

つまり、HTTP/2の仕様としてバイナリでのやり取りをhttpでもhttpsでも行うというのは問題ないのですが、Chromeをはじめとするブラウザの対応としてはhttpsでのみHTTP/2にきちんと対応していて、httpでのHTTP/2対応はやる気が無いということらしいのです。

どう設定すれば良いのか?

ということで、httpでの通信はLet’s Encryptの証明書取得・更新時の通信と、httpsへのリダイレクトだけにして、それは従来どおりHTTP/1.1での通信にする。そして、実際に高速化を行いたいhttpsでの通信はHTTP/2にするというのが、良さそうです。

Nginxの設定ファイルはこのようになります。

server {
listen 80;
server_name aaa.com;

location ^~ /.well-known/acme-challenge
default_type "text/plain";
root /home/dggc43576856891/html;
}

return 301 https://$host$request_uri;
}

server {
listen 443 ssl http2;
server_name aaa.com;
(以下省略)
}

/.well-known/acme-challengeのくだりはLet’s Encryptでのドメイン認証時の通信に必要な設定です。ドメイン認証時は/home/dggc43576856891/htmlにファイルを自動作成させ、そのファイルに対して証明書を取得したいドメインでLet’s Encryptからアクセスできればドメイン認証完了になります。

SSL証明書の取得

この環境でSSL証明書を取得する場合、使用するコマンドは下記のとおりです。

letsencrypt certonly --webroot

対話型での操作になり、証明書を取得したドメインと、webrootパス(上記のNginxの設定では、/home/dggc43576856891/html)の2つを入力すればOKです。

証明書の取得後、Apacheでは自動的に諸々の設定が行われますが、Nginxでは自分で下記のような設定が必要です。(これはSSLを使用するための最低限の設定です。)

server {
listen 443 ssl http2;
ssl on;
server_name aaa.com;

ssl_certificate /etc/letsencrypt/live/aaa.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/aaa.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/aaa.com/fullchain.pem;
(以下省略)
}

Apacheが有効だったときに取得した証明書の更新

Nginxが有効(80番ポートを使っている)な場合に上記のように証明書を取得した場合、あとはほったらかしていても自動的に証明書は更新されます(Ubuntu 18.04 LTS)。
ただ、Apacheを使っていたときに取得した証明書の更新を、Nginxを使うようになった後に行う場合は、ドメインごとに下記の設定ファイル(/etc/letsencrypt/renewal/{domain}.conf)の変更が必要になります。

変更箇所のみを示します。

[renewalparams]
authenticator = webroot
[[webroot_map]]
aaa.com = /home/dggc43576856891/html

authenticatorとしてapacheが指定されているのをwebrootに変更することと、[[webroot_map]]のドメインに対応する値としてwebrootのパス(ここでは/home/dggc43576856891/htmlを指定することの2点です。

この記事を書いた人

井上 研一

株式会社ビビンコ代表取締役、ITエンジニア/経済産業省推進資格ITコーディネータ。AI・IoTに強いITコーディネータとして活動。画像認識モデルを活用したアプリや、生成AIを業務に組み込むためのサービス「Gen2Go」の開発などを行っている。近著に「使ってわかった AWSのAI」、「ワトソンで体感する人工知能」。日本全国でセミナー・研修講師としての登壇も多数。