SSL 証明書の管理ガイド — 自動更新の落とし穴と対策
SSL 証明書の自動更新は、実は気づかないうちに止まっているケースが多く、cron の消失・API キーの失効・プロキシー設定の変更が主な原因です。このガイドでは状態確認から監視設定まで順番に確認できます。
SSL 証明書の取得と更新は、利用しているインフラによって管理方法が大きく異なります。AWS ACM、Vercel、Netlify、Fly.io などのマネージドプラットフォームでは、証明書の発行・更新・配置がすべて自動化されているため、このガイドで扱う管理作業のほとんどは不要です。一方、VPS やオンプレミスサーバーで Let’s Encrypt をはじめとする ACME 対応 CA を使って自分で証明書を管理している場合は、サーバー移行時に cron が引き継がれなかった、DNS プロバイダーの API キーが失効していた、ロードバランサーの設定変更で HTTP-01 チャレンジが通らなくなっていた、といった理由で更新が止まるケースが後を絶ちません。CA/Browser Forum は証明書の最大有効期間を段階的に短縮する方針を決定しており、2026年3月から200日、2027年3月から100日、2029年3月からは47日 になります。更新頻度が上がるほど、自動更新の信頼性がより求められます。
このガイドでは、SSL 証明書の管理で押さえておくべきポイントを Step 形式で確認できます。マネージド SSL を利用している場合は Step 1(状態確認)と Step 5(期限切れの検知)を中心に、自己管理の場合はすべての Step を確認してください。example.com の部分は実際のドメイン名に置き換えてください。
flowchart TD
A["証明書の有効期限を確認"] --> B{"残り30日以上?"}
B -- Yes --> C["自動更新の設定を確認"]
C --> D{"cron / systemd timer が動作している?"}
D -- Yes --> E["次回チェックまで待機"]
D -- No --> F["自動更新を設定する"]
B -- No --> G["即座に更新を実行"]
G --> H{"更新成功?"}
H -- Yes --> I["外部から到達確認"]
H -- No --> J["よくある失敗パターンを確認"]
J --> K["HTTP-01 チャレンジが\nブロックされていないか"]
J --> L["DNS-01 の API キーが\n有効か"]
J --> M["ファイアウォール設定"]
チェックリスト一括コピー用
以下をコピーして手元のメモや Issue に貼り付ければ、そのまま確認リストとして使えます。
## SSL 証明書管理チェックリスト
### 証明書の状態確認
- [ ] 証明書の有効期限が 30日以上残っている
- `openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null | openssl x509 -noout -dates`
- [ ] 証明書のコモンネーム(CN)または SAN が対象ドメインと一致している
- [ ] 中間証明書が正しく設定されている(チェーンが完全)
- [ ] ルート証明書まで信頼チェーンが繋がっている
- [ ] HTTPS で外部から正常に接続できる
### CAA レコード
- [ ] CAA レコードで利用中の認証局が許可されている
- `dig CAA example.com +short`
- [ ] ワイルドカード証明書を使う場合は issuewild タグも設定されている
### 自動更新の設定(自己管理の場合)
- [ ] ACME クライアント(certbot、acme.sh、lego 等)の cron / systemd timer が有効
- [ ] 直近の更新ログにエラーがない
- [ ] HTTP-01 または DNS-01 チャレンジが正常に通る
- [ ] DNS-01 を使う場合、DNS プロバイダーの API クレデンシャルが有効
- [ ] マネージド SSL(AWS ACM、Vercel 等)を使っている場合はこのセクションはスキップ
### 更新後の反映(自己管理の場合)
- [ ] Web サーバー(nginx、Apache 等)が更新後に reload されている
- [ ] ロードバランサーやリバースプロキシーにも新しい証明書が配置されている
- [ ] OCSP Stapling が有効で、更新後もレスポンスが取得できている(Let's Encrypt 以外の CA を使用している場合)
### 期限切れ対策
- [ ] 証明書の有効期限を監視する仕組みがある
- [ ] 有効期限が 30日を切ったらアラートが出る
### 一括確認(API / コマンド)
- `openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null | openssl x509 -noout -dates -subject -issuer`
- `curl "https://labee.dev/api/ssl-cert?hostname=example.com"`
- `dig CAA example.com +short`
- `curl "https://labee.dev/api/dns?domain=example.com&type=CAA"`
Step 1 証明書の状態を確認する
SSL 証明書の管理で最初に行うのは、現在の証明書がどういう状態かを把握することです。有効期限、ドメイン名の一致、証明書チェーンの完全性を確認します。マネージド SSL を利用している場合でも、外部からの確認は有効です。Let’s Encrypt の証明書は有効期間が 90日 で、残り30日を切ると自動更新が試行されます。CA やサービスによって有効期間は異なるため、利用中の環境に合わせて確認してください。
チェック項目
- 証明書の有効期限が 30日以上残っている
- 証明書のコモンネーム(CN)または SAN(Subject Alternative Name)が対象ドメインと一致している
- 中間証明書が正しく設定されている(チェーンが完全で、中間 CA の漏れがない)
- ルート証明書まで信頼チェーンが繋がっている
- HTTPS で外部から正常に接続できる
確認コマンド
openssl で証明書の有効期限とドメイン名を確認します。
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null | openssl x509 -noout -dates -subject -issuer
notBefore=Mar 1 00:00:00 2026 GMT
notAfter=May 30 00:00:00 2026 GMT
subject=CN = example.com
issuer=C = US, O = Let's Encrypt, CN = R12
notAfter が現在日時から30日以上先であることを確認してください。issuer に中間 CA(Let’s Encrypt の場合は R12 など。2026年5月以降は YR1/YR2 への移行が予定されています)が表示されていれば、証明書は正しく発行されています。
証明書チェーンの完全性を確認するには、-showcerts オプションを使います。
openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null 2>/dev/null | grep -c "BEGIN CERTIFICATE"
2
サーバー証明書と中間証明書の計2枚が返ればチェーンは完全です。1枚しか返らない場合、中間証明書が設定されていません。一部のクライアント(古い Android 端末など)は中間証明書を自動取得できないため、HTTPS 接続に失敗します。
SAN(Subject Alternative Name)に含まれるドメイン一覧を確認するには、以下のコマンドを使います。
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null | openssl x509 -noout -ext subjectAltName
X509v3 Subject Alternative Name:
DNS:example.com, DNS:www.example.com
labee.dev の API で外部からの HTTPS 到達性も確認できます。
curl "https://labee.dev/api/ssl-cert?hostname=example.com"
{
"success": true,
"data": {
"hostname": "example.com",
"port": 443,
"reachable": true,
"status": 200
},
"error": null,
"meta": { "responseTime": 142 }
}
data.reachable が true であれば、外部から HTTPS で正常に到達できています。到達できない場合は reachable が false になり、error フィールドに理由が返ります。証明書の問題、ファイアウォール、DNS 設定などを確認してください。この API は到達性のチェックに特化しており、証明書の有効期限や詳細情報は openssl コマンドで確認します。
Step 2 CAA レコードを確認する
CAA(Certification Authority Authorization)レコードは、どの認証局がそのドメインの証明書を発行できるかを DNS で制御する仕組みです(RFC 8659)。CAA レコードが設定されていない場合、任意の認証局が証明書を発行できます。利用中の認証局(Let’s Encrypt であれば letsencrypt.org、AWS ACM であれば amazon.com や amazontrust.com など)を許可する CAA レコードを設定しておくと、意図しない認証局からの発行を防げます。
チェック項目
- CAA レコードで利用中の認証局が許可されている(
issueタグ) - ワイルドカード証明書を使う場合は
issuewildタグも設定されている -
iodefタグでポリシー違反の通知先を設定している(任意)
確認コマンド
dig CAA example.com +short
0 issue "letsencrypt.org"
0 issuewild "letsencrypt.org"
issue タグに利用中の認証局が含まれていることを確認してください。ワイルドカード証明書(*.example.com)を使っている場合は issuewild も必要です。issuewild が未設定だと issue の値が適用されますが、明示的に設定しておくのが確実です。
labee.dev の DNS API でも CAA レコードを取得できます。
curl "https://labee.dev/api/dns?domain=example.com&type=CAA"
{
"success": true,
"data": {
"domain": "example.com",
"records": {
"CAA": [
{ "flags": 0, "tag": "issue", "value": "letsencrypt.org" },
{ "flags": 0, "tag": "issuewild", "value": "letsencrypt.org" }
]
}
},
"error": null,
"meta": { "responseTime": 85 }
}
data.records.CAA 配列の tag と value を確認してください。CAA が null の場合はレコードが未設定です。
Step 3 自動更新の設定を確認する
マネージド SSL(AWS ACM、Vercel、Netlify など)を利用している場合、このステップの確認はほぼ不要です。プラットフォーム側が自動的に証明書を更新します。
自分で ACME クライアントを使って証明書を管理している場合は、以下の確認が必要です。Let’s Encrypt の証明書は有効期間が 90日 で、certbot は残り30日を切ると更新を試行します。自動更新が正しく動いていれば手動作業は不要ですが、サーバー移行、OS アップグレード、パッケージ再インストールなどのタイミングで cron や systemd timer が消えていることがあります。
チェック項目
- 利用中の ACME クライアント(certbot、acme.sh、lego 等)の定期実行が設定されている
- 直近の更新ログにエラーが記録されていない
- HTTP-01 チャレンジを使う場合、
.well-known/acme-challenge/に外部からアクセスできる - DNS-01 チャレンジを使う場合、DNS プロバイダーの API クレデンシャルが有効期限内である
- ワイルドカード証明書を使う場合は DNS-01 チャレンジが設定されている(HTTP-01 ではワイルドカード証明書を取得できない)
確認コマンド
certbot の自動更新設定と直近の実行ログを確認します。
# systemd timer の状態を確認
systemctl list-timers | grep certbot
Sun 2026-04-26 03:22:00 UTC 3 days left Thu 2026-04-23 03:22:00 UTC 12h ago certbot.timer certbot.service
タイマーが表示されない場合、自動更新が設定されていません。cron を使っている場合は以下で確認します。
# cron の設定を確認
crontab -l | grep certbot
0 3 * * * certbot renew --quiet --deploy-hook "systemctl reload nginx"
更新のドライランで、チャレンジが正常に通るかを事前に検証できます。
certbot renew --dry-run
Congratulations, all simulated renewals succeeded:
/etc/letsencrypt/live/example.com/fullchain.pem (success)
--dry-run が失敗する場合、実際の更新も失敗します。エラーメッセージを確認し、チャレンジの到達性や認証情報を見直してください。
HTTP-01 と DNS-01 の使い分け
HTTP-01 チャレンジは、認証局がドメインの http://{domain}/.well-known/acme-challenge/{token} に HTTP リクエストを送り、ACME クライアントが配置したトークンファイルを検証します。Web サーバーが直接インターネットに公開されている場合に適しています。ロードバランサーやリバースプロキシーの背後にある場合は、チャレンジリクエストがバックエンドに到達するようルーティングを設定する必要があります。
DNS-01 チャレンジは、_acme-challenge.{domain} に TXT レコードを作成して検証する方式です。ワイルドカード証明書の取得には DNS-01 が必須です。DNS プロバイダーの API を使って自動化できますが、API キーやトークンの有効期限管理が追加で必要になります。
Step 4 更新後の反映を確認する
マネージド SSL を利用している場合は、プラットフォーム側が証明書の配置も自動で処理するため、このステップの確認は不要です。
自己管理の場合、証明書が更新されても Web サーバーが新しい証明書を読み込まなければ意味がありません。nginx や Apache は設定ファイルの reload で新しい証明書を反映します。certbot の --deploy-hook を使えば、更新成功時に自動で reload を実行できます。Caddy や Traefik のように ACME を組み込みでサポートするサーバーでは、この手順は不要です。
チェック項目
- Web サーバー(nginx、Apache 等)が証明書更新後に reload されている
- certbot を使う場合は
--deploy-hookで reload コマンドが設定されている - ロードバランサーやリバースプロキシーを使っている場合、そちらにも新しい証明書が配置されている
- 複数サーバー構成の場合、全サーバーで証明書が更新されている
- OCSP Stapling が有効で、更新後もレスポンスが正常に取得できている(Let’s Encrypt 以外の CA を使用している場合)
確認コマンド
nginx の場合、--deploy-hook で reload を設定する例を示します。
certbot renew --deploy-hook "systemctl reload nginx"
更新後に外部から証明書の有効期限が延びていることを確認します。
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null | openssl x509 -noout -dates
notBefore=Apr 22 00:00:00 2026 GMT
notAfter=Jul 21 00:00:00 2026 GMT
証明書の失効確認の仕組みは近年大きく変わっています。Let’s Encrypt で発行した証明書を使っている場合、以下の OCSP に関する説明は該当しません。Let’s Encrypt は 2025年5月に新規証明書から OCSP URL を削除し、同年8月にレスポンダーを完全停止しました。
ブラウザー側も OCSP から移行しています。Chrome は従来から OCSP を使わず CRLSets で失効を確認しています。Firefox は CRLite を採用し、DV 証明書の OCSP チェックを無効化しつつあります。Safari は引き続き OCSP を使用しています。
Let’s Encrypt 以外の CA(DigiCert、Sectigo など)の証明書を使っている場合は、OCSP Stapling の設定を維持してください。有効かどうかは -status オプションで確認できます。
openssl s_client -connect example.com:443 -servername example.com -status </dev/null 2>/dev/null | grep -A 3 "OCSP Response"
OCSP Response Status: successful (0x0)
OCSP Response Data:
OCSP Response Status: successful (0x0)
Response Type: Basic OCSP Response
OCSP Response Status: successful が表示されれば、サーバーが OCSP レスポンスをステープリングしています。Let’s Encrypt の証明書を使っている場合は、この出力が得られないのが正常です。
labee.dev の API で外部からの到達性を再確認します。
curl "https://labee.dev/api/ssl-cert?hostname=example.com"
{
"success": true,
"data": {
"hostname": "example.com",
"port": 443,
"reachable": true,
"status": 200
},
"error": null,
"meta": { "responseTime": 98 }
}
data.reachable が true であれば、更新後も外部から正常に HTTPS 接続できています。
Step 5 期限切れを事前に検知する仕組みを作る
証明書の更新が成功したかどうかを毎回手動で確認するのは現実的ではありません。有効期限が近づいたときにアラートを出す仕組みを設けておくと、自動更新が止まっていた場合でも事前に気づけます。
チェック項目
- 証明書の有効期限を定期的にチェックするスクリプトまたはツールがある
- 有効期限が 30日 を切った時点でアラートが通知される
- CI/CD パイプラインに SSL チェックを組み込んでいる(任意)
- 複数ドメインを運用している場合、すべてのドメインが監視対象になっている
確認コマンド
シェルスクリプトで有効期限の残り日数をチェックする例です。
DOMAIN="example.com"
EXPIRY=$(openssl s_client -connect "$DOMAIN:443" -servername "$DOMAIN" </dev/null 2>/dev/null | openssl x509 -noout -enddate | cut -d= -f2)
EXPIRY_EPOCH=$(date -d "$EXPIRY" +%s 2>/dev/null || date -j -f "%b %d %T %Y %Z" "$EXPIRY" +%s)
NOW_EPOCH=$(date +%s)
DAYS_LEFT=$(( (EXPIRY_EPOCH - NOW_EPOCH) / 86400 ))
echo "残り ${DAYS_LEFT} 日"
残り 62 日
CI/CD で labee.dev の API を使って到達性チェックを行う例です。
REACHABLE=$(curl -fsS "https://labee.dev/api/ssl-cert?hostname=example.com" | jq -r '.data.reachable')
if [ "$REACHABLE" != "true" ]; then
echo "HTTPS 到達不可: example.com" >&2
exit 1
fi
openssl による有効期限チェックと labee.dev の到達性チェックを組み合わせると、証明書自体の期限と外部からのアクセス可否の両方を監視できます。
よくある設定ミス
nginx の reload が実行されていない
certbot が証明書ファイルを更新しても、nginx は起動時に読み込んだ証明書をメモリ上に保持し続けます。reload が実行されないまま古い証明書を返し続けるため、更新したつもりでも外部から見ると期限切れのままになります。--deploy-hook "systemctl reload nginx" を certbot の設定に追加してください。
HTTP-01 チャレンジがリバースプロキシーで遮断されている
ロードバランサーやリバースプロキシーの背後で ACME クライアントを実行している場合、認証局から /.well-known/acme-challenge/ へのリクエストが ACME クライアントに到達しない場合があります。nginx などの設定で該当パスをバックエンドに直接通すか、DNS-01 チャレンジに切り替えてください。マネージド SSL を利用している場合、この問題は発生しません。
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
DNS-01 チャレンジの API キーが失効している
DNS-01 チャレンジを DNS プロバイダーの API で自動化している場合、API キーやトークンに有効期限が設定されていることがあります。キーが失効すると TXT レコードの作成に失敗し、更新が止まります。プロバイダーの管理画面でキーの有効期限を定期的に確認してください。
中間証明書を設定していない
サーバー証明書だけを配置し、中間証明書(Let’s Encrypt の場合は chain.pem または fullchain.pem)を含めていないケースがあります。Chrome、Firefox、Edge などのデスクトップブラウザーは AIA(Authority Information Access)フェッチで中間証明書を自動取得できますが、一部のモバイル端末や API クライアント、curl のデフォルト設定では失敗します。nginx では ssl_certificate に fullchain.pem を指定してください。マネージド SSL や Caddy などの ACME 組み込みサーバーでは、中間証明書の配置も自動で処理されます。
CAA レコードで認証局を許可していない
CAA レコードに利用中の認証局が含まれていない場合、認証局は証明書の発行を拒否します(RFC 8659)。CAA レコードを設定した後に認証局を変更した場合は、CAA レコードも更新が必要です。マネージド SSL に切り替えた場合、プラットフォームが利用する CA(たとえば AWS ACM は amazon.com)を CAA に追加する必要があります。
サーバー移行後に ACME クライアントの設定が引き継がれていない
サーバーを移行する際、ACME クライアントの設定ディレクトリー(certbot の場合は /etc/letsencrypt/)を新サーバーにコピーし忘れるケースがあります。このディレクトリーには証明書ファイル、秘密鍵、クライアントの設定(更新方法、deploy-hook など)が含まれています。ディレクトリーを丸ごとコピーした上で、cron または systemd timer も新サーバーに設定してください。マネージド SSL やコンテナベースのデプロイ(Kubernetes + cert-manager など)では、この問題は発生しにくくなります。
発展的なトピック
SSL 証明書の基本的な管理が整ったら、セキュリティをさらに強化するための仕組みも検討に入ります。以下のトピックはこのガイドでは詳しく扱っていませんが、証明書の運用と密接に関係しています。
HSTS(HTTP Strict Transport Security)
ブラウザーに対して、指定した期間中は HTTPS 接続のみを使うよう指示するレスポンスヘッダーです。Strict-Transport-Security: max-age=31536000; includeSubDomains のように設定します。HSTS を有効にすると、HTTP から HTTPS へのリダイレクト前に中間者攻撃を受けるリスクを排除できます。ただし、証明書の更新に失敗した状態で HSTS が有効だとサイトにアクセスできなくなるため、自動更新の信頼性を確保してから導入してください。
Certificate Transparency(証明書の透明性)
認証局が発行したすべての証明書を公開ログに記録する仕組みです。不正に発行された証明書や意図しない認証局からの発行を検知できます。Chrome は CT ログに登録されていない証明書を信頼しません。Let’s Encrypt をはじめとする主要な CA は発行時に自動で CT ログに登録するため、通常は追加の設定は不要です。
用語解説証明書透明性(Certificate Transparency)CA が発行した証明書をパブリックログに記録し、不正発行を検知可能にする仕組み。Chrome は CT ログ未登録の証明書を拒否する。OCSP Stapling と証明書失効確認の変化
証明書の失効確認は大きく変化しています。従来の OCSP Stapling はサーバー側で OCSP レスポンスをキャッシュしてクライアントに提供する仕組みでしたが、Let’s Encrypt が 2025年8月に OCSP レスポンダーを完全停止したため、Let’s Encrypt 証明書では利用できなくなりました。Chrome は CRLSets、Firefox は CRLite による失効確認に移行しています。DigiCert や Sectigo など他の CA の証明書では引き続き OCSP Stapling が有効です。
用語解説OCSP ステープリング(OCSP Stapling)サーバーが事前取得した OCSP レスポンスを TLS ハンドシェイクに添付する仕組み。クライアントが CA に直接問い合わせる必要がなくなり、接続が高速化する。ワイルドカード証明書
*.example.com のように、サブドメイン全体をカバーする証明書です。DNS-01 チャレンジでのみ取得でき、HTTP-01 チャレンジでは発行されません。サブドメインを頻繁に追加・変更する環境で便利ですが、秘密鍵が漏洩した場合の影響範囲が広がるため、運用上のリスクも伴います。
証明書の有効期間短縮(CA/Browser Forum)
CA/Browser Forum は証明書の最大有効期間を段階的に短縮する決議(SC-081)を採択しました。2026年3月15日から200日、2027年3月15日から100日、2029年3月15日から47日 に短縮されます。有効期間が短くなるほど自動更新の頻度が上がり、手動管理は事実上不可能になります。ACME クライアントによる自動化と、更新失敗の監視体制が必須になります。
用語解説証明書の有効期限切れ(Certificate Expiry)SSL/TLS証明書のNotAfterを過ぎた状態。ブラウザーが警告を表示しHTTPS接続が拒否されます。