2026年5月20日

2026年5月20日

WordPressのSSL証明書を更新する方法

はじめに

SSL証明書が期限切れになると、訪問者のブラウザに「この接続は安全ではありません」という警告が表示され、サイトへのアクセスが激減します。Let's Encryptは90日間有効で自動更新が前提ですが、設定ミスで自動更新が止まることがあります。

症状・原因

  • ブラウザに「NET::ERR_CERT_DATE_INVALID」が表示される
  • SSL証明書の有効期限が切れている
  • Let's Encryptの自動更新が動いていなかった
  • 有料SSL証明書の更新期限を過ぎてしまった

解決手順

ステップ1:証明書の有効期限を確認する

# 現在の証明書の有効期限を確認
echo | openssl s_client -connect example.com:443 2>/dev/null | \
    openssl x509 -noout -dates
# notBefore= : 発行日
# notAfter=  : 有効期限

# Let's Encrypt の証明書一覧と期限を確認
sudo certbot certificates
# 出力例:
# Found the following certs:
#   Certificate Name: example.com
#     Domains: example.com www.example.com
#     Expiry Date: 2024-12-31 (VALID: 30 days)
#     Certificate Path: /etc/letsencrypt/live/example.com/fullchain.pem

# 残り日数を数値で確認
echo | openssl s_client -connect example.com:443 2>/dev/null | \
    openssl x509 -noout -enddate | \
    awk -F= '{print $2}' | \
    xargs -I{} bash -c 'echo "Expires: {}; Days left: $(( ( $(date -d "{}" +%s) - $(date +%s) ) / 86400 ))"'

ステップ2:Let’s Encrypt証明書を手動更新する

# ① 更新テスト(実際には更新しない)
sudo certbot renew --dry-run

# ② 全証明書を更新
sudo certbot renew

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

# ④ 強制更新(期限が30日以上残っていても更新)
sudo certbot renew --force-renewal --cert-name example.com

# ⑤ 更新後にWebサーバーを再起動
sudo systemctl reload apache2
# または
sudo systemctl reload nginx
# 更新が失敗する場合のデバッグ
sudo certbot renew --dry-run --debug

# ログを確認
sudo cat /var/log/letsencrypt/letsencrypt.log | tail -50

# よくある失敗原因:
# 1. ポート80が開いていない(ファイアウォール)
# 2. DNSが正しく設定されていない
# 3. ウェブルートのパスが間違っている
# 4. Apacheの設定エラー

# ポート80の疎通確認
curl -I http://example.com/.well-known/acme-challenge/test

ステップ3:自動更新の設定を確認・修正する

# systemd タイマーの状態を確認(Ubuntu/Debian)
sudo systemctl status certbot.timer
sudo systemctl list-timers | grep certbot

# タイマーが無効の場合は有効化
sudo systemctl enable certbot.timer
sudo systemctl start certbot.timer

# crontab を確認
sudo crontab -l | grep certbot

# crontab に自動更新を追加(タイマーがない場合)
sudo crontab -e
# 以下を追加(月2回、午前3時に実行):
# 0 3 1,15 * * /usr/bin/certbot renew --quiet --post-hook "systemctl reload apache2"

# 更新フックが正しく設定されているか確認
ls -la /etc/letsencrypt/renewal-hooks/deploy/
# Webサーバー再起動フックを設定
# Apache の場合
cat > /etc/letsencrypt/renewal-hooks/deploy/reload-apache.sh << 'EOF'
#!/bin/bash
systemctl reload apache2
EOF
chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-apache.sh

# Nginx の場合
cat > /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh << 'EOF'
#!/bin/bash
systemctl reload nginx
EOF
chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh

ステップ4:証明書期限切れアラートを設定する

# cron で期限切れを監視してメール通知
cat > /usr/local/bin/check-ssl-expiry.sh << 'EOF'
#!/bin/bash
DOMAIN="example.com"
ALERT_DAYS=14
ADMIN_EMAIL="admin@example.com"

EXPIRY=$(echo | openssl s_client -connect ${DOMAIN}:443 2>/dev/null | \
    openssl x509 -noout -enddate | cut -d= -f2)
