はじめに:HTTPSはもはや選択ではなく必須

2026年現在、HTTPSはWebサイトに不可欠な要素です。Google Chrome、Firefox、Safariなどすべての主要ブラウザはHTTPサイトに「保護されていない通信」という警告を表示し、GoogleはHTTPSサイトに検索順位の加点を与えています。さらに、HTTP/2、HTTP/3、Service Workerといった最新のWeb技術はHTTPSでのみ動作します。

かつてSSL証明書の発行には年間数万円の費用がかかりましたが、Let's Encryptの登場で状況は一変しました。2015年に設立されたLet's Encryptは非営利の認証局(CA)で、誰でも完全無料でSSL証明書を発行できるようにしました。2026年現在、世界で最も多く利用されている無料SSL証明書であり、毎日数百万件の証明書が発行されています。

本ガイドでは、Let's Encryptを活用して実際のサービスにHTTPSを導入する全工程を解説します。Certbotのインストールから、Nginx/Apacheへの適用、ワイルドカード証明書、自動更新、そしてよくある問題とその解決方法まで、実務ですぐに活用できるよう詳しく説明します。

1. Let's EncryptとSSL証明書の基礎

1.1 Let's Encryptの特徴

  • 完全無料:発行、更新、再発行すべて無料
  • 自動化:ACMEプロトコルにより発行・更新を自動化可能
  • 有効期間90日:商用証明書(1~2年)より短いが、自動更新で対応
  • ドメイン認証(DV)のみ対応:組織認証(OV)、拡張認証(EV)は非対応
  • ワイルドカード対応*.example.com形式の証明書発行が可能
  • 主要ブラウザがすべて信頼:Chrome、Firefox、Safari、Edgeすべてで自動的に信頼される

1.2 認証方式の理解

Let's Encryptはドメイン所有権を検証するチャレンジ(Challenge)方式で証明書を発行します:

チャレンジ種別 検証方式 用途
HTTP-01 Webサーバー上の特定パスにファイルを配置して検証 最も一般的、単一ドメイン
DNS-01 DNS TXTレコードによる検証 ワイルドカード証明書には必須、自動化可能
TLS-ALPN-01 TLSハンドシェイク中に検証 特殊な環境(ポート80遮断など)
どの方式を選ぶべき?
・単一ドメイン(example.com、www.example.com):HTTP-01(最もシンプル)
・ワイルドカード(*.example.com):DNS-01必須
・内部ネットワークサーバー、ポート80遮断環境:DNS-01

1.3 発行前の準備事項

  • ドメイン所有権:実際に管理可能なドメインが必要
  • DNS設定:ドメインがサーバーのIPに正しく紐付いていること
  • ポート80/443の開放:HTTP-01方式ではポート80が外部から到達可能である必要あり
  • root権限:証明書のインストールおよびWebサーバー設定の変更に必要

2. Certbotのインストール

CertbotはEFF(Electronic Frontier Foundation)が開発したLet's Encrypt公式クライアントで、証明書の発行と管理を自動化してくれます。

2.1 Ubuntu/Debianのインストール

# snap方式(公式推奨)
sudo snap install core
sudo snap refresh core
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot

# または apt方式(手軽)
sudo apt update
sudo apt install certbot -y
sudo apt install python3-certbot-nginx -y    # Nginxプラグイン
sudo apt install python3-certbot-apache -y   # Apacheプラグイン(必要に応じて)

# バージョン確認
certbot --version

2.2 RHEL/Rocky Linux/CentOSのインストール

# EPELリポジトリの有効化(RHEL/CentOS 7)
sudo yum install epel-release -y
sudo yum install certbot python3-certbot-nginx -y

# RHEL 9 / Rocky Linux 9
sudo dnf install epel-release -y
sudo dnf install certbot python3-certbot-nginx -y

# snap方式(全バージョン共通)
sudo dnf install snapd -y
sudo systemctl enable --now snapd.socket
sudo ln -s /var/lib/snapd/snap /snap
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot

3. NginxへのSSL証明書適用

3.1 自動設定(最も簡単な方法)

CertbotのNginxプラグインを使えば、証明書発行とNginx設定の変更を一度に処理できます:

