午前2時。スマホが鳴る。監視ツールからアラート:“SSL certificate expired — site returning ERR_CERT_DATE_INVALID”。クライアントから電話が来て、ブラウザが真っ赤な警告を表示しており、売上が1分ごとに失われていると告げられた。
この状況を初めて経験したのは2年前、Ubuntu 20.04の本番サーバーでのことだった。当時はLet’s Encrypt証明書をaptで手動インストールし、auto-renewの設定を忘れていた。そして有効期限が切れても誰も気づかなかった。あの日以来、二度とそんなことが起きないよう誓った。
実際の問題 — なぜサイトに「保護されていない通信」と表示されるのか?
以下の3つの状況が、私がこれまでデバッグしてきたケースの約95%を占める:
- SSLを最初から設定していない:サーバーを新規セットアップしてHTTPのみで動作させると、Chromeのアドレスバーに「保護されていない通信」と表示され、Google Search Consoleでも警告が出る。
- 証明書の期限切れ:Let’s Encryptの有効期間は90日。auto-renewが動作しない場合(ファイアウォールがポート80をブロック、DNS変更、cronjobの停止など)、証明書は事前通知なく自動的に期限切れになる。
- インストール済みだがWebサーバーがロードしない:Certbotは正常に実行されたが、Nginx/ApacheのconfigがリロードされておらずサイトがHTTPのままになっている。
それぞれの状況には異なる原因と修正方法がある — 以下で一つずつ解説する。
なぜLet’s Encryptはエラーが起きやすいのか?
Let’s Encryptは誰でも証明書を発行するわけではない。ACMEメカニズムはこのように動作する:「私はexample.comを所有しています」と申告すると、Let’s Encryptのサーバーは「では証明してください」と要求する — Webサーバーに特別なファイルを配置するか、DNSにTXTレコードを追加する方法で。検証が完了して初めて90日間の証明書が発行される。
すべてが正常に動作している場合は完全自動化される。しかし、チェーンの中の何かがブロックされると、チャレンジが失敗して証明書を取得できなくなる。
よくある原因:
- UFWまたはクラウド(AWS/GCP/DigitalOcean)のセキュリティグループによるポート80のブロック
- DNS AレコードがサーバーのIPを正しく指定していない — サーバー移行時によく発生し、DNSの伝播には15〜30分かかる
- aptからインストールした古いバージョンのCertbotにはバグが多く、snapバージョンでは修正済み
- CertbotがStandaloneモードを使用している間、WebサーバーがPort 80を占有している
Certbotの3つのインストール方法 — どれを選ぶべきか?
ステップ0:作業前の確認事項
まず何もインストールしない。先にこれらの基本事項を確認する:
# DNSがサーバーを指しているか確認
dig +short yourdomain.com
# ポート80が開いているか確認
sudo ufw status
curl -I http://yourdomain.com
# 古いcertbotがインストールされているか確認
certbot --version
digが正しいサーバーのIPを返し、curlで接続できれば — 準備完了、開始しよう。
方法1:Snap経由でCertbotをインストール(推奨)
Snapは2021年からLet’s Encryptが公式に推奨する方法。理由はシンプル:常に最新バージョンが維持され、自動更新され、システムとのコンフリクトが発生しない。
始める前に注意事項:Let’s Encryptはドメインごとに週5件の重複証明書制限がある。繰り返しテストして失敗が続く場合は、本番のクォータを消費しないよう--stagingフラグを追加すること。
# aptでインストールした古いcertbotを削除(コンフリクト回避)
sudo apt remove certbot
# snap経由でcertbotをインストール
sudo snap install --classic certbot
# certbotコマンドを直接使用するためのシンボリックリンクを作成
sudo ln -s /snap/bin/certbot /usr/bin/certbot
証明書を取得する — certbotにはNginxとApache専用のプラグインがあり、configを自動的に修正する:
# Nginxを使用している場合
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
# Apacheを使用している場合
sudo certbot --apache -d yourdomain.com -d www.yourdomain.com
# Webサーバーを設定していない場合(standaloneモード)
sudo certbot certonly --standalone -d yourdomain.com
実行中、certbotは有効期限の警告を受け取るためのメールアドレスとToSへの同意を求める。完了すると、nginx configを自動修正する:SSLブロックの追加、cipher suitesの設定、HTTP → HTTPSリダイレクトの設定。
方法2:aptでインストール(Ubuntu 20.04以前)
snapを使いたくない場合は、aptでも可能:
sudo apt update
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d yourdomain.com
実際のデメリット:UbuntuのaptリポジトリのバージョンはSnapより数ヶ月遅れていることが多い。certbot 1.12(apt版)のバグに遭遇したが、snap版の2.xではとっくに修正済みだった。
方法3:DNSチャレンジによるワイルドカード証明書
複数のサブドメイン(api.example.com、app.example.com、cdn.example.comなど)があり、1つの証明書で全てをカバーしたい場合、ワイルドカードが解決策:
sudo certbot certonly \
--manual \
--preferred-challenges dns \
-d "*.yourdomain.com" \
-d yourdomain.com
DNSチャレンジでは手動でDNSにTXTレコードを追加する必要がある — DNS APIがなければ自動化できない。Cloudflare API keyで自動化できる場合のみこの方法を使い、そうでなければ各サブドメインを個別にインストールする方がシンプルだ。
本番環境対応のセットアップ — 2024年から使用している手順
あの午前2時の証明書期限切れ事件の後、本番環境に適用する前にUbuntu 22.04のステージング環境でこの手順を徹底的にテストした。必要な作業の全内容はこちらだ:
1. Nginxで証明書をインストールして取得する
# Nginxが起動しているか確認
sudo systemctl status nginx
# certbot snapをインストール
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
# 証明書を取得 — certbotがnginx configを自動修正
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com \
--email [email protected] \
--agree-tos \
--no-eff-email
--no-eff-emailフラグはEFFからのプロモーションメールを受け取らないようにするためのもの。
2. 自動更新の確認
certbot snapは自動更新のためのsystemd timerを自動でインストールする。インストール直後に確認する:
# timerがアクティブか確認
sudo systemctl status snap.certbot.renew.timer
# 更新プロセスをテスト(実際には更新しない)
sudo certbot renew --dry-run
--dry-runは実際の証明書を変更せずに更新プロセス全体をシミュレートする。ここでエラーが出れば、証明書の自動更新時にも同じエラーが発生する — 今のうちに検出できた方がよい。
3. インストール済み証明書の確認
# 全証明書と有効期限を表示
sudo certbot certificates
# 出力例:
# Found the following certs:
# Certificate Name: yourdomain.com
# Domains: yourdomain.com www.yourdomain.com
# Expiry Date: 2026-05-28 (VALID: 89 days)
# Certificate Path: /etc/letsencrypt/live/yourdomain.com/fullchain.pem
4. 更新後にNginxをリロードするdeployフックの設定
certbotが更新した後、新しい証明書を適用するためにWebサーバーをリロードする必要がある。certbot snapは通常これを自動処理するが、常に信頼できるわけではない。確実を期すために明示的なdeployフックを作成するのがベストだ:
# 現在のdeployフックを確認
ls /etc/letsencrypt/renewal-hooks/deploy/
# 更新成功後にnginxを自動リロードするフックを作成
sudo tee /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh << 'EOF'
#!/bin/bash
nginx -t && systemctl reload nginx
EOF
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
これ以降、certbotの更新が成功するたびにnginxが自動リロードされる。新しい証明書がインストールされたのにnginxが古い期限切れ証明書を引き続き提供するという状況はなくなる。
よくあるエラーの対処法
エラー:”Problem binding to port 80″
# NginxがPort 80を占有している — 一時停止し、証明書を取得してから再起動
sudo systemctl stop nginx
sudo certbot certonly --standalone -d yourdomain.com
sudo systemctl start nginx
エラー:”Connection timed out”
# UFWによるポート80のブロック
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw reload
クラウドを使用している場合、UFWはファイアウォールの一層に過ぎない — Security Group(AWS)またはFirewallルール(GCP/DigitalOcean)でもポートを開放する必要がある。
証明書が期限切れになった場合の緊急更新
# 即座に強制更新
sudo certbot renew --force-renewal
# 更新後にnginxをリロード
sudo nginx -t && sudo systemctl reload nginx
インストール後のSSLグレードの確認
# ターミナルからの簡易チェック
curl -vI https://yourdomain.com 2>&1 | grep -E "SSL|TLS|expire|issuer"
より詳細に確認したい場合はssllabs.com/ssltestのSSL Labsを使用する。A+からFまでのグレードを付け、設定の弱点(古いcipher suite、HSTSが無効など)を正確に指摘してくれる。certbotのデフォルト設定で正しく構成されたサイトは通常AまたはA+を取得できる。
最後に、Let’s Encryptからのメールを無視しないこと。certbot実行時にメールアドレスを登録していれば、有効期限の30日前、20日前、10日前に警告が届く。メールが届いたのにauto-renewが動作しない場合は、すぐにデバッグが必要なことを意味する。実際の有効期限まで待ってはいけない。その時、もう午前2時かもしれない。