EXPIRY_EPOCH=$(date -d "${EXPIRY}" +%s)
NOW_EPOCH=$(date +%s)
DAYS_LEFT=$(( (EXPIRY_EPOCH - NOW_EPOCH) / 86400 ))

if [ "${DAYS_LEFT}" -lt "${ALERT_DAYS}" ]; then
    echo "SSL certificate for ${DOMAIN} expires in ${DAYS_LEFT} days (${EXPIRY})" | \
        mail -s "[警告] SSL証明書の期限が${DAYS_LEFT}日後に切れます" "${ADMIN_EMAIL}"
fi
EOF
chmod +x /usr/local/bin/check-ssl-expiry.sh

# cron に追加(毎日確認)
echo "0 9 * * * root /usr/local/bin/check-ssl-expiry.sh" > /etc/cron.d/ssl-check
// functions.php: WordPress 管理画面でSSL期限を表示
add_action('admin_notices', function(): void {
    if (!current_user_can('manage_options')) return;

    $domain = parse_url(home_url(), PHP_URL_HOST);
    $cache_key = 'ssl_expiry_days_' . md5($domain);
    $days_left = get_transient($cache_key);

    if ($days_left === false) {
        $context = stream_context_create([
            'ssl' => ['capture_peer_cert' => true],
        ]);
        $conn = @stream_socket_client(
            "ssl://{$domain}:443", $errno, $errstr, 5,
            STREAM_CLIENT_CONNECT, $context
        );

        if ($conn) {
            $params = stream_context_get_params($conn);
            $cert = openssl_x509_parse(
                $params['options']['ssl']['peer_certificate']
            );
            $expiry = $cert['validTo_time_t'] ?? 0;
            $days_left = (int) (($expiry - time()) / 86400);
            set_transient($cache_key, $days_left, DAY_IN_SECONDS);
            fclose($conn);
        }
    }

    if ($days_left !== false && $days_left < 14) {
        echo '<div class="notice notice-error"><p>';
        printf(
            '<strong>SSL証明書の期限が %d 日後に切れます。</strong>早急に更新してください。',
            $days_left
        );
        echo '</p></div>';
    }
});

ステップ5:有料SSL証明書の更新

# 有料証明書の場合:CSRを生成して認証局に提出
# ① 秘密鍵を生成
openssl genrsa -out /etc/ssl/private/example.com.key 2048

# ② CSR(証明書署名要求)を生成
openssl req -new \
    -key /etc/ssl/private/example.com.key \
    -out /tmp/example.com.csr \
    -subj "/C=JP/ST=Tokyo/L=Tokyo/O=Company/CN=example.com"

# ③ CSR の内容を確認
openssl req -text -noout -in /tmp/example.com.csr

# ④ CSR をコピーして認証局のサイトに貼り付け
cat /tmp/example.com.csr

# ⑤ 認証局から受け取った証明書を設置
# fullchain.pem と privkey.pem を適切な場所に配置し
# Apache/Nginx の設定を更新してリロード
sudo systemctl reload apache2

注意事項

  • Let's Encrypt の証明書は有効期限が30日を切るまでcertbot renewは更新しません。--force-renewalは不要な更新を発生させ、レートリミット(週5回)に引っかかる可能性があるため、通常は使用しないでください
  • 証明書を更新してもWebサーバー(Apache/Nginx)をリロードしないと、古い証明書が引き続き使用されます。更新後は必ずリロードしてください
  • ワイルドカード証明書(*.example.com)はHTTP認証ではなくDNS認証が必要です。certbot certonly --manual --preferred-challenges dnsで手動更新が必要です

まとめ

SSL証明書更新は①certbot certificatesで残り日数を確認、②certbot renew --dry-runでテスト後にcertbot renewで更新、③systemctl status certbot.timerで自動更新タイマーを確認・有効化、④/etc/letsencrypt/renewal-hooks/deploy/にWebサーバーリロードフックを設置、⑤管理画面への期限アラートと日次cronによる監視で事前に気づける仕組みを構築します。

お気軽にご相談ください

お見積りへ お問い合わせへ