# 前提条件:Nginxに該当ドメインのserverブロックが既に存在している必要あり
# 例:server_name example.com www.example.com;

# 証明書の発行 + Nginxの自動設定
sudo certbot --nginx -d example.com -d www.example.com

# 実行すると以下の情報を尋ねられます:
# 1. メールアドレスの入力(有効期限通知の受信用)
# 2. 利用規約への同意(Y)
# 3. EFFニュースレター受信(Nでも可)
# 4. HTTP → HTTPSリダイレクト設定(2番を推奨)

成功すると以下のようなメッセージが表示されます:

Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/example.com/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/example.com/privkey.pem
This certificate expires on 2026-07-05.

Deploying certificate
Successfully deployed certificate for example.com
Successfully deployed certificate for www.example.com
Your existing server block has been modified to redirect HTTP traffic to HTTPS.

3.2 手動設定(証明書のみ発行)

Nginx設定を自分で書きたい場合は、証明書のみを発行し設定は手動で記述することも可能です:

# webroot方式(Nginxが稼働している状態で)
sudo certbot certonly --webroot -w /var/www/example.com/html \
  -d example.com -d www.example.com

# standalone方式(Nginxを一時停止して発行)
sudo systemctl stop nginx
sudo certbot certonly --standalone -d example.com -d www.example.com
sudo systemctl start nginx
# 手動Nginx設定の例
# /etc/nginx/conf.d/example.com.conf

# HTTP → HTTPSリダイレクト
server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;
    return 301 https://$host$request_uri;
}

# HTTPSメイン設定
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name example.com www.example.com;

    # Let's Encrypt 証明書パス
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # SSL 最適設定
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;

    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 1d;
    ssl_session_tickets off;

    # OCSP Stapling
    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 8.8.8.8 8.8.4.4 valid=300s;

    # HSTS(HTTPS強制)
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

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

    location / {
        try_files $uri $uri/ =404;
    }
}
# 設定を検証して適用
sudo nginx -t
sudo systemctl reload nginx

4. ApacheへのSSL証明書適用

4.1 自動設定

# Apache SSLモジュールの有効化(Ubuntu/Debian)
sudo a2enmod ssl
sudo a2enmod headers
sudo systemctl reload apache2

# Certbot Apacheプラグインによる自動設定
sudo certbot --apache -d example.com -d www.example.com

4.2 Apacheの手動設定

# /etc/apache2/sites-available/example.com-ssl.conf
<IfModule mod_ssl.c>
<VirtualHost *:443>
    ServerName example.com
    ServerAlias www.example.com
    DocumentRoot /var/www/example.com/html

    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem

    # SSLプロトコル(TLS 1.2以上)
    SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
    SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384
    SSLHonorCipherOrder off
    SSLSessionTickets off

    # OCSP Stapling
    SSLUseStapling on
    SSLStaplingCache "shmcb:logs/ssl_stapling(32768)"

    # HSTS
    Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"

    ErrorLog ${APACHE_LOG_DIR}/example.com-ssl-error.log
    CustomLog ${APACHE_LOG_DIR}/example.com-ssl-access.log combined
</VirtualHost>
</IfModule>

# HTTP → HTTPSリダイレクト
<VirtualHost *:80>
    ServerName example.com
    ServerAlias www.example.com
    Redirect permanent / https://example.com/
</VirtualHost>
# サイトの有効化
sudo a2ensite example.com-ssl.conf

# 設定を検証して適用
sudo apachectl configtest
sudo systemctl reload apache2

5. ワイルドカード証明書の発行(DNS-01)

ワイルドカード証明書(*.example.com)は、1枚の証明書ですべてのサブドメイン(app.example.com、api.example.comなど)をカバーできるため非常に便利です。ただし、DNS-01チャレンジのみサポートされます。

5.1 手動DNS-01による発行

# 手動DNSチャレンジ
sudo certbot certonly --manual --preferred-challenges=dns \
  -d example.com -d "*.example.com"

# Certbotが以下のようなメッセージを出力:
# Please deploy a DNS TXT record under the name:
# _acme-challenge.example.com
# with the following value:
# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

