この記事でできることと前提環境

この記事では、VPSにWebサーバーのNginxを入れ、独自ドメインで表示されるサイトをLet's Encryptの無料証明書でHTTPS化します。証明書の取得と更新にはCertbotという公式クライアントを使います。コマンドはUbuntu 24.04 LTSを前提にしています。Debian系なら、ほぼ同じ手順で進められます。

作業を始める前に、次の3つを用意してください。

  • ドメインのAレコード設定:ドメインのDNS設定で、Aレコード(ドメイン名をIPv4アドレスに結び付ける設定)にVPSのIPアドレスを登録しておきます。wwwありも使うなら、www側にも同じ設定が必要です。
  • 80番と443番ポートの開放:80番はHTTP、443番はHTTPSが使うポートです。証明書の取得と更新では、Let's Encryptが80番経由でドメインの所有を確認します。
  • sudo権限を持つユーザー:パッケージの導入や設定ファイルの編集に管理者権限が要ります。

本文では、ドメインを example.com と書きます。実際の作業では、自分のドメインに置き換えてください。

先に知っておきたい4つのつまずき

DNSがまだ反映されていない

DNSの設定変更は、すぐには世界中へ反映されません。数分で終わることもあれば、数時間かかることもあります。反映前に証明書を申請すると、Let's Encryptは古いIPアドレスか存在しない宛先を見に行き、認証に失敗します。手順2の確認コマンドで、VPSのIPアドレスが返ってくるまで待ってください。また、AAAAレコード(IPv6用の設定)に古いアドレスが残っていると、Let's EncryptはIPv6側を見に行って失敗することがあります。IPv6を使わないなら、AAAAレコードは消しておきます。

ファイアウォールで80番を閉じている

「HTTPSにするから443番だけ開けばよい」と考えて80番を閉じると、証明書の取得も更新もできません。ファイアウォールは2か所にある場合があります。1つはサーバー内のufw、もう1つはVPS事業者が管理画面で提供するパケットフィルタやセキュリティグループです。両方で80番と443番を許可してください。

認証失敗や発行回数には上限がある

Let's Encryptには発行回数の上限(レート制限)があります。執筆時点では、同じホスト名での認証失敗は1時間あたり5回までです。まったく同じドメインの組み合わせで証明書を発行できるのは、7日間で5回までです。設定を直さずに再実行を繰り返すと、しばらく申請できなくなります。上限の値は変わることがあるため、Let's Encrypt公式のレート制限ページで最新の値を確認してください。失敗が続くときは、本番の申請を止めてテスト実行(--dry-run)で原因を探ります。

自動更新の失敗に気づかない

Let's Encryptの証明書の有効期間は、執筆時点で90日です。Certbotは定期的に更新を試みますが、80番を閉じたり設定を書き換えたりすると更新が失敗することがあります。Let's Encryptは2025年に有効期限切れの通知メールを終了しました。そのため、期限切れはサイトが警告画面になって初めて気づくことになりがちです。手順8の確認方法を、導入時と設定変更のたびに実行してください。

手順1:Nginxをインストールする

パッケージ一覧を更新してから、Nginxを入れます。

sudo apt update
sudo apt install nginx
sudo systemctl status nginx

インストールすると、Nginxは自動で起動します。status の出力に active (running) と表示されれば、起動に成功しています。確認画面は q キーで閉じます。

手順2:ファイアウォールとDNSを確認する

ufwを使う場合は、SSHの許可を必ず先に追加します。SSHを許可しないままufwを有効にすると、ログイン中の接続が切れて入れなくなります。そうなった場合は、VPS事業者の管理画面にあるWebコンソールからログインし、sudo ufw allow OpenSSH を実行して戻します。

sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable
sudo ufw status

Nginx Full は、80番と443番をまとめて許可する設定です。事業者側のファイアウォールも管理画面で確認してください。

次に、DNSが反映されているかを確かめます。dig がない場合は sudo apt install dnsutils で入れます。

dig +short example.com A
dig +short www.example.com A

VPSのIPアドレスが表示されれば、次へ進めます。何も表示されないか、別のアドレスが表示された場合は、DNSの反映を待ちます。

手順3:サイトのファイルとNginxの設定を作る

公開するファイルを置くディレクトリを作り、確認用のページを1枚置きます。

sudo mkdir -p /var/www/example.com/html
echo '<h1>example.com works</h1>' | sudo tee /var/www/example.com/html/index.html

続けて、このドメイン用の設定ファイル /etc/nginx/sites-available/example.com を作ります。エディタには sudo nano などを使います。

server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;

    root /var/www/example.com/html;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }
}

server_name には、どのドメイン宛てのアクセスをこの設定で受けるかを書きます。Certbotはこの値を見て、証明書の設定を書き込む場所を決めます。ドメイン名の打ち間違いに注意してください。

