Debugging Network Issues: DNS, SSL, and Headers

A layer-by-layer debugging walkthrough for DNS, TLS, HTTP headers and CORS, with the exact commands and the spec clause behind each failure signature.

"The site is down" is usually a lie - the site is fine, and something between you and it is not. Before you blame the application, check the four layers underneath: DNS resolution, the TLS handshake, the response headers, and the browser's CORS policy. Each has its own failure signature, and each can be checked in seconds without leaving the browser. If you only need the headline answer first, the Website Status Checker reports whether the site is reachable at all before you dig into layers.

The table below maps each symptom to the layer that produced it.

What you see Layer First check Authoritative spec
Works on my machine, fails for a colleague DNS dig <name> A +short against two resolvers RFC 1035, RFC 8020
Browser warning, but curl succeeds TLS openssl s_client -servername <host> RFC 5280, RFC 6066
Right status code, wrong or stale content HTTP cache curl -sSI <url> RFC 9111
Request works everywhere except the browser CORS Re-send with an Origin header WHATWG Fetch
Infinite bounce between two URLs Redirects Count hops and read location RFC 9110, RFC 7538

Layer 1: DNS - does the name resolve to what you expect?

DNS problems look like "it works on my machine" - because your resolver or hosts file differs from the user's. When a domain behaves differently for different people, start by querying the actual records with the DNS Lookup tool. Check the A and AAAA records against the IP your provider says the site should be on, and look at MX and TXT records when email or verification is the complaint.

Record Answers the question Query
A / AAAA Which IPv4 / IPv6 address serves this name? dig name A +short
CNAME Is this name an alias of another name? dig name CNAME +short
MX Which hosts receive mail for the domain? dig name MX +short
TXT SPF, DKIM and domain-verification tokens dig name TXT +short
NS / SOA Which servers are authoritative, and what is the zone serial? dig name NS +short
CAA Which certificate authorities may issue for this domain? dig name CAA +short

The record types and their semantics are defined in RFC 1035, the DNS specification. Two rules in that spec explain most "impossible" DNS findings. First, CNAME is exclusive: "If a CNAME RR is present at a node, no other data should be present," per RFC 1034 section 3.6.2, and RFC 2181 section 10.1 confirms "there may be only one such canonical name for any one alias." So a CNAME at the apex - or a CNAME sitting beside the MX records - is invalid, and providers reject it for that reason.

Second, "not found" is louder than most people assume. RFC 8020 states that an NXDOMAIN response denies the name and every name underneath it, so deleting a parent record takes its children offline even when those child records still exist. The related timing limits are in RFC 1035 section 2.3.4: a single label is at most 63 octets, a whole domain name at most 255, and a record's TTL is "positive values of a signed 32 bit number." That TTL is why a fix can look broken for hours while cached answers age out - and why a stale record is not necessarily a wrong record.

dig softwaretesttips.com A +short
dig softwaretesttips.com MX +short
dig @1.1.1.1 softwaretesttips.com AAAA
dig +trace softwaretesttips.com

The last command is the one that earns its keep: +trace walks the delegation chain from the root down, so you see which nameserver is actually handing out the answer you dislike. If your own resolver is the suspect, swap in a public one - and remember that encrypted transports now run DNS over HTTPS, which hides the query from the local network. RFC 8484 defines that mapping of a DNS query-response pair onto an HTTP exchange, and it is why a query that fails in dig can still work in a browser configured for it.

Layer 2: TLS - is the certificate valid, and for the right names?

Certificate errors usually mean one of four things: the certificate expired, it does not cover the hostname being requested, the chain of signatures does not reach a trusted root, or the system clock is wrong. The SSL Checker pulls the certificate from public Certificate Transparency logs and shows the issuer, validity window, and the subject alternative names it actually covers.

Chain validation is not a single check but an algorithm: it walks from the leaf certificate through the issuers until it reaches a trust anchor, then verifies every signature and constraint along the way. RFC 5280 section 6.1 specifies that path validation - "valid paths begin with certificates issued by a trust anchor." That is why an "invalid certificate" on a perfectly current certificate is usually a missing intermediate: the leaf is fine, but the server never sent the certificate that signs it.

The classic hostname gotcha runs the other way. A certificate valid for example.com but not www.example.com - or vice versa - fails even though the dates are perfect, because browsers match the requested name against the SAN list. On a shared load balancer the server picks the certificate from the name the client sends during the handshake; that indicator is the server_name extension defined in RFC 6066 section 3. Omit it - as some old clients and many bare openssl invocations do - and you get the default certificate, which looks like a wrong-certificate bug but is really a missing SNI argument.

Handshake cost is the other number worth knowing. RFC 8446, the TLS 1.3 specification, completes a full handshake in a single round trip, with an optional 0-RTT mode that "saves a round trip at connection setup for some application data." On a high-latency link that is a measurable difference between TLS 1.3 and an older stack, and it is worth checking before you blame the application for a slow first byte.

