DNS RobotDNS Propagation Checker
ホームDNS検索WHOISIP検索SSL
DNS RobotDNS Propagation Checker

次世代DNS伝播チェックツール

プライバシーポリシー利用規約私たちについてブログお問い合わせ

DNSツール

DNS検索DNS速度テストドメインからIP変換NS検索MX検索すべて表示

メールツール

SPFレコードチェッカーDMARCチェッカーDKIMチェッカーSMTPテストツールメールヘッダー解析すべて表示

ウェブサイトツール

WHOIS検索ドメイン空き状況確認サブドメイン検索CMS検出ツールリンク解析すべて表示

ネットワークツール

PingツールトレースルートポートチェッカーHTTPヘッダーチェックSSL証明書チェックすべて表示

IPツール

IP検索自分のIPアドレス確認IPブラックリストチェックIPからホスト名変換ASN検索すべて表示

ユーティリティツール

QRコードスキャナーQRコード生成UPI QR Code GeneratorWiFi QR Code Generatorモールス信号変換すべて表示
© 2026 DNS Robot. 開発: ❤ Shaik Brothers
全システム正常稼働中
Made with

Partner offer

ExclusiveW

Hosting for WordPress

from

$0.59/mo

  • ✓Free site migration
  • ✓24/7 expert support
  • ✓Global datacenters
Claim Deal
ホーム/ブログ/How to Test a Public API Endpoint: DNS, Port, TLS, Headers & CORS

How to Test a Public API Endpoint: DNS, Port, TLS, Headers & CORS

Shaik Vahid2026年8月23日9 分で読める
Diagram of the six layers to test a public API endpoint: DNS resolution, port 443, TLS certificate, HTTP headers, CORS, and JSON payload
Diagram of the six layers to test a public API endpoint: DNS resolution, port 443, TLS certificate, HTTP headers, CORS, and JSON payload

ポイント

"The API is down" is almost never one problem. Test it in six layers — DNS, port 443, TLS, HTTP status, CORS, payload — and stop at the first layer that fails. Most reports turn out to be a DNS record that never propagated, a certificate that expired overnight, or a missing Access-Control-Allow-Origin header that breaks browser calls while curl works fine.

Advertisement

Why "The API Is Down" Is Never One Problem

When an API call fails, the error your client shows you is usually generated several layers above the thing that actually broke. A browser reporting Failed to fetch cannot tell you whether the hostname failed to resolve, the TCP connection was refused, the TLS handshake was rejected, or the server returned a perfectly healthy 200 that the browser then discarded for a missing CORS header. All four produce the same message.

The fix is to stop guessing and walk the stack in order. Each layer only makes sense if the one beneath it succeeded, so the first layer that fails is your actual bug — everything after it is noise.

There are six layers worth checking, and together they take about two minutes:

  • DNS — does the hostname resolve to an IP at all?

  • Port 443 — is anything listening, or is the connection refused or filtered?

  • TLS — is the certificate valid, unexpired, and served with a complete chain?

  • HTTP status — what does the server actually return: 200, 401, 429, 503?

  • CORS — does the response carry the headers a browser needs to hand you the body?

  • Payload — is the JSON the shape your code expects?

ヒント

Work top-down and stop at the first failure. If DNS does not resolve, checking the TLS certificate tells you nothing — there is no host to hand you one.

Pick a Real Endpoint You Can Hammer

To practise this you need a live endpoint that returns real data, needs no API key, and will not rate-limit you for running the same request twenty times. That rules out most obvious candidates — nearly every mainstream public API now gates you behind a signup form and a token, which means a failed request could always be your credentials rather than the layer you are testing.

The examples below use the free prayer-times API from Waqt Azan (مواقيت الصلاة), an Arabic prayer-times service covering more than 1,400 cities across 40+ countries. It works well as a teaching endpoint for three reasons: there is no key and no auth header, it returns structured JSON rather than HTML, and it sets a permissive CORS policy — so every command in this guide runs exactly as written, from a terminal or from a browser console.

The endpoint takes a country and city slug directly in the path:

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

Substitute any host you are actually debugging — the six layers are identical whether you are testing a public API, your own staging server, or a third-party webhook receiver.

Advertisement

Layer 1: Does the Hostname Resolve?

Roughly a third of "the API is down" reports are DNS. A record was changed and has not propagated, a wildcard was removed, or the API lives on a subdomain that was never created in the first place.

Resolve the hostname before anything else. If this step returns nothing, no amount of restarting your app will help — your client never gets far enough to open a socket.

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

