個人開発・サービス運用

SSL証明書が自動更新される仕組み:ACMEプロトコルの検証フローと個人開発での構成

SSL証明書の有効期間は、永遠ではない。Let's Encryptの証明書は標準で90日間。しかも、証明書ファイルを更新しただけでは、Webサーバーが新しい証明書を使い始めるとは限らない。…

SSL証明書が自動更新される仕組み:ACMEプロトコルの検証フローと個人開発での構成

個人開発のサービス運用では、ここに見落としが起きやすい。証明書の発行は成功している。更新処理も完了している。しかし、Nginxのプロセスが古い証明書を握ったままで、ブラウザーには期限切れの証明書が返り続ける。そんな事故は、アプリケーションのバグとは別の場所で起きる。

SSL証明書の自動更新を理解するには、単にCertbotのコマンドを覚えるだけでは足りない。ACMEプロトコルが何を検証し、HTTP-01とDNS-01がどこで分かれ、更新後にどのようにWebサーバーへ反映されるのか。運用の流れ全体を一つの仕組みとして捉える必要がある。

SSL証明書の自動更新を支えるACMEプロトコル

SSL証明書の自動更新は、ACMEという通信プロトコルを土台にしている。ACMEは、証明書の発行、更新、失効を自動化するための仕様で、RFC 8555で定義されている。

ここで大切なのは、ACMEが証明書を一方的に配る仕組みではないという点だ。

クライアントと認証局がHTTPSメッセージをやり取りし、申請者が対象ドメインを管理していることを確認する。その確認が完了して初めて、認証局が証明書を発行する。

Let's Encryptを利用する場合、CertbotなどのACMEクライアントが次のような処理を担当する。

1. 認証局へ証明書発行または更新を依頼する

2. 対象ドメインに対する認証方法を受け取る

3. HTTPまたはDNSに検証用の情報を配置する

4. 認証局によるドメイン所有権の確認を待つ

5. 検証に成功したら証明書を取得する

6. 更新成功後にWebサーバーへ新しい証明書を適用する

この流れの中で、ドメイン所有権を確認する部分がチャレンジだ。

チャレンジは「そのドメインを操作できるか」の確認

SSL証明書の申請者が、本当にドメインの管理者なのか。認証局はその確認をしなければならない。

たとえば、第三者が勝手に example.com の証明書を取得できてしまえば、HTTPSの信頼性は崩れる。そこで認証局は、申請者に対して検証用のトークンを提示する。

申請者は、そのトークンを指定された場所へ配置する。認証局がインターネット経由で確認し、正しい値を取得できれば、ドメインを操作できると判断する。

この検証方法には、代表的にHTTP-01とDNS-01がある。

項目HTTP-01DNS-01
検証場所Webサーバー上のファイルDNSのTXTレコード
主な配置先/.well-known/acme-challenge/_acme-challenge.<FQDN>
必須条件TCP 80番ポートでHTTPアクセスできることDNSを変更できること
ワイルドカード証明書利用できない利用できる
構成の分かりやすさ比較的分かりやすいDNS APIの設計が必要
向いている構成単一サーバー、通常のWebサイト複数サブドメイン、ロードバランサー、非公開サーバー

どちらを採用するかは、ドメインの構成と運用体制で決まる。個人開発だからHTTP-01、規模が大きいからDNS-01、と単純に分けるものではない。

自動更新の本体は、更新コマンドではない。ドメイン所有権の検証から、新しい証明書をプロセスへ反映するまでの一連の運用設計だ。

有効期間90日だからこそ、手動運用は相性が悪い

Let's Encryptの証明書は標準で90日間有効だ。90日ごとに手動で更新すればよい、と考えると、最初の数回は問題なく見える。

しかし、個人開発のサービスは、アプリケーション開発、問い合わせ対応、請求、アクセス解析、サーバー監視など、やることが多い。証明書の更新は緊急度が低く見える一方で、失敗したときの影響は大きい。結果として、対応が後回しになる。

さらに、証明書の有効期間は短くなる方向に進んでいる。調査時点の情報では、2026年3月15日から最長有効期間200日への移行開始目標、2027年3月15日から100日、2029年3月15日から47日という段階的な目標が示されている。

