API疎通確認の方法:DNS・TLS・CORSを順に調べる

ポイント

「APIが落ちた」は、ほとんどの場合1つの原因ではありません。DNS、443番ポート、TLS、HTTPステータス、CORS、ペイロードの6層を順に調べ、最初に失敗した層で止めます。よくある原因は、反映されていないDNSレコード、一晩で期限切れになった証明書、そしてcurlでは動くのにブラウザ呼び出しだけ壊す Access-Control-Allow-Origin ヘッダーの欠落です。

今すぐ確認

公開APIエンドポイントをテストする6つの層の図:DNS解決、443番ポート、TLS証明書、HTTPヘッダー、CORS、JSONペイロード
公開APIエンドポイントをテストする6つの層の図:DNS解決、443番ポート、TLS証明書、HTTPヘッダー、CORS、JSONペイロード
読み始める
  1. 「APIが落ちた」が1つの原因で済まない理由
  2. 何度叩いても問題ない実在のエンドポイントを選ぶ
  3. 第1層:ホスト名は解決できるか
  4. 第2層:443番ポートは開いているか
  5. 第3層:TLS証明書は有効か
  6. 第4層:レスポンスヘッダーを読む
  7. 正常なJSONレスポンスの見た目
  8. 第5層:ブラウザ呼び出しだけを壊すヘッダー
  9. プリフライトの罠:ヘッダーを足すまでは動く
  10. 第6層:ペイロードは想定した形か
  11. 症状 → 層 → ツール
  12. よくある質問

「APIが落ちた」が1つの原因で済まない理由

API呼び出しが失敗したとき、クライアントが表示するエラーは、実際に壊れた箇所より何層も上で生成されています。ブラウザの Failed to fetch だけでは、ホスト名が解決できなかったのか、TCP接続が拒否されたのか、TLSハンドシェイクが拒否されたのか、あるいはサーバーが正常な200を返したのにCORSヘッダーがないためブラウザが破棄したのか、区別できません。4つとも同じメッセージになります。

推測をやめて、スタックを順番にたどりましょう。各層は下の層が成功して初めて意味を持つため、最初に失敗した層が本当のバグで、それ以降はノイズです。

確認すべき層は6つあり、全部で2分ほどで済みます。

  • DNS:ホスト名はそもそもIPに解決されるか
  • 443番ポート:待ち受けているものはあるか、接続は拒否またはフィルタされていないか
  • TLS:証明書は有効で期限内か、チェーンは完全か
  • HTTPステータス:サーバーは実際に何を返すか(200、401、429、503)
  • CORS:ブラウザが本文を渡すために必要なヘッダーが付いているか
  • ペイロード:JSONはコードが想定する形か

エンドポイントのステータスとヘッダーを今すぐ確認

何度叩いても問題ない実在のエンドポイントを選ぶ

練習には、実データを返し、APIキーが不要で、同じリクエストを20回送ってもレート制限されないライブのエンドポイントが必要です。主要な公開APIの多くは今やサインアップとトークンを要求するため、失敗の原因がテスト対象の層ではなく認証情報かもしれず、候補はかなり絞られます。

以下の例では、Waqt Azan (مواقيت الصلاة) の無料の礼拝時刻APIを使います。アラビア語の礼拝時刻サービスで、100か国以上・6万以上の地点を扱っています。教材として向いている理由は3つあります。キーも認証ヘッダーも不要であること、HTMLではなく構造化されたJSONを返すこと、CORSポリシーが緩いことです。そのためcurlコマンドはそのまま動き、fetch() の例も、厳格なContent-Security-Policyのないページのコンソールから動きます。

エンドポイントは、国と都市のスラッグをパスにそのまま取ります。

bash
# Prayer times for a city — no API key, no headers required
curl -s https://waqtazan.com/api/v1/timings/saudi-arabia/riyadh

# The same data as a subscribable calendar feed
curl -s https://waqtazan.com/api/v1/ical/saudi-arabia/riyadh.ics