# 上記TXTレコードをDNS管理パネルに手動で登録

# 登録後、伝播確認(Enterを押す前に)
dig -t txt _acme-challenge.example.com

# DNS伝播が確認できたらEnterを押して検証を進める
注意: DNS TXTレコードは伝播に時間がかかることがあります(最大24時間、通常は数分~数十分)。digコマンドで先に伝播状況を確認してからEnterを押してください。早すぎると検証に失敗します。

5.2 DNS APIによる自動化(Cloudflareの例)

Cloudflare、Route53、Google Cloud DNSなどを利用している場合、APIで自動化できます:

# Cloudflareプラグインのインストール
sudo snap install certbot-dns-cloudflare
# または
sudo apt install python3-certbot-dns-cloudflare -y

# Cloudflare APIトークンの保存(/root/.secrets/cloudflare.ini)
sudo mkdir -p /root/.secrets
sudo cat > /root/.secrets/cloudflare.ini << 'EOF'
dns_cloudflare_api_token = YOUR_CLOUDFLARE_API_TOKEN
EOF
sudo chmod 600 /root/.secrets/cloudflare.ini

# ワイルドカード証明書の自動発行
sudo certbot certonly \
  --dns-cloudflare \
  --dns-cloudflare-credentials /root/.secrets/cloudflare.ini \
  -d example.com -d "*.example.com"
# Route53プラグイン(AWS)
sudo snap install certbot-dns-route53

sudo certbot certonly \
  --dns-route53 \
  -d example.com -d "*.example.com"
# (IAM認証情報が環境変数または ~/.aws/credentials に設定されている必要あり)

6. 証明書の自動更新

Let's Encrypt証明書は90日ごとに更新が必要です。Certbotは自動更新を標準でサポートしています。

6.1 自動更新の確認

# 自動更新のテスト(実際の更新を伴わないシミュレーション)
sudo certbot renew --dry-run

# 強制更新(有効期限まで30日以上残っている場合でも)
sudo certbot renew --force-renewal

# 特定ドメインのみ更新
sudo certbot renew --cert-name example.com

# 自動更新タイマーの状態(systemd)
sudo systemctl status certbot.timer

# cron方式(snap版は自動設定)
systemctl list-timers | grep certbot

6.2 自動更新の仕組み

Certbotはインストール時に自動更新の仕組みを構成します:

  • systemdタイマー/etc/systemd/system/certbot.timer(snap/最新インストール)
  • cron/etc/cron.d/certbot(aptインストール)
  • 実行頻度:1日2回(12時間ごと)
  • 更新条件:有効期限まで30日以内の証明書のみ実際に更新
# 手動でcronに登録(必要な場合)
sudo crontab -e

# 毎日午前3時に自動更新チェック
0 3 * * * /usr/bin/certbot renew --quiet --post-hook "systemctl reload nginx"

# 更新後に特定のコマンドを実行(hook)
sudo certbot renew --post-hook "systemctl reload nginx"
sudo certbot renew --deploy-hook "systemctl reload nginx"

6.3 更新失敗通知の設定

# 通知メールアドレスの変更
sudo certbot update_account --email new@example.com

# 更新失敗時にスクリプトを実行
# /etc/letsencrypt/renewal-hooks/deploy/notify.sh
#!/bin/bash
echo "Certificate renewed: $RENEWED_DOMAINS" | mail -s "SSL Renewal" admin@example.com

sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/notify.sh

7. 証明書管理コマンド

7.1 よく使うCertbotコマンド

# 発行済み証明書の一覧確認
sudo certbot certificates

# 特定の証明書を削除
sudo certbot delete --cert-name example.com

# 証明書の再発行(別のドメインを追加)
sudo certbot certonly --expand -d example.com -d www.example.com -d blog.example.com

# ECDSA鍵の使用(RSAより高速で小さい)
sudo certbot certonly --nginx --key-type ecdsa --elliptic-curve secp384r1 \
  -d example.com -d www.example.com

# 特定のメールアドレスで登録
sudo certbot register --email admin@example.com --agree-tos