この流れを考えると、証明書更新を人間の予定表に置く運用は、今後さらに不利になる。更新頻度が上がれば、記憶と気合いに依存した仕組みは必ずどこかで破綻する。

必要なのは、更新を忘れないことではない。忘れてもサービスが止まらない構成にしておくことだ。

HTTP-01チャレンジの仕組みと制約

HTTP-01は、Webサーバーを使ってドメイン所有権を検証する方式だ。通常の個人開発サービスでは、最初に検討しやすい方法でもある。

認証局から発行されたトークンを含む検証用ファイルを、Webサーバー上の特定パスへ配置する。認証局は対象ドメインへHTTPアクセスし、そのファイルを取得できるか確認する。

配置先は次のパスだ。

/.well-known/acme-challenge/

たとえば、対象ドメインが example.com で、検証用ファイル名が abc123 なら、認証局は概念的に次のURLへアクセスする。

http://example.com/.well-known/acme-challenge/abc123

ここで正しい内容を取得できれば、HTTP-01の検証は成功する。

TCP 80番ポートが必須になる理由

HTTP-01には、TCP 80番ポートが必須だ。HTTPSの443番ポートだけを開けていればよい、という構成では成立しない。

これは、HTTP-01の検証がHTTPで行われるためだ。Webサーバー側でHTTPSへリダイレクトしていても、最初の接続先として80番ポートへ到達できなければならない。

この部分は、Dockerやクラウド環境で特に見落としやすい。

たとえば、次のような構成を考える。

  • ホスト側の443番だけを公開している
  • Dockerコンテナ内のNginxは80番で待ち受けている
  • クラウドのファイアウォールでは80番を閉じている
  • すべてのHTTPアクセスを別のリダイレクト先へ送っている

この場合、コンテナ内で80番を待ち受けていても、認証局から到達できるとは限らない。外部から見たTCP 80番ポートまでの経路が成立している必要がある。

確認すべきなのは、アプリケーションの設定だけではない。

  • DNSが正しいサーバーを向いているか
  • クラウド側のファイアウォールで80番が許可されているか
  • VPSやホストOSのファイアウォールで80番が許可されているか
  • ロードバランサーが80番の通信を受けているか
  • リバースプロキシが検証パスを別サービスへ誤配送していないか
  • Dockerのポート公開設定が外部向けに正しくなっているか

一つでも経路が切れていれば、HTTP-01は失敗する。

LaravelやWordPressのルーティングに吸い込ませない

HTTP-01の検証ファイルは、LaravelやWordPressのアプリケーションルーティングで処理する必要がない。むしろ、アプリケーションに到達する前のWebサーバーで静的ファイルとして返す方が安全だ。

Laravelでは、通常のアクセスが public/index.php へ集約される。WordPressでも、存在しないパスを index.php へ渡す構成が一般的だ。そのため、検証パスまでアプリケーションへ送ると、認証局が期待するファイルではなく、HTMLのエラーページやリダイレクトを受け取ることがある。

Nginxであれば、検証用ディレクトリを別のルートへ割り当てる構成が分かりやすい。Certbotが生成したファイルをWebサーバーから直接返せるようにする。

設計上のポイントは、アプリケーションが停止していても検証パスだけは返せることだ。

データベースの障害、Laravelのデプロイ失敗、WordPressプラグインの不具合。こうしたアプリケーション層の問題が起きても、証明書更新の検証まで巻き込まれる必要はない。インフラの検証処理と、プロダクト本体の処理を分離する。

HTTP-01でワイルドカード証明書は発行できない

HTTP-01の明確な制約として、ワイルドカード証明書を発行・更新できない点がある。

*.example.com のようなワイルドカード証明書を利用したい場合、DNS-01が必要になる。

個人開発では、最初は次のような構成から始まることが多い。

  • example.com
  • www.example.com

この程度であれば、HTTP-01でも対応しやすい。一方で、サービスごとにサブドメインを分ける構成にすると、必要な証明書の考え方が変わる。

  • app.example.com
  • api.example.com
  • admin.example.com
  • staging.example.com
  • user-tenant.example.com

サブドメインが増えるたびに個別の証明書を管理するのか、ワイルドカード証明書を使うのか。ここは技術の選択というより、運用の境界を決める判断になる。

DNS-01チャレンジで所有権を検証する

