2026年8月18日

2026年8月18日

curl で出るエラーの読み解き方|終了コードとメッセージで名前解決・接続・TLS・HTTPを切り分ける

curl のエラーは「どの段階で止まったか」を示しています。名前解決 → TCP 接続 → TLS ハンドシェイク → HTTP の応答という順に通信が進むので、終了コードとメッセージからその段階を特定すれば、調べる場所はかなり絞れます。この記事では、よく出るエラーを段階ごとに読み解き、それぞれの確認コマンドと直し方を説明します。

まず「どこで止まったか」を見る

最初にやることは、詳細表示で通信の流れを見ることです。-v を付けると、接続先の IP、TLS のやり取り、送ったリクエストヘッダー、返ってきたレスポンスヘッダーが順に表示されます。

curl -v https://example.com/ -o /dev/null
echo "exit code: $?"

出力の * で始まる行が curl 自身の状況、> が送信したヘッダー、< が受信したヘッダーです。* Trying 192.0.2.10:443... の後で止まっていれば TCP 接続の段階、* TLSv1.3 (OUT), TLS handshake の途中で止まっていれば TLS の段階、< の行が出ていればサーバーまで到達して HTTP の応答を受け取れています。

終了コード($?)も手がかりになります。代表的なものは次のとおりです。

  • 6: ホスト名を解決できない(Could not resolve host)
  • 7: サーバーに接続できない(Failed to connect / Connection refused)
  • 28: タイムアウト(Operation timed out / Connection timed out)
  • 35: TLS ハンドシェイクの失敗(SSL connect error)
  • 60: サーバー証明書の検証に失敗(古い版の curl では 51 が使われることがあります)
  • 52: サーバーが何も返さずに切断した(Empty reply from server)
  • 56: 受信中に接続が切れた(Recv failure / Connection reset by peer)
  • 22: -f(--fail)指定時に HTTP ステータスが 400 以上だった

終了コードの一覧と意味は curl のマニュアル(man curl の EXIT CODES)に載っています。版によって追加・統合されたコードがあるため、迷ったら手元の版のマニュアルで確認してください(2026年10月現在)。

注意したいのは、404 や 500 が返っても curl 自体は成功(終了コード 0)として終わる点です。サーバーから応答が返った時点で通信は成立しているためです。スクリプトで HTTP エラーも失敗として扱いたいときは -f か --fail-with-body を付けます。

段階ごとの所要時間を測る

「遅い」「たまに失敗する」ときは、-w で各段階の経過時間を出すと、どこに時間がかかっているかが分かります。

curl -s -o /dev/null https://example.com/ \
  -w 'dns:%{time_namelookup} connect:%{time_connect} tls:%{time_appconnect} ttfb:%{time_starttransfer} total:%{time_total} code:%{http_code}\n'

値は開始からの累積秒数です。dns が大きければ DNS、connect と dns の差が大きければネットワーク経路、tls と connect の差が大きければ TLS、ttfb と tls の差が大きければサーバー側の処理(WordPress なら PHP やデータベース)に時間がかかっています。

名前解決のエラー(終了コード 6)

Could not resolve host: example.com は、ホスト名から IP アドレスを引けなかったことを示します。原因として多い順に、次を確認します。

  1. URL の打ち間違い。サブドメインの綴りやスキームの付け忘れは意外と多い原因です。
  2. DNS レコードが未設定、または変更直後で反映待ち。dig example.com A や dig example.com AAAA で応答を確認します。権威サーバーを直接引くなら dig @ネームサーバー example.com です。
  3. 実行環境の DNS 設定の問題。コンテナや社内ネットワークでは /etc/resolv.conf の参照先が外部の名前を引けないことがあります。
  4. プロキシ設定。環境変数 https_proxy などが残っていると、プロキシ名の解決に失敗して終了コード 5 になることがあります。env | grep -i proxy で確認します。

DNS を切り替える前に新しいサーバーを試したいときは、--resolve で名前と IP の対応を一時的に指定できます。

curl -v --resolve example.com:443:192.0.2.20 https://example.com/

この方法なら SNI やホスト名の検証は example.com のまま行われるので、本番切り替え前の動作確認に向いています。

接続のエラー(終了コード 7・28)

Connection refused はサーバーまで届いたものの、そのポートで待ち受けているプロセスがない状態です。Web サーバーが停止している、待ち受けポートが違う、127.0.0.1 だけで待ち受けている、といった原因が考えられます。サーバー側で ss -tlnp を実行し、443 や 80 を誰が待ち受けているかを確認します。