# 証明書の詳細情報を確認
openssl x509 -in /etc/letsencrypt/live/example.com/fullchain.pem -text -noout

# 有効期限の確認
echo | openssl s_client -connect example.com:443 2>/dev/null | \
  openssl x509 -noout -dates

7.2 証明書ファイルの構造

ファイル名 説明
cert.pem サーバー証明書のみ(単体)
chain.pem 中間CA証明書チェーン
fullchain.pem サーバー証明書+中間チェーン(Nginx/Apache用)
privkey.pem 秘密鍵(絶対に公開禁止!)
セキュリティ警告: privkey.pemは絶対にGitリポジトリ、共有フォルダ、外部サービスなどにアップロードしてはいけません。漏洩した場合は直ちに証明書を再発行し、既存の証明書を失効(revoke)する必要があります。

8. HTTPS設定の検証と最適化

8.1 オンラインSSLチェックツール

# testssl.shでローカル検査
git clone https://github.com/drwetter/testssl.sh.git
cd testssl.sh
./testssl.sh https://example.com

# 証明書チェーンの確認
curl -vI https://example.com 2>&1 | grep -i "SSL\|certificate"

# プロトコル/暗号化の確認
nmap --script ssl-enum-ciphers -p 443 example.com

8.2 A+等級のための追加設定

# Strong DH Parametersの生成(時間がかかる)
sudo openssl dhparam -out /etc/nginx/dhparam.pem 2048

# Nginx設定への追加
ssl_dhparam /etc/nginx/dhparam.pem;

# セキュリティヘッダー(SSL Labs A+要件)
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Frame-Options DENY always;
add_header X-Content-Type-Options nosniff always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;

8.3 HSTS Preloadへの登録

HSTS preloadに登録すると、サイトに初めてアクセスするユーザーに対してもブラウザが無条件でHTTPSで接続するよう強制できます:

# 登録条件(すべて満たす必要あり)
1. 有効な証明書を保持
2. HTTP → HTTPSリダイレクト
3. すべてのサブドメインでHTTPSを提供
4. HSTSヘッダーに max-age=63072000(2年)以上
5. HSTSヘッダーに includeSubDomains を含める
6. HSTSヘッダーに preload を含める

# 登録サイト:https://hstspreload.org/

9. よくある問題と解決方法

9.1 「Connection refused」エラー

Problem: Failed authorization procedure.
example.com (http-01): urn:ietf:params:acme:error:connection :: The server could not connect to the client to verify the domain :: Fetching http://example.com/.well-known/acme-challenge/xxx: Connection refused

原因:ポート80が外部からアクセス不可
解決

  • ファイアウォールでポート80を許可:sudo ufw allow 80/tcp
  • クラウドのSecurity GroupでHTTPを許可
  • ドメインが正しいIPを指しているか確認:dig example.com

9.2 Rate Limit超過

Problem: too many certificates already issued for exact set of domains

原因:Let's Encryptの発行制限に到達
Let's Encryptの主なRate Limit

  • 同じドメインセットに対して週5回の重複発行制限
  • 同じ登録済みドメインあたり週50個の証明書
  • 検証失敗の試行は1時間あたり5回

解決

  • テスト環境では--stagingオプションを使用(制限なし)
  • 時間を置いてから再試行(通常1週間)
  • ドメインを追加してセットを変更
# ステージング環境でテスト
sudo certbot --nginx --staging -d example.com

# ステージング証明書は実ブラウザでは信頼されない
# 設定完了後に本番発行:
sudo certbot --nginx --force-renewal -d example.com

9.3 DNS検証の失敗

Problem: DNS problem: NXDOMAIN looking up TXT for _acme-challenge.example.com

原因:DNS TXTレコードが伝播していない
解決

# TXTレコードの確認
dig -t txt _acme-challenge.example.com

# Google DNSで確認
dig -t txt _acme-challenge.example.com @8.8.8.8

# Cloudflare DNSで確認
dig -t txt _acme-challenge.example.com @1.1.1.1

# すべてで同じ値が出れば伝播完了

9.4 混在コンテンツ(Mixed Content)警告

HTTPSページからHTTPリソース(画像、スクリプトなど)を読み込むとブラウザが警告します。