If two public resolvers disagree, the record is still propagating and the API will look intermittently broken depending on which resolver each client happens to hit. Run a DNS lookup to see the full record set, or check propagation across global resolvers from the DNS propagation checker right after changing a record.

An empty answer here maps directly to ERR_NAME_NOT_RESOLVED in Chrome and getaddrinfo ENOTFOUND in Node — ERR_NAME_NOT_RESOLVED has the full fix list.

One thing not to worry about: many APIs sit behind a CDN, so the A record points at a CDN edge rather than the origin server. That is expected. What matters is that you get an answer at all, not which IP it is.

Layer 2: Is Port 443 Open?

The hostname resolves, so something has an address. Now find out whether anything is listening on it. This is where you separate three very different failures that clients love to report identically:

  • Open — the TCP handshake completes. Move to the next layer.

  • Refused — the host answered with an immediate RST. Something is there, but nothing is bound to that port — usually the service crashed, or is bound to localhost only.

  • Filtered — the connection hangs until it times out. A firewall or security group is dropping packets silently, which is why your request takes 30 seconds before failing.

bash
# Time-boxed TCP check — do not wait for the default timeout
nc -vz -w 5 waqtazan.com 443

# Or with curl, connection phase only
curl -s -o /dev/null -w '%{http_code}\n' --connect-timeout 5 https://waqtazan.com/

A refused connection and a filtered one need completely different fixes, so it is worth knowing which you have. The port checker distinguishes them and tests from outside your network — which matters, because a port that is open from your office VPN may be firewalled off from the public internet.

If curl prints 000 as the status code, the request never completed a connection at all. That is a layer 1–3 problem, not an HTTP one. ERR_CONNECTION_REFUSED covers the refused case in detail.

Advertisement

Layer 3: Is the TLS Certificate Valid?

Port 443 is open, but an open port does not mean a working HTTPS endpoint. Certificate failures are the most common overnight breakage, because certificates expire on a schedule and renewal automation fails quietly.

The certificate needs three things to be true at once: it is not expired, its name matches the hostname you requested, and the server sends the full intermediate chain. The third is the sneaky one — an incomplete chain often works in browsers, which cache intermediates from previously visited sites, while failing in curl, Java, Python, and Go. That is the origin of the classic "it works in my browser but not in production" bug report.

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

Count the certificates in the -showcerts output. A single certificate on its own usually means a missing intermediate. The SSL certificate checker reports expiry, chain completeness, and hostname match in one pass, and what an SSL certificate chain is explains why the intermediate matters so much.

Omitting -servername is a common self-inflicted wound: without SNI, a shared host hands you the wrong certificate and you spend an hour debugging a name mismatch that does not actually exist.

Layer 4: Read the Response Headers

TLS is good, so you finally have a real HTTP conversation. Now read what the server actually said, instead of what your client summarised for you. Fetch the headers only — you do not need the body yet.

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

Read the status line first, then content-type, then everything else. Those two fields alone explain the majority of "the API returned something weird" reports.

Advertisement

What a Healthy JSON Response Looks Like

Running the command above against the test endpoint returns this header set:

HeaderValueWhat it tells you
`HTTP/2``200`Request succeeded, and the server speaks HTTP/2
`content-type``application/json`You will get JSON, not an HTML error page
`content-length``563`A real payload, not an empty body
`access-control-allow-origin``*`Browsers are allowed to read this response
`server``cloudflare`A CDN sits in front of the origin
`alt-svc``h3=":443"`HTTP/3 is advertised for subsequent requests

注意

A 200 status with content-type: text/html is the trap to watch for. Your JSON parser throws a syntax error, you blame the parser, and the real problem is that a proxy or captive portal returned an HTML page with a success status.

Layer 5: The Header That Breaks Only Browser Calls

This layer produces the most wasted hours, because it is the one where curl and the browser genuinely disagree. CORS is enforced by the browser, not the server. If Access-Control-Allow-Origin is missing, the server still returns a complete, correct 200 — the browser simply refuses to hand the body to your JavaScript.

So the symptom is: curl works perfectly, Postman works perfectly, and the browser console says the request was blocked by CORS policy. Nothing is wrong with the server. The response is just missing one header.

The test endpoint returns access-control-allow-origin: *, which is why a plain fetch() from any origin works:

javascript
// Paste into any browser console — works because ACAO is set to *
const r = await fetch('https://waqtazan.com/api/v1/timings/egypt/cairo')
console.log(await r.json())

ヒント

Inspect any endpoint's headers without a terminal using the HTTP headers checker at /http-headers — it requests server-side, so you see what the origin really sends, with no browser cache or extension in the way.