デバッグ対象の実際のホストに置き換えてください。公開API、自分のステージングサーバー、サードパーティのWebhook受信側のどれをテストする場合でも、6つの層は同じです。

第1層:ホスト名は解決できるか

DNSはよくある原因です。レコードを変更したが反映されていない、ワイルドカードを削除した、APIのサブドメインがそもそも作られていない、といったケースがあります。

まず何よりホスト名を解決してください。ここで何も返らなければ、アプリを何度再起動しても無駄です。クライアントはソケットを開く段階にすら進めません。

bash
dig +short waqtazan.com A
nslookup waqtazan.com

# Compare what different resolvers see — catches mid-propagation records
dig +short waqtazan.com A @1.1.1.1
dig +short waqtazan.com A @8.8.8.8

レコードを変更した直後に2つのパブリックリゾルバの答えが食い違うなら、まだ反映の途中です。クライアントがどのリゾルバに当たるかで、APIが断続的に壊れているように見えます(ジオルーティングのCDNは、リゾルバごとに常に異なるアドレスを返すことがあるため、設定したレコードと照らし合わせてください)。DNSルックアップで全レコードを確認するか、レコード変更の直後にDNS伝播チェッカーで世界中のリゾルバでの反映状況を確認できます。

ここで答えが空なら、ChromeではERR_NAME_NOT_RESOLVED、Nodeでは getaddrinfo ENOTFOUND に直結します。対処法の一覧は ERR_NAME_NOT_RESOLVED を参照してください。

気にしなくてよい点が1つあります。多くのAPIはCDNの背後にあるため、AレコードはオリジンサーバーではなくCDNのエッジを指します。これは正常です。重要なのはどのIPかではなく、答えが返ること自体です。

第2層:443番ポートは開いているか

ホスト名は解決できたので、アドレスは存在します。次に、そこで何かが待ち受けているかを調べます。クライアントがどれも同じように報告してしまう、まったく異なる3つの障害をここで切り分けます。

  • Open(開いている):TCPハンドシェイクが完了します。次の層へ進みます。
  • Refused(拒否):ホストが即座にRSTを返しています。ホストはありますが、そのポートにバインドされたものがありません。多くはサービスのクラッシュか、localhostのみへのバインドです。
  • Filtered(フィルタ):接続がタイムアウトするまで固まります。ファイアウォールやセキュリティグループがパケットを黙って破棄しているため、リクエストは1分以上待ってから失敗します(macOSでは75秒)。
bash
# Time-boxed TCP check: -w limits the connect on Linux; on macOS use -G 5 instead
nc -vz -w 5 waqtazan.com 443

# Or with curl: --connect-timeout caps the connect; 000 means no response came back
curl -s -o /dev/null -w '%{http_code}\n' --connect-timeout 5 https://waqtazan.com/

拒否とフィルタでは対処がまったく違うため、どちらかを知っておく価値があります。ポートチェッカーは両者を区別でき、ネットワークの外からテストします。社内VPNからは開いているポートが、公開インターネットからはファイアウォールで閉じられていることがあるため、これは重要です。

curlがステータスコードとして 000 を出力したら、HTTPレスポンスが返ってきていません。多くは第1〜3層での失敗(DNS応答なし、ポートの拒否やフィルタ、TLSハンドシェイクの失敗)ですが、接続を受け付けた後に切断する、または一切応答しないサーバーでも 000 になります。-v を付けると、最後に成功した段階が分かります。拒否の場合は ERR_CONNECTION_REFUSED で詳しく解説しています。

第3層:TLS証明書は有効か

443番ポートは開いていますが、ポートが開いていることはHTTPSエンドポイントが動くことを意味しません。証明書は期限で切れ、更新の自動化は静かに失敗するため、一晩で壊れる典型的な原因です。

