エラーの内容
ブラウザで以下のエラーが表示されます:
NET::ERR_CERT_COMMON_NAME_INVALID
The server's security certificate does not match the website's URL.
サーバー上の証明書が、ブラウザがアクセスしようとしているドメインをカバーしていません。ブラウザは接続を拒否します。
根本原因
TLS証明書は、**コモンネーム(CN)とサブジェクト代替名(SAN)**拡張の2つのフィールドでカバーするドメインを宣言します。現代のブラウザはSANのみをチェックします。ChromeはChrome 58(2017年)でCNのみによるマッチングを廃止し、他の主要ブラウザもそれに倣いました。
よくある原因:
- 証明書が
example.comに対して発行されているが、アクセスしているのはwww.example.com(またはその逆) - 証明書が1つのドメインに対して発行されているが、別のドメインをホストするサーバーにデプロイされている
- 証明書にホスト名しか記載されていないのに、IPアドレスでサーバーにアクセスしている
- ワイルドカード証明書
*.example.com— サブドメインはカバーするが、ベアドメインexample.comはカバーしない - SAN拡張なしで生成された自己署名証明書
- 以前のドメインの古い証明書ファイルがサーバーに設定されたまま残っている
ステップ1:不一致を診断する
何かに触れる前に、デプロイされた証明書が実際にカバーしているドメインを正確に確認しましょう:
# 証明書のSANフィールドに記載されているドメイン名を確認する
openssl s_client -connect yourdomain.com:443 -servername yourdomain.com 2>/dev/null \
| openssl x509 -noout -text | grep -A1 "Subject Alternative Name"
# CNも確認する
openssl s_client -connect yourdomain.com:443 -servername yourdomain.com 2>/dev/null \
| openssl x509 -noout -subject
不一致の証明書は次のように見えます:
Subject: CN = old-domain.com
X509v3 Subject Alternative Name:
DNS:old-domain.com, DNS:www.old-domain.com
SANの下に自分のドメインが記載されていなければ、それが問題です。
修正1:正しいドメインで証明書を再発行する
長期的な正しい修正方法は、実際に自分のドメインをカバーする新しい証明書を取得することです。CertbotでLet's Encryptを使用してください:
# 単一ドメイン + www
certbot --nginx -d example.com -d www.example.com
# Apacheの場合
certbot --apache -d example.com -d www.example.com
# スタンドアロンモード(Webサーバー統合なし)
certbot certonly --standalone -d example.com -d www.example.com
Certbotは両方のドメインを自動的にSANフィールドに追加します。その後、Webサーバーをリロードしてください:
sudo systemctl reload nginx
# または
sudo systemctl reload apache2
修正2:正しい証明書なのにパスが間違っている — サーバーを正しいファイルに向ける
正しい証明書がすでにサーバーに存在しているのに、設定ファイルが読み込んでいるファイルが間違っている場合があります。パスを確認して修正しましょう。
Nginx:
server {
listen 443 ssl;
server_name example.com www.example.com;
# これらが example.com をカバーする証明書を指していることを確認する
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
}
Apache:
<VirtualHost *:443>
ServerName example.com
ServerAlias www.example.com
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
</VirtualHost>
リロードする前に設定をテストしてください — このステップは絶対に省略しないこと:
sudo nginx -t && sudo systemctl reload nginx
# または
sudo apachectl configtest && sudo systemctl reload apache2
修正3:SAN が欠如した自己署名証明書(開発/内部環境)
通常の openssl req コマンドはCNのみの証明書を生成し、SAN拡張が含まれません。ChromeはこれをそのまM拒否します。SANエントリを明示的に含む設定ファイルを渡す必要があります:
# SANを含むOpenSSL設定ファイルを作成する
cat > cert.cnf <<EOF
[req]
distinguished_name = req_distinguished_name
x509_extensions = v3_req
prompt = no
[req_distinguished_name]
CN = myapp.local
[v3_req]
subjectAltName = @alt_names
[alt_names]
DNS.1 = myapp.local
DNS.2 = localhost
IP.1 = 127.0.0.1
EOF
# SANを含む証明書を生成する
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout myapp.key -out myapp.crt -config cert.cnf
次に、ローカルで信頼してください。macOSの場合:
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain myapp.crt
Ubuntu/Debianの場合:
sudo cp myapp.crt /usr/local/share/ca-certificates/myapp.crt
sudo update-ca-certificates
修正4:ワイルドカード証明書がルートドメインをカバーしない
*.example.com は www.example.com、api.example.com などのサブドメインをカバーしますが、ベアドメイン example.com は含まれません。両方を明示的に指定して再発行してください:
certbot certonly --nginx -d example.com -d '*.example.com' \
--preferred-challenges dns-01
ワイルドカード証明書はHTTP-01ではなくDNS-01チャレンジが必要です。発行中、CertbotはDNSゾーンに _acme-challenge TXTレコードを追加するよう求めます。追加後、伝播のために30〜60秒待ってから続行してください。
修正を確認する
変更後、完了を宣言する前に証明書が正しいドメインを記載していることを確認してください:
# ライブサーバーのSANエントリを確認する
openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
| openssl x509 -noout -text | grep -A5 "Subject Alternative Name"
# 期待される出力:
# X509v3 Subject Alternative Name:
# DNS:example.com, DNS:www.example.com
# 有効期限の簡易確認
openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
| openssl x509 -noout -dates
curlでクロスチェックしましょう — 有効な証明書は通常のレスポンスヘッダーを返し、SSL警告は表示されません:
curl -vI https://example.com 2>&1 | grep -E "SSL|subject|issuer|expire"
出力に SSL certificate problem が表示されなければ、完了です。
予防策
- すべての証明書リクエストにベアドメインとwwwの両方を常に含めること。片方からもう片方にリダイレクトする場合でも同様です。
-d www.example.comを追加するのは5秒で済みますが、後でデバッグするとはるかに時間がかかります。 - **自動更新を設定する。**まず
certbot renew --dry-runでテストし、その後Certbotがインストールするsystemdタイマー(/etc/systemd/system/snap.certbot.renew.timer)を利用するか、独自のcronを追加してください。Let's Encryptの証明書は90日で期限切れになります — 手動で管理しないでください。 - デプロイ前に証明書をテストするには
openssl verify -CAfile chain.pem cert.pemを使用するか、SSL Labs(ssllabs.com/ssltest)でドメインを確認し、SAN対応を含む完全なレポートを取得してください。 - 内部サービスには、適切な内部CAを使用してください — step-ca や cfssl などのツールを使えば簡単に構築できます。アドホックな自己署名証明書は1台のマシンには十分ですが、チーム全体に広がると保守の悪夢になります。