Connection timed out や Failed to connect ... after 数万 ms は、応答そのものが返ってこない状態です。ファイアウォールやクラウドのセキュリティグループでパケットが捨てられている可能性が高く、次の順で確認します。

  1. サーバー側のファイアウォール(ufw、firewalld など)で該当ポートが許可されているか
  2. クラウドのセキュリティグループやネットワーク ACL で送信元が許可されているか
  3. 送信元(社内ネットワークなど)の出口で通信が止められていないか
  4. IPv6 の経路だけが壊れていないか。-4 と -6 を付けて結果を比べます

接続はできるのに応答が遅くてタイムアウトする場合は、--connect-timeout(接続までの上限)と --max-time(全体の上限)を分けて指定すると、どちらで切れたのかが分かります。

TLS・証明書のエラー(終了コード 35・60)

HTTPS で最も迷いやすいのがこの段階です。メッセージの文言で原因を見分けます。

SSL certificate problem: unable to get local issuer certificate は、証明書チェーンをたどって信頼できるルートに到達できなかったことを示します。サーバーが中間証明書を送っていないケースが最も多く、次に、クライアント側の CA バンドルが古いケースがあります。サーバーが送っている証明書は次のコマンドで確認できます。

openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null

Certificate chain に中間証明書が含まれていなければ、Web サーバーの設定でフルチェーンの証明書ファイルを指定し直します。

certificate has expired は有効期限切れです。サーバー証明書だけでなく、中間証明書や、クライアント側の時計のずれも確認対象です。date で実行環境の時刻を見ておくと見落としを防げます。

no alternative certificate subject name matches target host name は、証明書に記載されたホスト名(SAN)と、アクセスしたホスト名が一致しない状態です。www あり・なしの片方だけが証明書に入っている、IP アドレスで直接アクセスしている、といった原因が典型です。

SSL connect error や wrong version number、handshake failure は、ハンドシェイク自体が成立していない状態です。HTTPS ではないポート(80 番など)に https で接続している、サーバーとクライアントで共通の TLS バージョンや暗号スイートがない、SNI が必要なサーバーに古いクライアントで接続している、といった原因が考えられます。--tlsv1.2 や --tls-max 1.2 でバージョンを固定して試すと、どこに差があるかを切り分けられます。

使っている curl の TLS ライブラリは curl -V で確認できます。同じエラーでも OpenSSL、Schannel(Windows 標準の curl)、Secure Transport などでメッセージの文言が異なります。Windows では証明書の失効確認に失敗してエラーになることがあり、その場合は社内プロキシや失効確認の通信経路を疑います。

なお、-k(--insecure)で検証を無効にすると通信はできるようになりますが、これは原因の切り分けに一時的に使う手段です。スクリプトや本番の設定に残すと、通信の相手が本物かどうかを確かめられなくなります。社内 CA などを使う場合は --cacert で信頼する CA を明示するのが正しい対処です。

サーバー到達後のエラー(終了コード 52・56・22)

Empty reply from server は、TCP 接続はできたのにサーバーが何も返さずに切断した状態です。Web サーバーやアプリケーションのプロセスが処理中に異常終了した、ロードバランサーのバックエンドが落ちている、といった原因が考えられます。サーバー側のエラーログ(Nginx や Apache の error.log、PHP-FPM のログ)を同じ時刻で確認します。

Connection reset by peer は、通信の途中で相手から接続を切られた状態です。WAF やプロキシに遮断された、タイムアウト値の短い中継機器で切られた、といった原因が多く見られます。

HTTP ステータスが 4xx や 5xx の場合は、curl ではなくアプリケーション側の問題です。-i でレスポンスヘッダーと本文を表示し、Server や CDN 固有のヘッダーを見ると、どの層がそのエラーを返したのかを判断できます。リダイレクトが続く場合は -L を付けて --max-redirs で回数を制限し、ループしていないかを確かめます。

調べた結果を共有するとき

調査結果をチームや問い合わせ先に渡すときは、-v の出力に加えて --trace-ascii trace.txt で詳細ログを保存すると話が早く進みます。ただし、これらの出力には Cookie、Authorization ヘッダー、API キーなどがそのまま含まれます。共有前に該当部分を伏せるか、認証情報を含まないリクエストで再現してから渡してください。

まとめ

  • curl のエラーは名前解決・TCP 接続・TLS・HTTP のどの段階で止まったかを示している。-v と終了コードで段階を特定する
  • 遅さの切り分けには -w で各段階の経過時間を出す
  • 終了コード 6 は DNS、7 は待ち受けなし、28 はファイアウォールなどでの破棄が典型
  • 証明書エラーはメッセージで中間証明書不足・期限切れ・ホスト名不一致を見分け、openssl s_client で裏付けを取る
  • -k は切り分け専用。恒久対処は証明書チェーンの修正か --cacert の指定
  • 404 や 500 でも curl は終了コード 0。スクリプトでは -f や --fail-with-body を使う

お気軽にご相談ください

お見積りへ お問い合わせへ
▲