証明書は3つの条件を同時に満たす必要があります。期限切れでないこと、名前が要求したホスト名と一致すること、サーバーが中間証明書のチェーンを完全に送ることです。3つ目が厄介です。チェーンが不完全でもブラウザでは動くことが多く、Chromeは足りない中間証明書を自分でダウンロードし、Firefoxは中間証明書をプリロードしています。一方、Node、Python、Java、OpenSSLビルドのcurlは失敗します(macOS標準のcurlはAppleの検証機構を使うため通ることがあります)。これが「ブラウザでは動くのに本番では動かない」という定番のバグ報告の原因です。

bash
# Expiry dates and subject
echo | openssl s_client -connect waqtazan.com:443 -servername waqtazan.com 2>/dev/null \
  | openssl x509 -noout -subject -dates

# Full chain as presented by the server
openssl s_client -connect waqtazan.com:443 -servername waqtazan.com -showcerts < /dev/null

-showcerts の出力に含まれる証明書の数を数えてください。証明書が1枚だけなら、多くは中間証明書の欠落です。SSL証明書チェッカーは、有効期限、チェーンの完全性、ホスト名の一致を一度に報告します。中間証明書がなぜ重要かは SSL証明書チェーンとは で解説しています。

-servername は残してください。OpenSSL 1.1.1以降は -connect のホスト名からSNIを設定しますが、macOS標準のLibreSSL版 openssl は設定せず、IPアドレスに接続する場合はどのバージョンでも設定されません。SNIがないと、共有ホストはデフォルト証明書を返すか、Cloudflareのようにハンドシェイクを中断し、存在しない名前の不一致を追いかけることになります。

第4層:レスポンスヘッダーを読む

TLSは問題ないので、ようやく本物のHTTPのやり取りができます。クライアントが要約したものではなく、サーバーが実際に返した内容を読みましょう。ここではヘッダーだけを表示します。本文はダウンロードして捨てているので、クライアントが送るGETと同じです。

bash
curl -s -D - -o /dev/null https://waqtazan.com/api/v1/timings/egypt/cairo

まずステータス行、次に content-type、そのほかは後です。この2つのフィールドで、リクエストが成功したか、本文がJSONかが、ほかを読む前に分かります。

正常なJSONレスポンスの見た目

上のコマンドを2026年10月27日にテスト用エンドポイントへ実行すると、ほかにもありますが次のヘッダーが返りました。

ヘッダー値分かること
HTTP/2200リクエストは成功し、サーバーはHTTP/2に対応している
content-typeapplication/jsonHTMLのエラーページではなくJSONが返る
content-length567空ではない実際のペイロードがある
access-control-allow-origin*ブラウザはこのレスポンスを読める
servercloudflareオリジンの前にCDNがある
alt-svch3=":443"以降のリクエストでHTTP/3が案内されている

第5層:ブラウザ呼び出しだけを壊すヘッダー

この層は何時間も溶ける場所です。curlとブラウザの結果が本当に食い違うからです。CORSを強制するのはサーバーではなくブラウザです。Access-Control-Allow-Origin がなくても、サーバーは完全で正しい200を返しますが、ブラウザがJavaScriptに本文を渡すのを拒否するだけです。

つまり症状はこうです。curlは完璧に動き、Postmanも完璧に動くのに、ブラウザのコンソールには、CORSポリシーによってリクエストがブロックされたと出ます。サーバーには何の問題もなく、レスポンスにヘッダーが1つ足りないだけです。

テスト用エンドポイントは access-control-allow-origin: * を返すため、どのオリジンからでも通常の fetch() が動きます。

javascript
// Run it in the console of a tab on a site without a strict CSP, such as example.com
// (this page's own CSP blocks it). It works there because ACAO is set to *
const r = await fetch('https://waqtazan.com/api/v1/timings/egypt/cairo')
console.log(await r.json())

プリフライトの罠:ヘッダーを足すまでは動く

もう一段階厄介な落とし穴があり、このエンドポイントがよい実例になります。シンプルなリクエスト(カスタムヘッダーなしの通常のGET)は成功します。しかしカスタムヘッダーを足した瞬間、または Content-Type: application/json でPOSTした瞬間、ブラウザはまず OPTIONS のプリフライトを送り、そのレスポンスにも独自のCORSヘッダーが必要です(MDN)。