DNS-01は、WebサーバーではなくDNSを使ってドメイン所有権を確認する。

認証局が指定した値を、対象ドメインのDNSにTXTレコードとして登録する。レコード名は次の形式だ。

_acme-challenge.<FQDN>

たとえば example.com を検証する場合、_acme-challenge.example.com にTXTレコードを追加する。認証局がDNSを参照し、正しい値を取得できれば検証が完了する。

HTTP-01と違い、Webサーバーへ直接アクセスできる必要はない。Webサーバーがプライベートネットワーク内にある場合や、80番ポートを公開したくない場合でも、DNSを変更できるなら検証できる。

DNS-01が向いている構成

DNS-01の強みは、ワイルドカード証明書に対応できることだ。

一枚の証明書で、同一階層のサブドメインをまとめて保護したい場合に使いやすい。個人開発サービスでテナントごとのサブドメインを発行する場合や、API、管理画面、検証環境をサブドメインで分ける場合に、構成を整理しやすくなる。

また、次のような環境とも相性がよい。

  • Webサーバーを外部へ直接公開していない
  • 80番ポートを閉じたままにしたい
  • CDNやロードバランサーの背後にWebサーバーがある
  • 複数台のWebサーバーへ同じ証明書を配布する
  • ワイルドカード証明書を利用したい
  • DNSをコードやAPIで管理している

ただし、DNS-01はDNSの変更権限を扱う。ここが最大の注意点だ。

DNS APIの権限を広くしすぎない

DNS-01を自動化するには、DNSプロバイダーのAPIを使ってTXTレコードを作成・削除することが多い。Certbotのプラグインや、DNSプロバイダーが提供するクライアントを利用して、チャレンジの開始時にTXTレコードを追加し、検証完了後に削除する。

このとき、APIキーにドメイン全体の管理権限を与えると、証明書更新のための認証情報が強すぎる状態になる。

個人開発では、管理者が自分一人であることを理由に、すべての権限を一つのキーへまとめがちだ。私も開発速度を優先すると、権限の分離を後回しにしたくなる。しかし、秘密情報が漏えいした場合の影響は、証明書更新の失敗より大きい。

可能であれば、次のように範囲を絞る。

  • 対象ドメインだけを操作できるキーにする
  • TXTレコードの更新に必要な権限だけを与える
  • APIキーをソースコードへ直接書かない
  • Dockerの環境変数やシークレット管理を利用する
  • ログへAPIキーや認証情報を出力しない
  • 更新処理専用のユーザーやロールを分ける

DNSプロバイダーによっては、APIによる細かな権限制御が難しい場合もある。自動化できるかどうかだけで判断せず、漏えい時の被害範囲まで見て選ぶ。

DNSの反映時間を前提にする

DNS-01では、TXTレコードを作成した直後に認証局から見えるとは限らない。DNSのキャッシュやネームサーバーの構成によって、反映に時間差が出る。

そのため、DNSレコードの作成直後に一度だけ検証し、失敗したら即座に再実行する設計は不安定になりやすい。

特に、次のような構成では確認が複雑になる。

  • 複数の権威DNSを利用している
  • DNSプロバイダーを移行したばかり
  • 委任設定が複数階層に分かれている
  • サブドメインだけ別のDNSサービスへ委任している
  • 古いTXTレコードが残っている

DNS-01を採用する場合、検証用TXTレコードがどこへ作られるのかを、ドメイン名の構造から把握しておく必要がある。

DNSの設定画面でレコードを作成できたことと、認証局が正しい値を取得できることは別だ。ここも、管理画面の成功表示だけで判断しない。

証明書の更新とWebサーバーへの適用は別の処理

SSL証明書の自動更新で、最も誤解されやすい部分だ。

Certbotが新しい証明書を取得し、ディスク上の証明書ファイルを更新したとしても、NginxやApacheが自動的に新しいファイルを読み直すとは限らない。

Webサーバーのプロセスは、起動時に証明書を読み込んでメモリ上へ保持している。ファイルだけが差し替わっても、実行中のプロセスが古い証明書を使い続けることがある。

つまり、処理は少なくとも二段階に分けて考える必要がある。

1. 認証局から新しい証明書を取得する

2. Webサーバーへ新しい証明書を読み込ませる

この二つをつなぐのが、Certbotのフック機能だ。

