2026年7月2日
2026年7月2日
SSL証明書の中間CA(チェーン)エラーを直す方法:確認コマンドとNginx・Apacheの設定
HTTPS のサイトが、パソコンのブラウザでは開けるのに、スマートフォンのアプリ、curl、外部サービスの webhook、別サーバーの WordPress からの接続では「証明書を検証できない」と失敗する場合、多くはサーバーが中間 CA 証明書を送っていないことが原因です。この記事では、欠けているかどうかの確認、正しいチェーンの作り方、Nginx・Apache・Certbot での設定、反映後の確認を順に説明します。
中間 CA 証明書とチェーンの仕組み
サーバー証明書は、ルート CA から直接ではなく、中間 CA が発行するのが一般的です。接続してきたクライアントは、次のつながり(チェーン)をたどって証明書を信頼します。
- サーバー証明書(自分のドメインの証明書)
- 中間 CA 証明書(1 を発行した CA。複数段のこともある)
- ルート CA 証明書(OS やブラウザにあらかじめ入っている)
サーバーが送る必要があるのは 1 と 2 です。3 のルート証明書はクライアント側にあるものを使うため、送る必要はありません。中間証明書が欠けていると、クライアントは 1 と 3 をつなげられず、検証に失敗します。
パソコンのブラウザで問題が出にくいのは、ブラウザによっては欠けた中間証明書を自動で取りに行ったり、以前に見た中間証明書を使ったりするためです。curl・PHP・アプリなどはこの補完をしないことが多く、そこで初めてエラーが表に出ます。
手順1:中間証明書が欠けているか確認する
サーバーが実際に送っている証明書を openssl s_client で確認します。-servername を付けないと、1台のサーバーで複数のドメインを運用している場合に別の証明書が返ることがあります。
openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null
出力で見るのは次の2か所です。
Certificate chainの一覧:0 s:が自分のドメイン、1 s:以降に中間 CA が並んでいるか。0しか無ければ中間証明書が送られていません- 最後の
Verify return code:0 (ok)なら正常です。21 (unable to verify the first certificate)や20 (unable to get local issuer certificate)は、中間証明書が欠けているときの典型です
curl でも同じ問題を確認できます。
curl -sSI https://example.com/
# 中間証明書が欠けていると、次のようなエラーになる
# curl: (60) SSL certificate problem: unable to get local issuer certificate
WordPress の HTTP API(wp_remote_get() など)も内部で cURL を使うため、他サイトからの取得や連携で同じ cURL error 60 が出ることがあります。
手順2:手元の証明書ファイルの中身を確かめる
設定に使っているファイルに何が入っているかを確認します。1つのファイルに複数の証明書が入っている場合も、次のコマンドで全部の subject(誰の証明書か)と issuer(誰が発行したか)を表示できます。
# ファイル内の証明書の数
grep -c 'BEGIN CERTIFICATE' fullchain.pem
# すべての証明書の subject と issuer を順に表示する
openssl crl2pkcs7 -nocrl -certfile fullchain.pem | openssl pkcs7 -print_certs -noout
正しいファイルは次の条件を満たします。
- 1番目が自分のドメインの証明書
- 各証明書の issuer が、次の証明書の subject と一致している
- subject と issuer が同じ証明書(自己署名のルート証明書)は含まない
名前に「Root」が付いていても、subject と issuer が異なる証明書は、別のルートが署名した「クロス署名」の中間証明書です。2026年10月時点の Let's Encrypt では、サーバー証明書のあとに中間 CA とこうしたクロス署名の証明書が続くチェーンが使われており、これらは必要な中間証明書なので消さないでください。
手順3:中間証明書を入手する
中間証明書は、証明書を発行した CA(または販売事業者)が配布しています。発行時のメールや、ダウンロードした ZIP ファイル、管理画面の「中間証明書」欄に含まれていることが多いので、まずそれを使ってください。
見当たらない場合は、サーバー証明書の中に「発行元の証明書の取得先(CA Issuers)」が書かれていることがあります。
# サーバー証明書の AIA 拡張から CA Issuers の URL を確認する(OpenSSL 1.1.1 以降)
openssl x509 -in server.crt -noout -ext authorityInfoAccess
# 表示された CA Issuers の URL から取得する(URL は上の出力に置き換える)
curl -o intermediate.der http://ca.example.net/intermediate.crt
# 取得したファイルが DER 形式なら PEM 形式に変換する
openssl x509 -inform DER -in intermediate.der -out intermediate.pem
取得したファイルの subject が、サーバー証明書の issuer と一致することを確認してから使います。
手順4:正しい順番で fullchain を作り、検証する
サーバー証明書を先頭に、続けて中間証明書を並べます。ルート証明書は入れません。
# サーバー証明書 → 中間証明書 の順に連結する
cat server.crt intermediate.pem > fullchain.pem
# 前のファイルの末尾に改行が無いと、証明書の区切りが壊れる。次の行が無いか確認する
grep -n 'END CERTIFICATE-----.*BEGIN' fullchain.pem
配置する前に、チェーンがつながるかを検証します。OK と出れば、OS が持つルート証明書まで正しくたどれています。
openssl verify -untrusted intermediate.pem server.crt
# server.crt: OK
秘密鍵とサーバー証明書が対になっているかも確認しておきます。2つの出力が同じなら一致しています。
openssl x509 -in server.crt -noout -pubkey | openssl sha256
openssl pkey -in privkey.pem -pubout | openssl sha256
秘密鍵のファイルは所有者だけが読めるようにします(chmod 600 privkey.pem)。
手順5:Web サーバーに設定する
Nginx
ssl_certificate には、サーバー証明書と中間証明書をまとめた fullchain を指定します。
server {
listen 443 ssl;
server_name example.com;
# サーバー証明書 + 中間証明書(この順で1ファイル)
ssl_certificate /etc/ssl/example.com/fullchain.pem;
ssl_certificate_key /etc/ssl/example.com/privkey.pem;
}
ssl_trusted_certificate は、OCSP 応答やクライアント証明書の検証に使う証明書を指定する設定で、ここに書いた証明書はクライアントに送られません。中間証明書の欠落はこの設定では直らないので、ssl_certificate のファイルを直してください。
sudo nginx -t
sudo systemctl reload nginx
Apache(2.4.8 以降)
Apache 2.4.8 以降は、SSLCertificateFile に中間証明書を含めたファイルを指定できます。以前使われていた SSLCertificateChainFile は 2.4.8 で廃止扱いになっています。
<VirtualHost *:443>
ServerName example.com
SSLEngine on
SSLCertificateFile /etc/ssl/example.com/fullchain.pem
SSLCertificateKeyFile /etc/ssl/example.com/privkey.pem
</VirtualHost>
sudo apachectl configtest
# Ubuntu / Debian
sudo systemctl reload apache2
# RHEL / Rocky Linux / AlmaLinux
sudo systemctl reload httpd
Let’s Encrypt(Certbot)の場合
Certbot は /etc/letsencrypt/live/<ドメイン>/ に次のファイルを作ります。
| ファイル | 中身 |
|---|---|
cert.pem |
サーバー証明書だけ |
chain.pem |
中間証明書だけ |
fullchain.pem |
サーバー証明書 + 中間証明書 |
privkey.pem |
秘密鍵 |
Web サーバーの証明書には fullchain.pem を指定します。cert.pem を指定すると中間証明書が欠けます。Let's Encrypt の中間証明書は随時切り替わり、2026年10月時点の公式ページでは、以前の「Let's Encrypt Authority X3」はすでに期限切れとして掲載されています。古い記事にある X3 の URL から中間証明書を取ってきて連結するのではなく、Certbot が更新のたびに作り直す fullchain.pem をそのまま使ってください。
レンタルサーバー・CDN・ロードバランサーの場合
管理画面に「証明書」「秘密鍵」「中間証明書(CA 証明書)」の欄が分かれている場合は、中間証明書の欄に中間 CA の証明書を貼ります。ルート証明書は入れません。CDN やロードバランサーで HTTPS を終端している場合は、Web サーバーではなく、そのサービス側の証明書設定を直す必要があります。
手順6:反映後に確認する
設定を反映したら、手順1のコマンドをもう一度実行し、Certificate chain に中間 CA が並び、Verify return code: 0 (ok) になることを確認します。外部からの確認には、Qualys の SSL Server Test(SSL Labs)も使えます。結果の「Chain issues」が「Incomplete」なら中間証明書の不足、「Contains anchor」ならルート証明書を含めている状態です。
証明書を更新したときは、新しい証明書と一緒に配布された中間証明書でチェーンを作り直してください。同じ CA でも、更新のタイミングで発行元の中間 CA が変わることがあります。
まとめ
- パソコンのブラウザでは開けても、
curl・アプリ・webhook で失敗するなら、中間証明書の欠落を疑う openssl s_client -servernameで、送られている証明書とVerify return codeを確認する- チェーンは「サーバー証明書 → 中間証明書」の順。ルート証明書は含めない
- Nginx は
ssl_certificate、Apache 2.4.8 以降はSSLCertificateFileに fullchain を指定する。Certbot ならfullchain.pemを使う
関連記事: