症状
nginxでHTTPS接続時に「この接続ではプライバシーが保護されません」「NET::ERR_CERT_AUTHORITY_INVALID」「SSL_ERROR_RX_RECORD_TOO_LONG」などのSSL証明書関連エラーが表示される。
結論:まずこれを確認
- 証明書の有効期限を確認する:
echo | openssl s_client -connect localhost:443 2>/dev/null | openssl x509 -noout -dates - nginxの設定で
ssl_certificateとssl_certificate_keyのパスが正しいか確認する - 証明書ファイルのパーミッションを確認する(nginxユーザーが読めるか)
トラブルシューティングフロー
flowchart TD
A[SSL証明書エラー発生] --> B{ブラウザのエラー種別は?}
B -->|期限切れ系| C[証明書の有効期限を確認]
B -->|信頼できない系| D[中間証明書を確認]
B -->|接続エラー系| E[nginx設定を確認]
C --> F{期限切れ?}
F -->|Yes| G[証明書を更新]
F -->|No| H[システム時刻を確認]
D --> I{中間証明書あり?}
I -->|No| J[中間証明書を結合]
I -->|Yes| K[証明書チェーンを確認]
E --> L{設定ファイルにエラー?}
L -->|Yes| M[パス・構文を修正]
L -->|No| N[パーミッションを確認]
よくある原因
- 証明書の有効期限切れ - Let’s Encryptは90日、更新忘れが多い
- 中間証明書の未設定 - サーバー証明書のみで中間証明書が結合されていない
- 証明書ファイルパスの誤り - nginx設定内のパスが実際のファイル位置と異なる
- 秘密鍵と証明書の不一致 - 異なる鍵ペアの証明書と秘密鍵を指定している
- パーミッション不足 - nginxのワーカープロセスが証明書ファイルを読めない
- ポート443でHTTP設定 - HTTPSサーバーブロックにSSL設定がない
- サーバー名の不一致 - 証明書のCN/SANとアクセス先ドメインが異なる
確認手順
ステップ1: 証明書の有効期限を確認する
# リモートから確認
echo | openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -dates
# ローカルファイルを直接確認
openssl x509 -in /etc/nginx/ssl/server.crt -noout -dates
🔍 チェックポイント: notAfter が現在日時より未来であれば有効期限内
ステップ2: 証明書と秘密鍵の整合性を確認する
# 証明書のモジュラス(ハッシュ)
openssl x509 -noout -modulus -in /etc/nginx/ssl/server.crt | openssl md5
# 秘密鍵のモジュラス(ハッシュ)
openssl rsa -noout -modulus -in /etc/nginx/ssl/server.key | openssl md5
🔍 チェックポイント: 両方の出力が一致すれば、証明書と秘密鍵はペアである
ステップ3: 中間証明書の設定を確認する
# 証明書チェーンを確認
openssl s_client -connect example.com:443 -showcerts </dev/null 2>/dev/null | grep -E "s:|i:"
🔍 チェックポイント: ルート認証局まで正しくチェーンがつながっている
ステップ4: nginx設定を確認する
# 設定ファイルの構文チェック
nginx -t
# SSL関連の設定を抽出
grep -r "ssl_certificate" /etc/nginx/
🔍 チェックポイント: nginx -t で syntax is ok と test is successful が表示される
ステップ5: 証明書ファイルのパーミッションを確認する
# 証明書ファイルの権限
ls -la /etc/nginx/ssl/
# nginxの実行ユーザーを確認
ps aux | grep nginx | grep -v grep
🔍 チェックポイント: 証明書は644以上、秘密鍵は600でnginxユーザーが読める
ステップ6: サーバー名と証明書の一致を確認する
# 証明書のCN(Common Name)とSAN(Subject Alternative Name)を確認
openssl x509 -in /etc/nginx/ssl/server.crt -noout -text | grep -A1 "Subject:"
openssl x509 -in /etc/nginx/ssl/server.crt -noout -text | grep -A1 "Subject Alternative Name"
🔍 チェックポイント: アクセスするドメイン名が証明書のCNまたはSANに含まれている
ステップ7: Let’s Encrypt自動更新の状態を確認する(該当する場合)
# certbotの証明書一覧
certbot certificates
# 更新テスト(実際には更新しない)
certbot renew --dry-run
🔍 チェックポイント: dry-run が成功し、証明書の残り日数が表示される
NG行動(やってはいけないこと)
- 秘密鍵のパーミッションを777にする - セキュリティリスク、秘密鍵は600が適切
- エラーを無視してnginxを強制再起動する - 設定エラーの原因が不明なまま状態が悪化する
- 本番環境で証明書ファイルを直接編集する - バックアップなしで作業すると復旧困難
- 自己署名証明書を本番で使用する - ブラウザ警告が出続け、ユーザーが離脱する
- 中間証明書なしでルート証明書を結合する - 一部ブラウザ・OSで検証に失敗する
よくある質問(FAQ)
Q1: Let’s Encryptの証明書が自動更新されない場合は?
A: certbot renew --dry-run でエラー内容を確認する。ポート80/443の疎通、webroot パスの設定、DNSレコードの確認が必要。cronジョブまたはsystemd timerの設定も確認する。
Q2: 中間証明書はどこで入手する?
A: 証明書発行元(CA)のWebサイトからダウンロードする。Let’s Encryptの場合は fullchain.pem を使用すると中間証明書が含まれている。
Q3: 複数ドメインを1つの証明書でカバーできる?
A: SAN(Subject Alternative Name)対応の証明書、またはワイルドカード証明書(*.example.com)を使用する。Let’s Encryptでは -d オプションで複数ドメインを指定可能。
関連するエラー・症状
解決しない場合
公式ドキュメント
確認すべきログファイル
# nginxエラーログ
/var/log/nginx/error.log
# Let's Encryptログ
/var/log/letsencrypt/letsencrypt.log
次に調べるキーワード
nginx ssl handshake failedcertificate verify failedSSL routines:ssl3_get_server_certificatecertbot renewal failed