Let's Encrypt は九十日間の証明書を発行し、自動で更新します。だからこそ人々は設定して一週間もすればそれについて考えなくなります。厄介なのは、自動更新が静かに壊れることです。エラーは誰も見ておらず、三か月後にサイトはブラウザの警告で訪問者を迎えます。2025 年からは Let's Encrypt が期限切れの通知メールも送らなくなったので、外からの最後の合図も消えました。
以下は更新が動かなくなる原因を、頻度の高い順に並べたものです。
1. 証明書は更新されたが、サービスが読み直していない
最も多い形です。certbot renew は走り、新しいファイルはディスク上にあり、nginx や Apache は古いものをメモリに抱えたまま——そして設定が読み直されるまで、それを訪問者へ返し続けます。形式上は万事順調で、実際にはサイトの証明書は期限切れです。
治し方は、更新の成功後に走る hook です。
sudo certbot renew --deploy-hook "systemctl reload nginx"
これは更新ファイル(/etc/letsencrypt/renewal/example.com.conf の [renewalparams] 節、renew_hook の行)へ一度書き込まれ、以後は自分で働きます。
2. Web サーバーが変わり、challenge が変わっていない
HTTP-01 challenge は /.well-known/acme-challenge/ のファイルが素の HTTP で返されることを要求します。壊れ方はたいてい次のとおりです。
- 設定に HTTP から HTTPS への無条件のリダイレクトが加えられた——challenge はリダイレクトを追って失敗します;
deny allつきのlocation ~ /\.という規則が現れ、ドットで始まるディレクトリをすべて閉じた。.well-knownも含めて;- サイトのルートが移動したのに、certbot の設定には古い
--webroot-pathが残っている; - 国別のブロックや基本認証が有効になり、検証サーバーまで一緒に捕まえた。
設定での正しい順序は、まず /.well-known/acme-challenge/ 専用の location、そのあとに一般のリダイレクトです。
3. タイマーが切れている
更新は systemd のタイマーか cron のジョブから走ります。そしてそのどちらも、障害調査の途中で誤って切られたり、サーバー移設のときに失われたりします。
systemctl list-timers | grep certbot
sudo certbot renew --dry-run
二つ目のコマンドは、あなたの割り当てを消費せずにテストモードで更新の全工程を走らせます。「一か月後にこれは更新されるのか」に事後ではなく事前に答えてくれる唯一の確認です。
4. あるドメインがもうここを指していない
証明書が五つのドメインを覆っていて、そのうち一つが別のホストへ移り、DNS が変わった。そのドメインの検証が通らなくなり、certbot は証明書全体を更新しません——動いている四つのドメインもろともです。余分なドメインは、メッセージごと無視するのではなく証明書から外す必要があります。
5. 上限にぶつかった
Let's Encrypt は一つのドメインについて週あたりの証明書数を制限します。たいていそれは障害調査中の「うまくいかない、もう一度」という繰り返しの結果です。実験用には staging サーバーがあり(--dry-run か --test-cert)、それは割り当てを使い切る前に使うべきものであって、後からではありません。
保存されているものではなく、返されているものを確認する
ディスク上のファイルに基づく確認にはどれも一つ欠点があります。ファイルは新しくても、訪問者は古い証明書を受け取っているかもしれないということです。クライアント側から見てください。
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
| openssl x509 -noout -dates -subject -issuer
-servername オプションは必須です。これが無いと、複数のサイトがあるサーバーでは最初に来る仮想ホストの証明書が返り、あなたが意図したものではありません。出力の notAfter が本当の有効期限です。
ついでに証明書チェーンも確認しましょう。
echo | openssl s_client -connect example.com:443 -servername example.com -showcerts 2>/dev/null | grep -c 'BEGIN CERTIFICATE'
証明書が一つしか無ければ、中間証明書が返されていません。ブラウザはたいていその構成を許します——自分でチェーンを組み立てるからです——が、curl やモバイルアプリや Java のクライアントは検証に失敗します。これが典型的な「うちでは普通に開くのに、お客さんのところでは動かない」です。
何日前に警告するか
三十日が最初の警告として妥当です。certbot が更新を試み始めるときに残っているのもちょうどそれくらいで、つまりその時点で何も起きていなければ仕組みが壊れているということです。十四日が二度目の警告で、これはもう今日動くことを求めます。七日は非常事態です。
大事なのは、Let's Encrypt 由来でないものも含めてすべての証明書を同時に見ることです。一年分を購入したものは最も確実に忘れられます。一年もあれば、その存在も、通知が誰の受信箱に届くのかも、みんな忘れてしまうからです。ドメインと日付と残り日数を並べた一つのページが、この問題をすっかり片づけます——下のデモのように。