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 アドレスを引けなかったことを示します。原因として多い順に、次を確認します。
- URL の打ち間違い。サブドメインの綴りやスキームの付け忘れは意外と多い原因です。
- DNS レコードが未設定、または変更直後で反映待ち。
dig example.com Aやdig example.com AAAAで応答を確認します。権威サーバーを直接引くならdig @ネームサーバー example.comです。 - 実行環境の DNS 設定の問題。コンテナや社内ネットワークでは
/etc/resolv.confの参照先が外部の名前を引けないことがあります。 - プロキシ設定。環境変数
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 は、応答そのものが返ってこない状態です。ファイアウォールやクラウドのセキュリティグループでパケットが捨てられている可能性が高く、次の順で確認します。
- サーバー側のファイアウォール(ufw、firewalld など)で該当ポートが許可されているか
- クラウドのセキュリティグループやネットワーク ACL で送信元が許可されているか
- 送信元(社内ネットワークなど)の出口で通信が止められていないか
- 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を使う