1.

curlの証明書ホスト名不一致|旧エラー51・現行60の確認と対処

▶

curlの証明書名不一致を、URL・返された証明書・接続経路に分けて調べるガイドです。検証を止めずに、誤った名前や配備先を直します。

接続IP、URL名とSNI、HTTP Host、証明書SANの関係

エラー内容

タイトルの(51)は旧curlでの表示です。現行curlでは証明書の名前不一致も(60)として出る場合があるため、番号だけでCA信頼の問題と区別しないでください。エラー本文と証明書を確認します。

curl: (51) SSL: certificate subject name '~' does not match target host name '~'

HTTPSのURLにあるホスト名と、サーバーが返した証明書の対象名が一致しないエラーです。DNS名はSANのdNSName、IPアドレスはSANのiPAddressで照合します。CNはSANと同列の単純な文字列検索ではありません。SANにDNS名がある場合、CNだけ一致しても救済されません。SANなしのCNフォールバックはTLSバックエンドや版によるため、新規発行は適切なSANを指定します。

この記事の要点
  • curl の SSL: certificate subject name does not match target host name
  • 原因: URLの名前と、返された証明書の対象名が合わない。SANを優先して照合する
  • URLのIP・ホスト名が証明書の対象に合わない場合に発生。IP用SANがあればIPでも検証できる
  • 対処: 証明書の SAN に合うホスト名でアクセス / 証明書を更新 / -kは修復ではなく検証無効化。通常は使用しない

 

発生条件 / 原因

  • 証明書がそのホスト名向けに発行されていない — 例: example.com 用証明書を api.example.com で使っている
  • IPアドレスで直接アクセスしている — https://192.168.0.10/ など。証明書にそのIPアドレスのSANがなければ不一致になる
  • 証明書のSANが古い — ドメイン追加後に証明書を更新していない
  • 自己署名証明書/社内CA — 発行元が未信頼の問題と、ホスト名不一致の問題は別。社内CAでも対象ホスト名が必要
  • SNI(Server Name Indication)と仮想ホストの選択 — SNIの欠落・違い、LBの設定や配備先の違いで、別の証明書が返る場合がある。クライアントが古いとだけ断定しない

対処法

1. 接続先ホスト名と証明書の中身を確認

まずcurl --versionで版とTLSバックエンドを記録し、URLのホスト名・ポート・利用する経路を確認します。次の旧例は返された証明書の表示用です。-servernameはSNIの指定であり、名前を検証する指定ではありません。接続可能でも相手を信頼できる証拠にはなりません。

$ openssl s_client -connect example.com:443 -servername example.com < /dev/null | openssl x509 -noout -subject -ext subjectAltName

このパイプの末尾はopenssl x509による表示です。表示できたことやパイプ全体の終了値だけでTLS検証成功と判断しません。s_clientは既定で検証エラー後も継続し得ます。返されたSAN・発行者・期限を確認し、違う証明書ならDNS・SNI・LBの証明書選択も調べます。証明書の再発行が常に必要とは限りません。

openssl s_client -connect 192.168.0.10:443 \
  -servername example.com -verify_hostname example.com \
  -verify_return_error -CAfile company-ca.pem < /dev/null

追加例はPOSIXシェル/OpenSSL向けです。接続先IP、SNI、検証名を別々に明示し、-verify_return_errorで失敗を止めます。company-ca.pemは管理者から別経路で確認したCAで置き換えてください。公開CAなら環境の信頼ストアを使う方法を確認します。OpenSSLの信頼ストアとcurlのTLSバックエンドのストアは同じとは限りません。IPの検証は-verify_hostnameではなく-verify_ipを使います。

2. 正しいホスト名で接続する

本来利用すべきサービスのURLを管理者の案内と照合します。証明書に載っているという理由だけで別のドメインへ変更し、資格情報を送らないでください。example.com用のDNS名証明書はapi.example.comやIPを自動的には含みません。

$ curl https://example.com/path

3. IP接続だが特定ホストで証明書を検証したい場合

直接接続では--resolveが指定したホスト名とポートの接続先IPを置き換えます。URLはexample.comのままなので、証明書の検証名とSNI、通常のHTTP Hostもexample.comです。証明書の名前検証を無効にはしません。次の旧例のIPは管理下の接続先へ読み替えてください。

$ curl --resolve example.com:443:192.168.0.10 https://example.com/path

URL・SNI・Hostとプロキシの経路を分ける

https://IP/ に -H 'Host: example.com' を付けても、TLSの検証対象はURLのIPのままです。HTTP HostはTLSハンドシェイクより後なので、SNIや証明書名を修復しません。HTTP/2の:authorityも同じ役割です。指定IPへ接続してDNS名で検証したいなら、URLをDNS名にして--resolveを使います。

次は直通が許可された管理下サーバーの診断例です。-qは最初の引数に置き、既定curl設定ファイルを読みません。--noproxy '*'はこの呼出しだけproxyを使わない指定で、TLS検証停止ではありません。組織の必須proxyを迂回する目的には使わず、直通を許可されていない場合は実行しません。--cacertは確認済みの専用CAファイルだけを指定し、OS全体の信頼設定は変更しません。

curl -q --noproxy '*' --cacert company-ca.pem \
  --connect-timeout 5 --max-time 15 \
  --resolve example.com:443:192.168.0.10 https://example.com/path

HTTPS_PROXY / https_proxy / ALL_PROXYとNO_PROXY / no_proxy、および--proxy / --noproxyで経路が変わります。HTTP用のhttp_proxyは小文字のみを読む点にも注意してください。設定やverboseログにはproxy認証・Cookie等が含まれ得るので、そのまま共有しません。