deploy-hookを使って更新成功時だけ反映する

Certbotには、証明書の更新処理に合わせてコマンドを実行するフック機能がある。中でもdeploy-hookは、証明書の更新が成功した場合に処理を実行するための機能だ。

更新が発生していないのに、毎回Webサーバーをリロードする必要はない。更新成功時だけNginxをリロードする構成にすれば、無駄な処理と不要な影響を抑えられる。

ここでのポイントは、証明書の更新成功と、Webサーバーへの反映成功を分けて記録することだ。

  • 証明書の取得に成功した
  • 証明書ファイルが期待する場所へ保存された
  • Nginxの設定テストに成功した
  • Nginxのリロードに成功した
  • 外部から新しい証明書を確認できた

最初の二つだけを見て、すべて完了したと判断してはいけない。

Nginxのリロード前に設定テストを入れる

Nginxへ証明書を適用するとき、いきなり再起動するのは避けたい。設定ファイルに問題があった場合、サービス全体が停止する可能性があるからだ。

まず設定テストを実行し、問題がなければリロードする。リロードであれば既存の接続への影響を抑えながら、新しい設定と証明書を読み込ませやすい。

運用の考え方としては、次の順番になる。

1. 証明書更新の成功を確認する

2. Nginxの設定ファイルを検証する

3. 検証が成功した場合だけNginxをリロードする

4. リロードの結果をログへ残す

5. 必要に応じて外部から証明書の有効期限を確認する

Docker環境なら、Nginxがホスト上にいるのか、コンテナ内にいるのかでコマンドが変わる。

ホスト上のCertbotがホスト上のNginxを操作する構成と、Certbot自体をコンテナで動かす構成では、証明書の保存場所やソケット、ボリュームの設計が異なる。技術的にはどちらも成立するが、責任の所在が曖昧な構成は保守しにくい。

Dockerで証明書を扱うときの境界線

Dockerを使う個人開発では、アプリケーション、Nginx、Certbotをすべてコンテナへ分ける構成がよく使われる。

この場合、次の三つを最初に決めておくと混乱しにくい。

  • 証明書ファイルをどのコンテナが取得するか
  • 証明書ファイルをどのボリュームへ保存するか
  • 更新後にどのコンテナへリロードを通知するか

証明書を取得するコンテナと、HTTPS通信を処理するNginxコンテナが別の場合、共有ボリュームが必要になる。Certbotが更新したファイルをNginx側から読めるようにしなければならない。

一方で、共有ボリュームを用意しただけでは反映されない。Nginxが新しいファイルを読み込むためのリロードも必要だ。

ここで、コンテナを再作成するのか、Nginxプロセスだけをリロードするのかも判断することになる。証明書更新のたびにアプリケーションコンテナまで再起動する設計は、影響範囲が広い。SSL証明書だけを反映したいなら、可能な限りWebサーバーのリロードに閉じ込める方がよい。

証明書ファイルを置き換えただけでHTTPSが切り替わる、という期待は危険だ。ファイルを更新する処理と、プロセスへ読み込ませる処理は別に設計する。

自動更新を「動いているつもり」にしないための確認

自動更新は、一度成功しただけでは完成しない。証明書の有効期間が残っている間は、更新処理が実際には証明書を更新していないこともある。

そのため、導入時には次の観点で確認する。

更新処理を実行できるか

Certbotには、実際の証明書を変更せずに更新可能性を確認するためのテスト運用がある。導入時や構成変更時には、本番証明書を何度も発行するのではなく、テスト用の認証局を利用する方法も検討する。

ここで確認するのは、コマンドが終了コード0を返すかだけではない。

  • HTTP-01の検証ファイルを外部から取得できるか
  • DNS-01のTXTレコードが正しく公開されるか
  • 証明書ファイルの保存先が正しいか
  • deploy-hookが実行されるか
  • Nginxのリロードが成功するか
  • ログに失敗理由が残るか

自動化の検証は、成功ルートだけでなく失敗ルートを見る作業だ。

アプリケーションのヘルスチェックと分ける

LaravelやWordPressの監視を導入している場合、HTTPSの有効期限確認をアプリケーション監視へ混ぜない方がよい。

アプリケーションが200を返していても、証明書の有効期限が残り少ない可能性はある。逆に、証明書は正しくてもLaravelのデータベース接続が失敗していることもある。