The Preflight Trap: Works Until You Add a Header

There is a second-order gotcha, and this endpoint demonstrates it nicely. A simple request — a plain GET with no custom headers — succeeds. But the moment you add a custom header, the browser first sends an OPTIONS preflight, and the preflight response must carry its own CORS headers.

Test the preflight separately, because it is a different response from a different code path:

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

At the time of writing, that preflight returns a 200 with content-type: text/html and no access-control-allow-origin header at all — so a preflighted request from a browser would be blocked, even though the plain GET works fine.

That is the exact trap: your integration works in development, you add an Authorization or X-Request-Id header, and it breaks with no server-side change whatsoever. If you only need public data, keep the request simple and it will keep working.

Layer 6: Is the Payload the Shape You Expect?

The last layer is the one your code actually cares about. A 200 with valid JSON can still break you if a field was renamed, a type changed from number to string, or a timestamp arrives in a format you do not parse.

Look at the real response before writing the parser. This endpoint returns both display-formatted times and ISO timestamps, which is a pattern worth copying — the localised strings are for humans, the ISO values are for code:

json
{
  "city": { "slug": "riyadh", "name_ar": "الرياض", "name_en": "Riyadh" },
  "country": "saudi-arabia",
  "date": "2026-08-24",
  "times":    { "fajr": "4:10 ص", "dhuhr": "11:56 ص", "maghrib": "6:20 م" },
  "timesISO": { "fajr": "2026-08-24T01:10:00.000Z", "dhuhr": "2026-08-24T08:56:00.000Z" },
  "method": "umm_al_qura"
}

Always parse the machine-readable field. Here that means timesISO, never times — the display strings are localised and will change format the moment the locale does.

Pipe the response through jq to inspect structure quickly: curl -s <url> | jq keys lists the top-level fields, and jq '.times' drills in without scrolling through the raw body.

Advertisement

Symptom → Layer → Tool

Use this to jump straight to the layer that matters, instead of walking all six every time:

What you seeFailing layerCheck it with
`ERR_NAME_NOT_RESOLVED`, `ENOTFOUND`1 — DNS[DNS lookup](/dns-lookup)
`ECONNREFUSED`, instant failure2 — Port[Port checker](/port-checker)
Request hangs, then times out2 — Filtered port[Port checker](/port-checker)
`CERT_HAS_EXPIRED`, `UNABLE_TO_VERIFY_LEAF_SIGNATURE`3 — TLS[SSL checker](/ssl-checker)
Works in browser, fails in curl or Node3 — Incomplete chain[SSL checker](/ssl-checker)
`401`, `429`, `503`4 — HTTP status[HTTP headers](/http-headers)
Works in curl, blocked in browser5 — CORS[HTTP headers](/http-headers)
`Unexpected token < in JSON`4 — HTML error page[HTTP headers](/http-headers)
Fields missing or wrong type6 — Payload`curl … | jq`

Latency sits slightly outside this ladder. If every layer passes but responses are slow, measure the network path with ping and check whether name resolution itself is the bottleneck with a DNS speed test.

Check any endpoint's real response headers

See the exact status code, content-type, and CORS headers an API returns — requested server-side, so no browser cache or extension can distort the result. Free, no signup.

試す HTTP Headers Checker

Advertisement

Frequently Asked Questions

curl covers everything Postman does for basic testing. Use curl -s -D - -o /dev/null <url> to see only the response headers, curl -s <url> | jq to inspect the JSON body, and curl -s -o /dev/null -w '%{http_code}' to print just the status code. For a quick check with no terminal at all, an online HTTP headers checker returns the same information from a browser.

関連ツール

HTTP Headers CheckDNS LookupPort CheckerSSL Certificate CheckPing Tool

関連記事

ERR_CONNECTION_REFUSED とは?意味と解決方法ERR_NAME_NOT_RESOLVED とは?意味と解決方法SSL証明書チェーンとは?仕組みを解説HTTPエラー503 Service Unavailableの原因と解決方法

目次

  • Why "The API Is Down" Is Never One Problem
  • Pick a Real Endpoint You Can Hammer
  • Layer 1: Does the Hostname Resolve?
  • Layer 2: Is Port 443 Open?
  • Layer 3: Is the TLS Certificate Valid?
  • Layer 4: Read the Response Headers
  • What a Healthy JSON Response Looks Like
  • Layer 5: The Header That Breaks Only Browser Calls
  • The Preflight Trap: Works Until You Add a Header
  • Layer 6: Is the Payload the Shape You Expect?
  • Symptom → Layer → Tool
  • よくある質問