Two policy failures also surface as TLS problems. A site that refuses plain HTTP for a year advertises that with Strict-Transport-Security: max-age=31536000 - the one-year value used throughout RFC 6797, HTTP Strict Transport Security. And if a certificate authority refuses to issue, check for a CAA record: RFC 8659 defines the record that "allows a DNS domain name holder to specify one or more Certification Authorities authorized to issue certificates," so a leftover CAA from an old provider blocks the new one.

openssl s_client -connect softwaretesttips.com:443 -servername softwaretesttips.com
openssl s_client -connect softwaretesttips.com:443 -servername softwaretesttips.com -showcerts

Read the output bottom-up: the verification result first, then the chain, then the subject and SAN list. If the chain stops short of a known root, the server is not sending the intermediate. If the dates look impossible, check the clock on the machine doing the verifying before you file a ticket - an expiry that is off by years is a clock-skew symptom, not a certificate symptom.

Layer 3: Headers - what is the server actually saying?

When the connection works but the behaviour is wrong, read the response headers. The HTTP Header Checker fetches a URL and shows exactly what the server sent, which answers a surprising number of "why is this broken" questions. Caching is the most common answer, and RFC 9111 defines the directives that decide it.

Directive What it means for the cached copy
max-age=600 Reuse without revalidating for 600 seconds.
no-cache May be stored, but must be revalidated before every reuse.
no-store Must not be stored at all - the setting that removes caching as a suspect.
must-revalidate Never serve the stored copy once it is stale, even if the origin is down.
private / s-maxage Keep it out of shared caches; give the CDN its own expiry.
stale-while-revalidate=30 Serve the stale copy for 30 seconds while refreshing in the background (RFC 5861).

Redirects are the second most common header bug. RFC 9110 section 15.4 defines the family, with 308 specified separately in RFC 7538: 301 and 308 are permanent, 302 and 307 are temporary, and only 307 and 308 guarantee that a redirected POST stays a POST - a form that silently becomes a GET is usually a 301 where a 307 was needed.

Then confirm whether the browser is even asking for the page. Conditional requests carry the validator the server supplied - ETag in the response, If-None-Match in the next request - and 304 Not Modified means the client keeps its copy. When a response varies by request header, Vary tells caches which one mattered; a missing Vary: Origin in front of a CDN is how one visitor's CORS headers reach another. The HTTP Status Codes reference explains any code you do not recognize.

curl -sSI https://example.com
curl -sS -o /dev/null -L -w 'code=%{http_code} hops=%{num_redirects} dns=%{time_namelookup}s tls=%{time_appconnect}s total=%{time_total}s\n' https://example.com

The second command splits the request into its phases, which shows whether the time is going to DNS, to the TLS handshake, or to the server.

Layer 4: CORS - the one that only appears in the browser

CORS failures are the strangest to debug because curl cannot reproduce them - the browser enforces the policy, not the server. For anything beyond a simple request the browser first sends an OPTIONS request and checks the answer; the WHATWG Fetch standard defines that step as the CORS-preflight fetch. The CORS Tester sends a request with a chosen Origin header and reports which access-control-* headers actually came back.

Read the two failures differently. If access-control-allow-origin does not match your origin, the fix belongs in the API configuration, not the frontend - and a wildcard * is not enough once credentials are involved. If the preflight itself is rejected, look for a missing access-control-allow-methods or access-control-allow-headers: the browser asked permission for a method the server never listed.

The runbook: four commands, in order

  1. Does the name resolve, and to the address you expect? dig name A +short, then repeat against a public resolver.
  2. Does the TLS handshake complete with a valid chain and the right SAN? openssl s_client -servername name.
  3. What did the server actually answer, and how long did each phase take? curl -sS -L -w with the timing format.
  4. Is the browser enforcing a policy the server never granted? Re-send with an Origin header and read the access-control-* response.

Nine times out of ten the answer is in one of those four places - and when it is not, you have per-layer evidence for the person upstream.

Related reading

  • How to Test JSON APIs Without Writing Code - once the plumbing checks out, that guide covers the other half of API debugging: making sense of the payloads the server finally delivers.
  • How to Generate Realistic Test Data for QA - reproducing an intermittent network bug usually means building a realistic reproducer payload, and that guide shows how to generate one without real user data.
  • Regex Testing for Beginners - a surprising number of "network" bugs are really a validation pattern rejecting a valid hostname, an email or a header value.
  • Smoke Testing vs Sanity Testing - the layer-by-layer walk here is the manual version of a smoke test; that guide explains where each check belongs in a release cycle.
  • Webhook Tester - when an integration fails silently, capturing the raw delivery is the fastest way to see whether the request ever arrived intact.