TL;DR: クイック修正
このエラーは、Webサーバーがサイトの証明書のみを送信し、中間証明書を省略しているために発生します。最新のデスクトップブラウザではこの問題が自動的に補完されることが多いですが、curl などのCLIツール、Node.js スクリプト、古い Android ブラウザでは即座にエラーとなります。
修正するには、ドメイン証明書と中間CA証明書を1つのファイルにまとめます:
# ドメイン証明書とCAから提供されたバンドルファイルを結合する
cat your_domain_cert.crt intermediate.crt > fullchain.pem
最後に、Webサーバーの設定を更新し、単体の証明書ではなく、この fullchain.pem ファイルを参照するように変更します。
チェーンが切れる理由
Let's Encrypt、Sectigo、DigiCert などの認証局(CA)は、通常、マスターである「ルート」証明書から直接署名することはありません。代わりに、中間証明書を使用して信頼のチェーン(Chain of Trust)を構築します。これにより、ルート証明書をオフラインで安全に保管できるため、セキュリティ層が強化されます。
「リーフ」(ドメインの証明書)のみをインストールすると、信頼チェーンが切断されます。デスクトップ版の Chrome や Firefox は、AIA Fetching という機能を使用して不足している中間証明書を自動的にダウンロードします。しかし、openssl、Python の requests、および多くのモバイルアプリはこの処理を行いません。サーバー側がルートまでの完全なパスを提供することを期待しています。
unable to verify the first certificate — SSL certificate problem: unable to get local issuer certificate
これが、MacBook では完璧に表示されるサイトが、Docker コンテナ内や3年前の Android スマートフォンではエラーになる理由です。
Nginx の正しい設定方法
Nginx では、証明書チェーン全体を1つの .pem または .crt ファイルに含める必要があります。ここで重要なのは記述の順番です。最初にドメイン証明書を記述し、その後に中間証明書を続ける必要があります。
- バンドルファイルを作成します:
cat example_com.crt intermediate.crt > /etc/nginx/ssl/fullchain.pem
/etc/nginx/sites-available/default内の server ブロックを更新します:
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/example_com.key;
# ... その他の設定
}
- 構文を確認して再起動します:
nginx -t && systemctl reload nginx
Apache の設定方法
Apache の設定はバージョンによって異なります。2013年にリリースされたバージョン 2.4.8 以降、Apache は Nginx と同様に証明書を処理します。
最新の Apache (2.4.8以降)
先ほど作成した fullchain.pem を使用します。これにより、設定が大幅に簡略化されます。
SSLCertificateFile /etc/apache2/ssl/fullchain.pem
SSLCertificateKeyFile /etc/apache2/ssl/example_com.key
旧バージョンの Apache (2.4.8未満)
SSLCertificateChainFile ディレクティブを使用して、中間ファイルを個別に指定する必要があります。
SSLCertificateFile /etc/apache2/ssl/example_com.crt
SSLCertificateKeyFile /etc/apache2/ssl/example_com.key
SSLCertificateChainFile /etc/apache2/ssl/intermediate.crt
Node.js クライアントエラーへの対処
Node.js アプリが SSL チェーンの切れた API にアクセスした際、NODE_TLS_REJECT_UNAUTHORIZED = '0' を設定したくなるかもしれません。しかし、これは避けてください。 セキュリティを無効化することは、中間者攻撃を招く危険なショートカットです。
より良い方法は、環境変数を使用して、Node.js に特定の中間証明書を教えることです:
# 特定の中間証明書を指定して実行する
export NODE_EXTRA_CA_CERTS="/path/to/intermediate.crt"
node app.js
これにより、セキュリティ層全体を損なうことなく、Node.js が特定のサーバーを検証できるようになります。
変更内容のテスト
修正の確認を個人のブラウザだけに頼らないでください。チェーンの深さを具体的にチェックするツールを使用しましょう。
1. OpenSSL ターミナルテスト
以下のコマンドを実行して、生のチェーンデータを確認します。出力結果で深さ(depth)が 1 または 2 になっていることを確認してください。
openssl s_client -connect example.com:443 -showcerts
結果に Verify return code: 21 が含まれている場合、チェーンはまだ不完全です。
2. Qualys SSL Labs
包括的な診断には、SSL Labs Server Test を使用してください。チェーンが切れている場合、**「Chain issues: Incomplete」**という明確な警告が表示されます。正しく設定されたサーバーは「A」評価を取得するはずです。
3. 更新後にこの問題が発生したのはなぜですか?
認証局(CA)は頻繁に中間証明書を更新します。設定を何年も変えていない場合でも、新しく発行された証明書には、以前使用していたものとは異なる intermediate.crt が必要な場合があります。更新時には、常に CA から提供される最新のバンドルファイルをダウンロードするようにしてください。