監視対象を分けると、障害の切り分けが速くなる。

  • HTTP応答の確認
  • HTTPS証明書の有効期限確認
  • 証明書の発行先ドメイン確認
  • TLS接続の確認
  • NginxやApacheのプロセス状態
  • Certbotの更新ログ
  • DNSのTXTレコード更新ログ

個人サービスでは、大規模な監視基盤を最初から構築する必要はない。ただし、証明書だけは期限切れになる前に通知できる状態にしておきたい。

更新成功の通知より、更新失敗の通知を優先する

運用通知は、成功通知を増やすより失敗通知を確実に届ける方が有効だ。

毎回の更新成功を通知すると、通知が多くなり、重要な異常が埋もれる。更新処理が失敗した場合、あるいは証明書の残り期間が一定以下になった場合に通知する方が、個人開発の運用には合いやすい。

通知先は、メールでもチャットでもよい。重要なのは、普段見ている場所へ届くことだ。

サービスを作っていると、ログは記録しているのに誰も見ていない、という状態になりがちだ。監視の導入で満足せず、実際に通知を受け取れるか、通知後に原因を追えるかまで確認する。

HTTP-01とDNS-01、どちらを選ぶか

二つの方式には、それぞれ明確な向き不向きがある。

HTTP-01は、Webサーバー上へ検証ファイルを配置する構成だ。単一のVPSでLaravelアプリケーションやWordPressを動かし、Nginxが外部からのHTTPアクセスを受けているなら、比較的自然に組み込める。

DNS-01は、DNSのTXTレコードを操作する構成だ。ワイルドカード証明書、複数サーバー、非公開のWebサーバーなど、HTTPアクセスだけでは扱いにくい構成で強い。

選択条件HTTP-01を選びやすいケースDNS-01を選びやすいケース
サーバー公開80番ポートを公開できるWebサーバーを直接公開したくない
ドメイン数通常のドメインや少数のサブドメイン多数のサブドメインをまとめて扱う
証明書個別ドメインの証明書ワイルドカード証明書
DNS権限DNS APIを用意したくないDNS APIを安全に利用できる
インフラ単一のVPSや単純なNginx構成CDN、ロードバランサー、複数台構成
運用リスクWebサーバーの経路管理が中心DNS APIキーの管理が中心

判断基準は、設定項目の少なさだけではない。将来の構成変更に耐えられるか、失敗したときに原因を追いやすいか、認証情報を安全に管理できるか。この三つを見て決める。

個人開発の初期構成ならHTTP-01から始める

単一のVPSで一つのWebサービスを運用しているなら、HTTP-01は現実的な選択肢だ。

80番ポートを公開し、Nginxで /.well-known/acme-challenge/ を正しく返す。Certbotの更新後にNginxをリロードする。これで、構成の理解に必要な要素が少ない。

初期段階では、仕組みが見えることに価値がある。自動化の部品を増やしすぎると、どこで失敗しているのか分からなくなる。

Laravelのサービスを一つ運用している段階で、DNS API、ワイルドカード証明書、複数の証明書配布処理まで導入すると、SSL証明書のために運用コストが膨らむことがある。ユーザーの痛みを解決するためのプロダクトなのに、裏側の自動化が新しい痛みになる。

複数サブドメインならDNS-01を早めに検討する

一方で、サブドメインが増えることが分かっているなら、初期段階からDNS-01を検討する価値がある。

後からHTTP-01からDNS-01へ移行すると、証明書の発行方式だけでなく、秘密情報の管理、更新ジョブ、障害対応手順も変わる。もちろん移行は可能だが、サービスが成長してからの切り替えは、ユーザー影響を避けながら進める必要がある。

たとえば、ユーザーごとにサブドメインを割り当てるサービスでは、個別証明書の管理を続けるより、ワイルドカード証明書の方が運用を単純化できる場合がある。

ただし、ワイルドカード証明書は強力な分、漏えい時の影響範囲も広い。証明書の取得方式だけでなく、証明書ファイルへのアクセス権や秘密鍵の保管場所も設計する必要がある。

証明書短命化に備えて、今から自動化の境界を決める

証明書の有効期間が短くなる流れは、個人開発者にとって単なるセキュリティニュースではない。運用設計そのものに関わる変化だ。