プリフライトは別のコードパスから返る別のレスポンスなので、単独でテストしてください。

bash
curl -s -D - -o /dev/null -X OPTIONS \
  -H 'Origin: https://example.com' \
  -H 'Access-Control-Request-Method: GET' \
  https://waqtazan.com/api/v1/timings/egypt/cairo

2026年10月27日時点で、このプリフライトは content-type: text/html の200を返し、access-control-allow-origin ヘッダーはまったくありません。そのため、通常のGETは問題なく動くのに、プリフライトを伴うブラウザからのリクエストはブロックされます。

これがまさに罠です。開発中は動いていた連携に Authorization や X-Request-Id ヘッダーを足すと、サーバー側の変更が一切ないのに壊れます。公開データだけが必要なら、リクエストをシンプルに保てば動き続けます。

第6層:ペイロードは想定した形か

最後の層は、コードが本当に気にする層です。フィールド名が変わった、数値が文字列に変わった、パースできない形式のタイムスタンプが届いた、という場合は、有効なJSONの200でも壊れます。

パーサーを書く前に、実際のレスポンスを見てください。このエンドポイントは表示用にフォーマットされた時刻とISOタイムスタンプの両方を返します。真似する価値のあるパターンで、ローカライズされた文字列は人間向け、ISO値はコード向けです。

json
{
  "city": { "slug": "riyadh", "name_ar": "الرياض", "name_en": "Riyadh" },
  "country": "saudi-arabia",
  "date": "2026-10-26",
  "times":    { "fajr": "4:38 ص", "dhuhr": "11:37 ص", "maghrib": "5:17 م" },
  "timesISO": { "fajr": "2026-10-26T01:38:00.000Z", "dhuhr": "2026-10-26T08:37:00.000Z" },
  "method": "umm_al_qura"
}

必ず機械可読のフィールドをパースしてください。ここでは times ではなく timesISO です。表示用の文字列はローカライズされており、ロケールが変わればすぐ形式が変わります。

レスポンスを jq に通すと構造をすばやく確認できます。curl -s <url> | jq keys で最上位のフィールドを一覧でき、jq '.times' で生の本文をスクロールせずに掘り下げられます。

症状 → 層 → ツール

毎回6層すべてをたどらずに、問題の層へ直行するための表です。

見える症状失敗している層確認方法
ERR_NAME_NOT_RESOLVED, ENOTFOUND1 — DNSDNSルックアップ
ECONNREFUSED, instant failure2 — ポートポートチェッカー
リクエストが固まり、その後タイムアウト2 — フィルタされたポートポートチェッカー
CERT_HAS_EXPIRED, UNABLE_TO_VERIFY_LEAF_SIGNATURE3 — TLSSSLチェッカー
ブラウザでは動くが、curlやNodeでは失敗3 — 不完全なチェーンSSLチェッカー
401, 429, 5034 — HTTPステータスHTTPヘッダーチェッカー
curlでは動くが、ブラウザでブロックされる5 — CORSHTTPヘッダーチェッカー
Unexpected token '<', "<!DOCTYPE "... is not valid JSON4 — HTMLのエラーページHTTPヘッダーチェッカー
フィールドがない、または型が違う6 — ペイロードcurl … | jq

レイテンシはこの階層から少し外れます。すべての層が通るのにレスポンスが遅いなら、pingでホストまでの往復時間を測り、DNS速度テストで名前解決自体がボトルネックかどうかを確認してください。

エンドポイントの実際のレスポンスヘッダーを確認

APIが返すステータスコード、content-type、CORSヘッダーを正確に確認できます。リクエストはサーバー側から送るため、ブラウザのキャッシュや拡張機能の影響を受けません。無料・登録不要です。

出典

  1. Cross-Origin Resource Sharing (CORS), MDN
  2. Fetch Standard: CORS-safelisted request headers, WHATWG

よくある質問

PostmanなしでAPIエンドポイントをテストするには?