HTTP proxyのCONNECT経由では、通常proxyがURLの名前とポートへトンネルを作ります。--resolveを付けてもproxy側の名前解決を固定する保証にはなりません。socks5とsocks5hも名前解決する側が異なります。直接接続のs_clientとproxy経由のcurlで違う証明書を見ていないか、接続経路を揃えて比較します。

HTTPS proxyでは「curlからproxyへのTLS」と「トンネル内の接続先TLS」が別です。--proxy-cacertは前者、--cacertは後者の信頼に使い、相互の代用品にはしません。TLS検査proxyが証明書を再発行する構成なら、確認済みの組織CAと正しい対象名の両方が必要です。--proxy-insecureも修復策にしません。

4. 検証停止の旧例(隔離した負例に限る・非推奨)

次の-k付き旧コードは保存用の負例であり、開発・検証でも通常は推奨しません。-k / --insecureは名前とCAによる相手の検証を無効化し、原因を修復しません。成功しても正しい接続先である証拠にはなりません。実サーバーへ実行せず、挙動を試す必要がある場合も外部通信のない隔離環境とダミーデータだけに限定し、認証情報・Cookie・機密データは送らないでください。

$ curl -k https://192.168.0.10/path

5. 証明書側を直す(サーバー管理者側)

  • 必要なホスト名を SAN に追加して証明書を再発行する
  • Let's Encrypt なら certbot で対象ドメインを追加して再発行
  • 必要な名前を個別SANにするか、適用範囲を確認してワイルドカードを選ぶ。単に切り替えれば直るわけではない

社内CAは検証を残して信頼させる

信頼できる管理者から取得したCA証明書を指定する例です。CAへの信頼を追加しても、URLと証明書のホスト名不一致は直りません。エラー先から取得した証明書を確認せず信頼対象へ追加しないでください。

curl --cacert company-ca.pem https://api.example.com/path

ワイルドカード*.example.comは通常api.example.comのような1階層を対象にし、example.com自体やa.b.example.comを自動で含むものではありません。必要なSANを確認してください。

名前・CA・期限は別々の検査

観測切り分けと対処
SANが別のDNS名URL誤りか、DNS/SNI/LBの証明書選択かを確認。正しい送り先でも対象名が不足ならSANを含めて再発行
*.example.comapi.example.comのような1階層用。example.com、a.b.example.com、IPには使えない。必要な個別SANを確認
IPでのアクセスそのIPと一致するiPAddress型SANが必要。DNS型SANにIP文字列を書くだけでは代用不可。CNのIPでも代用不可
unable to get local issuer certificate等発行元への信頼とサーバーの中間証明書チェーンを確認。正しいCAを指定しても名前の不一致は直らない
certificate has expired / not yet valid証明書のnotBefore/notAfterと検証側の時計を確認。時刻を偽装せず、期限更新や正規の時刻同期を管理者へ依頼

複数の問題があれば最初のエラーだけが見える場合があります。CAを正しくした後も名前・期限が通るか再確認します。-kで成功したことを、これらのどれが原因かの判定に使わないでください。

注意点

  • -k は暗号化はされるが認証は無い状態になり、中間者攻撃の検知ができなくなる
  • 本番ではホスト名と証明書を必ず一致させる。AWS ALB/CloudFront などのCDN/LBを通すときも証明書のSANに注意
  • 旧(51)は現在の(60)へ統合されています(curl 7.62.0以降)。(60)だからCAだけの問題とは判断せず、名前不一致・信頼・期限などエラー本文を確認すること

安全な確認順

  1. curl --versionで版とTLSバックエンドを確認します。
  2. URLのホスト名と実際に返された証明書のSANを比較します。
  3. DNSだけを固定したい場合は、本文の--resolve例でURLの名前とSNIを維持します。
  4. 名前が正しければCAへの信頼・証明書チェーン・期限を確認します。
  5. -kを付けずに再実行し、証明書検証を通ることを確認します。

-kの例は検証を無効にする動作の説明として残しています。隔離したダミーデータの切り分けでも慎重に扱い、認証情報・Cookie・機密データを送信しないでください。

公式資料: curlエラー番号の統合、TLS検証とCA指定、ホスト名とIPの検証、curlオプション(2026年10月確認)。

公式資料と検証範囲(2026年10月26日)

curl公式: URL名・Host・SNIと名前解決、OpenSSL: s_clientの検証と停止条件、OpenSSL: SANとCNの照合を確認しました。オプションの詳細は既出のcurl公式資料を参照してください。

追加例の実測はcurl 8.14.1 / OpenSSL 3.5.7 / Python 3.13.5の専用Docker内で、生成したダミーCAとloopback TLSだけを対象にしています。名前不一致・誤CA・期限切れ、SAN優先、ワイルドカード/IP SAN、--resolveのSNI/Host維持、Hostのみの変更で修復しないこと、HTTP CONNECT経路、s_clientの表示と検証停止を確認しました。-kは試験でも使っていません。OS全体のCAや時計・既存設定は変更していません。実組織proxy、HTTPS proxy、SOCKS、Schannel等の他バックエンド、実サービスの更新・再発行は未検証です。

子ページ

子ページはありません

同階層のページ
  1. curl: (51) SSL: certificate subject name '~' does not match target host name '~'
  2. mount: special device ... does not exist
  3. 【yum install php-fpm】エラー: パッケージ: php-fpm-5.4.16-46.el7.x86_64 (base)
  4. ファイルが開けません: https://centos7.iuscommunity.org/ius-release.rpm を飛ばします。
  5. File "/usr/bin/yum", line 30 except KeyboardInterrupt, e:
  6. unable to execute 'gcc': No such file or directory

最近更新/作成されたページ