証明書が90日有効なら、更新失敗に気づくまでにある程度の余裕がある。しかし、最長有効期間が段階的に短くなれば、同じ失敗でもサービス停止までの時間は短くなる。

だからこそ、次の境界を明確にしておきたい。

  • ドメイン所有権の検証を誰が担当するか
  • 証明書をどこへ保存するか
  • 秘密鍵へ誰がアクセスできるか
  • 更新処理をどのスケジューラーで実行するか
  • 更新後にどのプロセスへ反映するか
  • 失敗時に誰が通知を受けるか
  • 復旧時にどのログを見るか

この整理は、Certbotの設定ファイルを増やす作業ではない。サービス運用の責任範囲を定義する作業だ。

定期実行はあるが、定期実行だけでは足りない

自動更新では、Cronやシステムタイマー、コンテナのスケジュール実行などを使うことになる。

ただし、定期実行の設定があることと、更新が成功し続けることは別だ。

  • ジョブそのものが停止している
  • Dockerコンテナが終了している
  • DNS APIキーが期限切れになっている
  • 80番ポートがファイアウォールで閉じられた
  • DNSの委任先が変わった
  • Nginxの設定ファイルが変わった
  • 証明書の保存ボリュームがマウントされていない
  • deploy-hookの実行ユーザーに権限がない

こうした障害は、更新予定日になって初めて発生するとは限らない。インフラ変更の数週間後に、初めて自動更新で表面化することもある。

そのため、デプロイやDNS変更のチェック項目に、証明書更新経路を含める。アプリケーションのリリースだけを成功させても、次の更新処理が壊れていれば、運用は完成していない。

秘密鍵と証明書を同じ感覚で扱わない

証明書は公開情報に近い性質を持つが、秘密鍵は別物だ。証明書ファイルが見えること自体より、秘密鍵が漏えいすることの方が深刻な問題になる。

Dockerのボリュームやバックアップを設計するとき、証明書ディレクトリを丸ごと雑に扱わない。ログへ内容を出さない。不要なユーザーへ読み取り権限を与えない。

また、バックアップに含める場合も、保存先のアクセス権と暗号化を考える。個人開発では、サーバーのディレクトリをそのままバックアップサービスへ同期してしまうことがある。便利な反面、秘密鍵まで広い範囲へ複製される。

自動化は、作業を減らす仕組みだ。しかし、権限管理を省略する仕組みではない。

自動更新の構成をサービス運用へ組み込む

SSL証明書の自動更新を導入するとき、最初から完璧な構成を目指す必要はない。ただし、どこまでを自動化し、どこからを人間が確認するのかは決めておく。

たとえば、個人開発サービスの初期構成なら、次のような最小構成から始められる。

1. DNSで対象ドメインをWebサーバーへ向ける

2. TCP 80番と443番への通信経路を用意する

3. Nginxで検証パスを静的に返せるようにする

4. CertbotでHTTP-01の証明書を取得する

5. 更新処理を定期実行する

6. deploy-hookでNginxをリロードする

7. 更新失敗時のログと通知を確認する

8. 構成変更後に更新テストを実行する

サーバーを複数台に増やす、CDNを導入する、ワイルドカード証明書が必要になる。この段階でDNS-01へ移行しても遅くはない。

大切なのは、最初から複雑な仕組みを入れることではなく、現在の構成に対して失敗原因が見える状態を作ることだ。

ユーザーの痛みから逆算する

プロダクトグロースの観点で見ると、SSL証明書の自動更新はユーザーに直接見せる機能ではない。コンバージョンを増やす施策のように、分かりやすい成果が出るわけでもない。

しかし、HTTPSエラーが発生すれば、ユーザーはサービスへ到達できない。ログイン画面を見る前に離脱する。問い合わせフォームへ進む前にブラウザーの警告画面で止まる。

ここで発生する離脱は、アクセス解析のファネルだけでは捉えにくい。アプリケーションへ到達する前のインフラでユーザーを失うからだ。

だから、SSL証明書の自動更新は、保守作業ではなくユーザー体験の保護として考えたい。

  • サービスへ安定してアクセスできる
  • ブラウザーの警告を出さない
  • デプロイ後もHTTPS設定が壊れない
  • 障害時に復旧までの時間を短くする
  • 開発者が更新作業を忘れてもサービスが止まらない

