pingは通るのにcurlが失敗する
「疎通はあるのに繋がらない」を層ごとに下から潰す手順が身につく。ICMP・TCP・TLS・HTTPのどこで落ちているかを3コマンドで確定させ、犯人をファイアウォールか証明書かアプリかに絞り込める。
- pingが通るのはL3(IP到達性)が生きている証拠にすぎない。TCPの特定ポート・TLSハンドシェイク・HTTPの応答は、それぞれ別の層で独立に失敗しうる。
- 切り分けは下から順に ping → nc -vz でポート → openssl s_client でTLS → curl -v でHTTP。最初に失敗した層が犯人で、それより上は見る必要がない。
- 即座に Connection refused なら相手は生きていてポートが閉じている。数十秒沈黙して timeout ならファイアウォールが黙って捨てている。この応答時間の差が最初の決め手になる。
「サーバーには ping が通る。なのにアプリからは繋がらない」——現場で最も多い報告のひとつです。この報告が厄介なのは、ping が通ったという事実が判断を歪めるからです。「ネットワークは生きている」と結論づけてアプリ側だけを疑い始めると、実際にはファイアウォールのポート設定が原因だった、という遠回りをします。
この記事は特定のエラーメッセージの逆引きではなく、症状から犯人の層を確定させる手順を扱います。
なぜpingが通っても意味が薄いのか
ping が使う ICMP エコー要求と、アプリが使う TCP は、IP の上に乗る別のプロトコルです。ping が成功して分かるのは次の3点だけです。
- 名前解決が(ping にホスト名を渡したなら)できている
- 経路が存在し、相手ホストまでパケットが届く
- 相手ホストの OS が生きていて ICMP に応答する設定になっている
逆に言えば、そこから先はすべて未確認です。目的のポートで誰かが待ち受けているか、TLS ハンドシェイクが成立するか、アプリが正しく応答するか。これらは ping では一切分かりません。多くのファイアウォールやセキュリティグループは ICMP を許可しつつ TCP をポート単位で絞るので、「ping は通るが繋がらない」はむしろ設定として自然な状態です。
「ping が通らないから落ちている」も同様に誤りです。クラウドのセキュリティグループは既定で ICMP を拒否することが多く、Web サーバーとしては完全に正常なのに ping だけ返らないホストは珍しくありません。ping の成否は、ホストの生死の証明にも反証にもなりません。
切り分けは下から順に、最初に落ちた層で止める
横にスクロール
原則はひとつです。下の層から順に確かめ、最初に失敗した層が犯人。それより上の層は見る必要がありません。
| 層 | 確認コマンド | ここで失敗したら疑うもの |
|---|---|---|
| L3 到達性 | ping <host> | 経路・ルーティング・ホスト停止(ICMP拒否の可能性も) |
| L4 ポート | nc -vz <host> <port> | ファイアウォール/セキュリティグループ、アプリ未起動、バインドアドレス |
| L4 経路の中間 | traceroute -T -p <port> | 途中のNAT・LB・NAPTでの遮断 |
| L6 TLS | openssl s_client -connect host:443 -servername host | 証明書の期限・チェーン・SNI・プロトコル/暗号スイート不一致 |
| L7 HTTP | curl -v https://host/path | Hostヘッダによる振り分け、パス、認証、アプリのエラー |
手順1: ポートが開いているか(ここで大半が決まる)
nc(netcat)でポートだけを叩きます。HTTP を喋らずに TCP の接続確立だけを見るのが要点です。
# -v 詳細 / -z 接続だけしてデータを送らない / -w タイムアウト秒
nc -vz api.example.com 443
# 成功: Connection to api.example.com 443 port [tcp/https] succeeded!
ここで失敗の仕方が決定的な情報になります。
即座に Connection refused
→ 相手ホストは生きていて、そのポートで誰も LISTEN していない
=アプリ未起動/ポート番号違い/127.0.0.1 だけにバインド
数十秒沈黙して timed out
→ パケットが黙って捨てられている
=ファイアウォール/セキュリティグループ/NACL の DROP
この差が生まれる理屈は単純です。ポートが閉じているだけなら相手 OS のカーネルが即座に TCP RST を返します。一方ファイアウォールの DROP は何も返さないため、こちらは SYN を再送し続け、最終的にタイムアウトします。個別のエラーの詳細はECONNREFUSEDとETIMEDOUTにまとめてあります。
接続先ホストにログインできるなら ss -ltnp が一発です。目的のポートの行が無ければアプリ未起動、127.0.0.1:8080 になっていればループバック限定バインドで外から繋がりません。0.0.0.0:8080 や [::]:8080 なら全インターフェースで待ち受けています。読み方はss -tulpn の出力解剖で詳しく扱っています。
手順2: TLSハンドシェイクが成立するか
ポートは開いたのに HTTPS だけ失敗する場合、TLS 層を単独で確かめます。
openssl s_client -connect api.example.com:443 -servername api.example.com </dev/null
-servername を必ず付けます。これが SNI で、1つのIPで複数の証明書を出し分けているサーバーでは、これが無いと意図しない証明書が返ってきます。「ブラウザでは見えるのにスクリプトからは証明書エラー」の典型的な正体がこれです。出力では次を見ます。
Verify return code: 0 (ok)かどうかsubjectとissuer、そして証明書チェーンが最後まで繋がっているかNotAfterの日付(期限切れは驚くほど多い)
チェーンが途中で切れている場合、サーバーが中間証明書を配信し忘れています。これはブラウザでは動くのに curl やアプリからは失敗するという紛らわしい症状を生みます。ブラウザは不足した中間証明書を自前でキャッシュしたり取得したりして補完してしまうためです。詳細はSSL certificate verify failedにあります。
手順3: HTTPの応答を見る
ここまで通ったなら、残るはアプリの層です。
curl -v -o /dev/null -w '%{http_code} %{time_total}s\n' https://api.example.com/health
-v でリクエスト行とレスポンスヘッダを確認します。ここで多いのは、接続先は正しいのに Host ヘッダの振り分けで別のバックエンドに落ちているケースです。ロードバランサやリバースプロキシは Host ヘッダでバックエンドを選ぶため、IP 直打ちや /etc/hosts での上書きをすると、想定と違うアプリに届きます。
# IPは固定しつつ、正しいHost/SNIで叩く(hostsを汚さない)
curl -v --resolve api.example.com:443:203.0.113.10 https://api.example.com/health
--resolve は接続先IPだけを差し替え、Host ヘッダも SNI も正しいまま保つので、LBの特定ノードだけが壊れているといった切り分けに極めて有効です。
よくある落とし穴
アプリからだけ失敗し、curlでは成功する。 この場合ネットワークは無罪です。疑うのはプロキシ設定(HTTP_PROXY/NO_PROXY 環境変数をアプリだけが読んでいる)、コンテナ内と外での名前解決の差、アプリが使う CA バンドルの場所、そしてクライアント側のタイムアウト値です。curl は成功しても、アプリのタイムアウトが 1 秒なら 1.2 秒かかる応答は失敗します。
時々だけ失敗する。 DNS が複数のIPを返していて、そのうち1台だけが壊れている可能性があります。dig +short で全IPを列挙し、--resolve で1台ずつ叩けば特定できます。名前解決そのものが疑わしい場合は名前解決だけが遅い・たまに失敗するを参照してください。
社内からは繋がるが外からは繋がらない。 送信元IPによるフィルタ、スプリットホライズンDNS(社内と社外で違うIPを返す)、あるいは内部専用のロードバランサに向いているといった構成差を疑います。
症状が出たときに、いきなりファイアウォールを開けたりTLS検証を無効化したりして「直った」とするのは最悪の対処です。原因が特定できていないので再発しますし、検証の無効化は恒久的な穴として残ります。上の手順は数分で終わるので、必ず犯人の層を確定させてから設定に触ってください。
まとめ
- ping の成功は L3 到達性の証明でしかない。目的のポート・TLS・HTTP はそれぞれ独立に失敗しうる。
- 切り分けは下から順に
ping→nc -vz→openssl s_client -servername→curl -v。最初に失敗した層が犯人で、それより上は見なくてよい。 - 即 refused ならポートが閉じている、沈黙して timeout ならフィルタが捨てている。応答時間の差が最初の決め手。
--resolveを使えば Host と SNI を正しく保ったまま接続先IPだけを差し替えられ、LBの特定ノードの故障を切り分けられる。- curl では成功しアプリだけ失敗するなら、プロキシ環境変数・CAバンドル・タイムアウト値というクライアント側の差を疑う。
トラブルシュート実戦の記事ガイド
pingは通るのにcurlが失敗するを実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
トラブルシュート
比較で見る軸
難易度: advanced / カテゴリ: トラブルシュート実戦 / タグ数: 6
導入後に効く点
切り分けは下から順に ping → nc -vz でポート → openssl s_client でTLS → curl -v でHTTP。最初に失敗した層が犯人で、それより上は見る必要がない。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- トラブルシュート実戦
- タグ数
- 6
判断チェックリスト
- 自社の用途が「トラブルシュート / ネットワーク」に近いか確認する。
- 強みである「pingが通るのはL3(IP到達性)が生きている証拠にすぎない。TCPの特定ポート・TLSハンドシェイク・HTTPの応答は、それぞれ別の層で独立に失敗しうる。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。