手順4:設定を有効にして文法をチェックする

Ubuntu版のNginxは、sites-enabled にある設定だけを読み込みます。作った設定へのシンボリックリンク(ファイルへの参照)を置いて有効にします。

sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

nginx -t は、設定を実際に反映せずに文法だけを検査します。syntax is ok と test is successful が出たら、reload で反映します。この順番を守れば、動いているNginxを壊した設定で止めることはありません。ブラウザで http://example.com を開き、確認用のページが表示されることを確かめます。

手順5:設定ミスでNginxが起動しなくなったときの戻し方

設定ファイルを編集する前に、バックアップを取る習慣をつけてください。

sudo cp /etc/nginx/sites-available/example.com /etc/nginx/sites-available/example.com.bak

バックアップは sites-available に置きます。sites-enabled にコピーを置くと、同じ設定が二重に読み込まれてエラーになります。

Nginxが起動しなくなったら、まず原因の場所を調べます。

sudo nginx -t
sudo journalctl -u nginx --no-pager -n 30

nginx -t は、エラーのあるファイル名と行番号を表示します。その行を直せない場合は、バックアップを書き戻します。どの設定が原因か分からない場合は、該当するリンクを sites-enabled から一時的に外します。リンクを消しても、sites-available の元ファイルは残ります。

sudo cp /etc/nginx/sites-available/example.com.bak /etc/nginx/sites-available/example.com
# または一時的に無効化する
sudo rm /etc/nginx/sites-enabled/example.com

sudo nginx -t && sudo systemctl restart nginx

&& でつなぐと、文法チェックに通ったときだけ再起動します。

手順6:Certbotで証明書を取得する

CertbotとNginx用のプラグインを入れます。Certbot公式サイトはsnap版を推奨しています。ここではUbuntu標準のapt版を使います。どちらを使っても、以降のコマンドは同じです。

sudo apt install certbot python3-certbot-nginx

本番の申請の前に、テスト用の環境で試します。テスト用の環境で出た失敗は、本番の回数制限に数えられません。

sudo certbot certonly --nginx --dry-run -d example.com -d www.example.com

The dry run was successful と表示されたら、本番の証明書を取得します。

sudo cp /etc/nginx/sites-available/example.com /etc/nginx/sites-available/example.com.bak
sudo certbot --nginx -d example.com -d www.example.com

初回は、連絡用メールアドレスの入力と利用規約への同意を求められます。Certbotは次の順に処理します。まず80番で所有者の確認を受けます。次に証明書を /etc/letsencrypt/live/example.com/ に保存します。最後にNginxの設定へ443番の待ち受け、証明書のパス、HTTPからHTTPSへの転送を書き込み、Nginxを再読み込みします。Certbotは設定ファイルを直接書き換えます。そのため、直前にもう一度バックアップを取っています。

終わったら https://example.com を開き、鍵マーク付きで表示されることを確認します。http:// で開いたときにHTTPSへ転送されるかも見ておきます。

手順7:自動更新の仕組みを確認する

apt版のCertbotは、systemdのタイマー(定期実行の仕組み)で1日2回、更新が必要かを確認します。有効期限の残りが30日を切った証明書を更新します。タイマーが登録されているかは、次のコマンドで確認できます。

systemctl list-timers | grep certbot

certbot.timer が表示されれば登録されています。snap版の場合は snap.certbot.renew.timer という名前で表示されます。

手順8:自動更新をテスト実行で確かめる

更新処理が実際に通るかを、テスト実行で確かめます。本物の証明書は変わりません。

sudo certbot renew --dry-run

Congratulations, all simulated renewals succeeded と表示されれば、更新は成功する状態です。失敗した場合は、表示されたエラーを読みます。多い原因は、80番の閉鎖、DNSの変更、Nginx設定の文法エラーの3つです。サーバー上の証明書の期限と、外から見える証明書の期限は、次のコマンドで確認できます。

sudo certbot certificates
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -enddate

1つ目で表示される期限が新しいのに、2つ目の期限が古いままのことがあります。この場合、更新はできていてもNginxに反映されていないので、sudo systemctl reload nginx を実行します。

公開後に続けて確認すること

サーバーの設定を変えたら、そのたびに sudo nginx -t と sudo certbot renew --dry-run を実行してください。対象はファイアウォール、DNS、Nginxの設定です。この2つが通れば、サイトの表示と証明書の更新は続けられる状態です。人の確認に頼らず期限切れを防ぐなら、外部の監視サービスで証明書の期限を監視します。残り日数が14日を切ったら通知する設定にしておくと、更新の失敗に気づけます。複数のサイトを載せる場合は、手順3〜6をドメインごとに繰り返します。