解決

  • HTML内のhttp:////(プロトコル相対)またはhttps://に変更
  • CSPヘッダーによる自動アップグレード:upgrade-insecure-requests
# Nginxでの自動アップグレード
add_header Content-Security-Policy "upgrade-insecure-requests" always;

9.5 証明書更新の失敗

# 更新ログの確認
sudo journalctl -u certbot.timer
sudo cat /var/log/letsencrypt/letsencrypt.log

# よくある原因
# 1. Webサーバー設定エラー → nginx -t / apachectl configtest
# 2. ファイアウォール遮断 → ufw status / firewall-cmd --list-all
# 3. ディスク容量不足 → df -h
# 4. DNS変更 → dig example.com

# 手動での強制更新
sudo certbot renew --force-renewal --cert-name example.com

10. 実運用チェックリスト

10.1 証明書発行後の確認事項

  • ブラウザアクセス確認https://example.comが警告なしで開くか
  • 鍵マーク確認:アドレスバーに鍵マークが表示されるか
  • HTTPリダイレクト確認http://example.comが自動でHTTPSに遷移するか
  • SSL Labs等級の確認:最低A等級、可能ならA+
  • 自動更新のテストcertbot renew --dry-runが成功するか
  • 証明書有効期限の監視:期限30日前の通知設定
  • バックアップ/etc/letsencrypt/ディレクトリの定期バックアップ

10.2 証明書モニタリングスクリプト

#!/bin/bash
# /usr/local/bin/check_ssl_expiry.sh - 証明書有効期限モニタリング

DOMAINS=("example.com" "api.example.com" "blog.example.com")
WARNING_DAYS=30
ALERT_EMAIL="admin@example.com"

for domain in "${DOMAINS[@]}"; do
    expiry_date=$(echo | openssl s_client -servername "$domain" \
        -connect "$domain:443" 2>/dev/null | \
        openssl x509 -noout -enddate | cut -d= -f2)

    expiry_epoch=$(date -d "$expiry_date" +%s)
    now_epoch=$(date +%s)
    days_left=$(( (expiry_epoch - now_epoch) / 86400 ))

    echo "$domain: $days_left days remaining"

    if [ $days_left -lt $WARNING_DAYS ]; then
        echo "WARNING: $domain expires in $days_left days!" | \
            mail -s "SSL Expiry Alert: $domain" "$ALERT_EMAIL"
    fi
done
# 実行権限の付与とcron登録
sudo chmod +x /usr/local/bin/check_ssl_expiry.sh

# 毎朝9時に実行
sudo crontab -e
# 0 9 * * * /usr/local/bin/check_ssl_expiry.sh

まとめ:Let's Encryptですべてのウェブサイトに HTTPS を

Let's Encryptは無料SSL証明書という概念を超え、ウェブ全体のセキュリティレベルを引き上げたイノベーションです。かつてはコストの問題でHTTPS導入を諦めていた多くのサイトが、いまやすべて安全なHTTPSで提供されるようになり、これはユーザーとサービス提供者双方にとって有益な変化です。

本ガイドで扱った重要ポイントを改めて整理すると:

  • Certbotで自動化せよ - 手動の発行と更新はミスの余地が多くあります。Certbotの自動更新を必ず活用してください。
  • ワイルドカードはDNS-01で - 複数のサブドメインを運用するならワイルドカード証明書がはるかに便利です。
  • モニタリングを設定せよ - 自動更新を設定していても失敗する可能性があるため、有効期限の監視は必須です。
  • SSL Labs A+等級を目標に - 単にHTTPSを適用するだけでなく、最適化されたSSL設定まで併せて適用しましょう。
  • HSTSは慎重に - 一度有効化すると戻すのが難しいため、すべての設定が完璧な段階で適用してください。
  • 秘密鍵は徹底的に保護せよ - privkey.pemは絶対に外部に漏らしてはいけません。

Let's EncryptとCertbotを活用すれば、数分でプロダクションレベルのHTTPSを構築できます。本ガイドの例を皆さんの環境に合わせて適用し、安全なWebサービスの運用にお役立てください。