基本的なテストならcurlでPostmanと同じことができます。curl -s -D - -o /dev/null <url> でレスポンスヘッダーだけ、curl -s <url> | jq でJSON本文の確認、curl -s -o /dev/null -w '%{http_code}' でステータスコードだけを表示できます。ターミナルなしで手早く確認するなら、オンラインのHTTPヘッダーチェッカーがブラウザから同じ情報を返します。

APIがcurlでは動くのにブラウザでは失敗するのはなぜ?

ほぼ間違いなくCORSです。サーバーはどちらにも有効なレスポンスを返しますが、ブラウザは、あなたのオリジンを許可する Access-Control-Allow-Origin ヘッダーがレスポンスにない限り、JavaScriptに本文を渡しません。CORSはサーバーのポリシーではなくブラウザのセキュリティポリシーなので、curlは完全に無視します。ヘッダーがない場合はサーバー側で送る必要があり、クライアント側での回避策はありません。

curlのステータスコード000は何を意味する?

HTTPレスポンスを一度も受け取っていないため、報告できるステータスコードがないという意味です。多くはHTTPより前の段階で失敗しています。DNSが解決しなかった、TCP接続が拒否またはフィルタされた、TLSハンドシェイクが失敗した、などです。接続を受け付けた後に切断する、または応答しないサーバーでも000になるため、-v を付けて再実行し、最後に成功した段階を確認してください。

APIが全員にとって落ちているのか、自分だけなのか確認するには?

自分のネットワークの外からテストします。サーバー側で動くポートチェッカーとDNSルックアップなら、ローカルのリゾルバ、VPN、社内ファイアウォール、ブラウザのキャッシュの影響を排除できます。外部のチェッカーでは応答するのに自分のマシンからは応答しないなら、問題はローカルにあり、多くはDNSキャッシュかファイアウォールのルールです。

APIが200を返すのにJSONパーサーがエラーを出すのはなぜ?

ほぼ確実に、200ステータスでHTMLを受け取っています。キャプティブポータル、プロキシ、一部のエラーページは、HTML本文に成功コードを付けて返します。content-type ヘッダーを確認してください。application/json ではなく text/html なら、本文はWebページであり、パースエラーは原因ではなく症状です。

CORSプリフライトとは何で、いつ発生する?

リクエストが「シンプル」でないときに、本番のリクエストの前にブラウザが自動で送る OPTIONS リクエストです。GET、HEAD、POST以外のメソッドを使う、Authorization などのカスタムヘッダーを付ける、フォームデータやプレーンテキスト以外の Content-Type を送る(application/json が該当します)場合に発生します。プリフライトのレスポンス自体にもCORSヘッダーが必要です。認証ヘッダーを足すまでは連携が問題なく動き、サーバーの変更なしに壊れるのはこのためです。

APIのDNSは自分のリゾルバとパブリックリゾルバのどちらでテストすべき?

両方で、結果を比較してください。ISPのリゾルバは、権威側のレコードが変わった後も古いキャッシュを返し続けることがあります。デフォルトのリゾルバに加えて1.1.1.1と8.8.8.8にも問い合わせると、反映の遅れなのか、本当にレコードがないのかが分かります。

443番ポートが開いていれば、HTTPSエンドポイントは動いている?

いいえ。ポートが開いていることは、何かがTCP接続を受け付けたことしか示しません。証明書の期限切れ、ホスト名の不一致、中間証明書チェーンの不完全さによって、TLSハンドシェイクは失敗することがあります。ポートが開いていることを確認した後に、証明書を別途確認してください。

著者について

Shaik Vahid

DNS Robot 創設者. Vahid は長年 Web ホスティングのサポートで、お客様のドメイン、DNS レコード、SSL 証明書、メール配信の問題を解決してきました。DNS Robot のツールを開発し、実際によく起きるエラーをもとにこれらのガイドを書いています。

HTTPヘッダーチェッカー

HTTPレスポンスヘッダーとセキュリティヘッダーのグレーディング分析