華やかな機能ではない。しかし、サービスへの信頼を下支えする基盤だ。

HTTPSの証明書更新は、ユーザーから見えない場所で実行されるコンバージョン施策でもある。失敗した瞬間だけ、強烈にユーザーの前へ現れる。

仕組みを理解したうえで、自分の構成を選ぶ

ACMEプロトコルによるSSL証明書の自動更新は、認証局とクライアントがやり取りし、ドメイン所有権をチャレンジで検証する仕組みだ。

HTTP-01では、TCP 80番ポートを通じて /.well-known/acme-challenge/ の検証ファイルを取得できることが必要になる。分かりやすい一方で、ワイルドカード証明書には対応しない。

DNS-01では、_acme-challenge.<FQDN> のTXTレコードをDNSへ登録する。Webサーバーへ外部アクセスできない構成でも利用でき、ワイルドカード証明書にも対応できる。ただし、DNS APIの認証情報を安全に扱わなければならない。

そして、証明書の取得成功はゴールではない。新しい証明書をNginxやApacheへ読み込ませるため、deploy-hookなどを使ったリロードまで含めて設計する必要がある。

私が個人開発で重視したいのは、設定の多さではなく、失敗したときに理由が追えることだ。仕組みが複雑になるほど自動化の見た目は強くなるが、運用者が原因を理解できなければ、障害対応は遅くなる。

まずは、現在のドメイン構成とサーバー構成を書き出す。80番ポートを使えるのか、DNS APIを安全に使えるのか、ワイルドカード証明書が必要なのかを整理する。そのうえで、HTTP-01かDNS-01を選ぶ。

次に試したいことは、証明書更新の成功通知だけではなく、更新後の外部確認まで含めた監視だ。証明書ファイルが更新されたことではなく、ユーザーが実際に新しい証明書でHTTPS接続できることを検証する。

個人開発のサービスは、機能を作って終わりではない。ユーザーがいつ訪れても、安心して使える状態を維持して初めて、プロダクトとして成立する。SSL証明書の自動更新は、その地味で重要な運用を、記憶や根性から切り離すための最初の自動化になる。

Related reading: Let's EncryptのDNS-01チャレンジ:ワイルドカード証明書が自動更新される仕組み and Laravel SanctumとPassport:API認証における選定基準の最適解.

よくある質問

ACMEプロトコルとは何ですか?
ACMEは、SSL証明書の発行、更新、失効を自動化するための通信プロトコルです。クライアントと認証局がやり取りし、申請者が対象ドメインを管理していることを確認したうえで証明書を発行します。
HTTP-01の検証に必要な条件は何ですか?
HTTP-01では、認証局が対象ドメインのTCP 80番ポートへアクセスでき、`/.well-known/acme-challenge/` に置かれた検証ファイルを取得できる必要があります。DNS、ファイアウォール、ロードバランサー、リバースプロキシ、Dockerのポート公開設定も確認が必要です。
ワイルドカード証明書にはHTTP-01とDNS-01のどちらを使いますか?
ワイルドカード証明書を発行・更新するにはDNS-01が必要です。DNS-01では、`_acme-challenge.<FQDN>` に指定されたTXTレコードを登録してドメイン所有権を検証します。
証明書ファイルを更新したのに古い証明書が表示されるのはなぜですか?
NginxやApacheなどのWebサーバーは、起動時に証明書を読み込んでプロセスのメモリ上に保持することがあります。そのため、ファイルを差し替えただけでは新しい証明書が使われず、Webサーバーのリロードが必要になる場合があります。
Certbotのdeploy-hookは何に使いますか?
deploy-hookは、証明書の更新が成功した場合にコマンドを実行するための機能です。更新成功時だけNginxを設定テスト後にリロードする構成にすると、不要なリロードを避けながら新しい証明書を反映できます。
個人開発ではHTTP-01とDNS-01のどちらを選ぶべきですか?
単一のVPSで通常のWebサービスを運用し、TCP 80番ポートを公開できるなら、HTTP-01は比較的分かりやすい選択肢です。ワイルドカード証明書、複数サーバー、CDNやロードバランサー、非公開のWebサーバーが必要な構成ではDNS-01を検討しやすくなります。

参考情報