DNS failures have a specific torture quality: the system works perfectly for most people and fails intermittently for others, depending on which resolver their ISP or network assigned them. You can spend an hour debugging what turns out to be a resolver cache issue 500 kilometers away.
This guide gives you a systematic debugging process so you're not guessing.
How DNS Resolution Actually Works
When a browser navigates to api.example.com, the following chain runs:
- Browser cache — checks if it has a recent answer for this hostname
- OS resolver cache — checks
/etc/hostsand the OS DNS cache - Recursive resolver — your ISP's resolver, or
8.8.8.8, or1.1.1.1— this resolver does the heavy lifting - Root nameservers — there are 13 root nameserver clusters (a.root-servers.net through m.root-servers.net). They know which nameservers are authoritative for
.com,.io,.dev, etc. - TLD nameservers — know which nameservers are authoritative for
example.com - Authoritative nameservers — your DNS provider (Cloudflare, Route 53, etc.) — these have the actual records
The recursive resolver caches every response at each step according to the TTL on each record. This is why propagation is not instant — it's bounded by the TTLs in place at the time you make a change.
DNS Record Types
A Record
Maps a hostname to an IPv4 address.
api.example.com. 300 IN A 203.0.113.45
AAAA Record
Maps a hostname to an IPv6 address.
api.example.com. 300 IN AAAA 2001:db8::1
CNAME Record
Creates an alias from one hostname to another. The target must eventually resolve to an A or AAAA record.
www.example.com. 300 IN CNAME example.com.
api.example.com. 300 IN CNAME lb-123.us-east-1.elb.amazonaws.com.
Critical constraint: You cannot have a CNAME on an apex domain (example.com). CNAMEs require the target to be another hostname, and DNS doesn't allow other records (like MX or NS) to coexist with a CNAME. Use ALIAS or ANAME records (provider-specific) for apex domains, or A records pointing to IPs.
MX Record
Specifies which mail servers accept email for a domain.
example.com. 300 IN MX 10 mail1.example.com.
example.com. 300 IN MX 20 mail2.example.com.
The number is priority — lower is preferred.
TXT Record
Arbitrary text data. Used for:
- SPF records (
v=spf1 include:_spf.google.com ~all) - DKIM public keys (
v=DKIM1; k=rsa; p=...) - DMARC policy (
v=DMARC1; p=quarantine; rua=mailto:...) - Domain ownership verification (Google Search Console, etc.)
NS Record
Specifies which nameservers are authoritative for a domain.
example.com. 86400 IN NS ns1.cloudflare.com.
example.com. 86400 IN NS ns2.cloudflare.com.
SOA Record
Start of Authority — contains administrative information about the zone including the primary nameserver, responsible email, and serial number. Changes to the serial number signal secondary nameservers to refresh.
Understanding TTL and Propagation
TTL (Time to Live) is the number of seconds a resolver should cache a record before re-querying. A TTL of 300 means resolvers can cache the answer for 5 minutes.
"DNS propagation" is a misnomer. DNS doesn't push changes to all resolvers — resolvers pull fresh data when their cache expires. So when you change a DNS record:
- Resolvers holding a cached copy continue serving the old value for up to
old TTLseconds - After their cache expires, they fetch the new value
- The maximum propagation time equals the TTL that was in place before the change
This has a practical implication: lower your TTL before you make a change.
Standard operating TTL: 3600 (1 hour)
Pre-migration TTL: 300 (5 minutes) ← set this 24 hours before the change
Migration: change the A record
Post-migration: raise TTL back to 3600 after traffic confirms working
If you change your A record from IP-A to IP-B with a TTL of 86400 (24 hours), some users will continue hitting IP-A for up to 24 hours. Some resolvers (particularly mobile networks) don't honor TTL and cache aggressively for even longer.
Why different users see different results
After a DNS change, you may see your site correctly (because your resolver fetched the new record) while a colleague in another city still sees the old IP (their resolver's cache hasn't expired).
Tools like Google's 8.8.8.8 and Cloudflare's 1.1.1.1 tend to propagate changes faster than ISP resolvers. When testing, always query multiple resolvers.
Debugging DNS Without Installing Any Tools
Browser-based DNS lookup
DeveloperLab's Public DNS Lookup tool queries Cloudflare's DNS-over-HTTPS API (1.1.1.1/dns-query) directly from your browser — no dig, no nslookup, no terminal required.
Enter any hostname and select the record type. Results show the full answer section including all returned values and their TTLs.
DNS-over-HTTPS API (manual)
You can query 1.1.1.1 directly from any fetch-capable environment:
const hostname = 'api.example.com';
const type = 'A'; // A, AAAA, MX, TXT, NS, CNAME
const res = await fetch(
`https://cloudflare-dns.com/dns-query?name=${hostname}&type=${type}`,
{ headers: { Accept: 'application/dns-json' } }
);
const data = await res.json();
console.log(data.Answer); // array of records with TTL and data
For Google's resolver:
https://dns.google/resolve?name=api.example.com&type=A
Querying both Cloudflare and Google in parallel gives you a quick cross-resolver consistency check.
Command-line tools
When you do have terminal access:
# Query specific resolver (Cloudflare)
dig @1.1.1.1 api.example.com A
# Check propagation across multiple resolvers
dig @8.8.8.8 api.example.com A
dig @9.9.9.9 api.example.com A # Quad9
dig @208.67.222.222 api.example.com A # OpenDNS
# Trace the full resolution path
dig +trace api.example.com
# Check TTL remaining in your local cache (macOS)
sudo killall -INFO mDNSResponder # flush cache on macOS
Common DNS Failure Patterns
NXDOMAIN (Non-Existent Domain)
The record doesn't exist at the authoritative nameserver.
$ dig api.example.com A
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN
Common causes:
- Typo in the hostname you're querying
- Record deleted accidentally
- Querying a subdomain that was never configured
- Wrong zone (configuring
api.staging.example.combut queryingapi.example.com)
SERVFAIL
The authoritative nameserver is unavailable or misconfigured.
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL
Common causes:
- DNSSEC misconfiguration
- Authoritative nameserver unreachable
- Zone transfer failure on secondary nameservers
- TTL of 0 on SOA record causing excessive refresh load
Stale cache after migration
You changed the record but some users still hit the old server.
Diagnosis:
# Check TTL remaining (how long until this resolver refreshes)
dig api.example.com A +noall +answer
# example.com. 47 IN A 203.0.113.45
# ^^-- 47 seconds until this cache entry expires
Fix: Lower TTL before the next migration. For users already cached on the old IP, wait for their TTL to expire — you cannot force their resolver to flush.
CNAME flattening issues
You configured a CNAME on your apex domain and MX records stopped working.
Problem: DNS spec forbids CNAME coexistence with other record types. Some providers silently break MX, TXT, or NS records when a CNAME is present at the apex.
Fix: Use your provider's ALIAS/ANAME record type instead of CNAME for apex domains. Route 53, Cloudflare, DNSimple all support this. The alias is resolved server-side before being returned as an A record to the resolver.
Subdomain takeover
A CNAME points to an external service (S3 bucket, Heroku app, GitHub Pages) that no longer exists. An attacker can claim that resource and serve content from your subdomain.
# Check for dangling CNAMEs
dig legacy.example.com CNAME
# legacy.example.com CNAME old-app.herokuapp.com.
dig old-app.herokuapp.com A
# NXDOMAIN — nobody owns this anymore
Audit all CNAME records pointing to cloud providers and delete any that resolve to NXDOMAIN.
Email DNS (SPF, DKIM, DMARC)
Email deliverability depends entirely on three TXT records being correctly configured:
SPF (Sender Policy Framework)
Specifies which IP addresses and services are authorized to send mail for your domain.
v=spf1 include:_spf.google.com include:sendgrid.net ~all
include:— allow this service's IPs-all— fail anything else (strict)~all— softfail anything else (relaxed — for testing)- Never exceed 10 DNS lookups in a single SPF record.
include:directives each count as one lookup.
DKIM (DomainKeys Identified Mail)
A cryptographic signature attached to outgoing emails, verified against a public key in your DNS.
selector1._domainkey.example.com TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkqhk..."
The selector prefix is set by your email provider. You may have multiple selectors (one per provider).
DMARC (Domain-based Message Authentication)
Tells receiving servers what to do when SPF or DKIM fail, and where to send reports.
_dmarc.example.com TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com; pct=100"
p=none— monitor only (start here)p=quarantine— send to spam on failurep=reject— reject the email on failure
To debug email deliverability, check all three records:
dig example.com TXT # SPF
dig selector1._domainkey.example.com TXT # DKIM
dig _dmarc.example.com TXT # DMARC
Or use DeveloperLab's DNS Lookup tool — paste the hostname and select TXT to inspect all three without a terminal.
DNS Debugging Workflow
When something breaks that might be DNS:
1. Can you reach the IP directly?
curl -H "Host: api.example.com" http://203.0.113.45/health
→ If yes: DNS problem. If no: server problem.
2. What does your local resolver think?
dig api.example.com A
3. What does the authoritative nameserver think?
dig @$(dig example.com NS +short | head -1) api.example.com A
4. Are Cloudflare and Google resolvers consistent?
dig @1.1.1.1 api.example.com A
dig @8.8.8.8 api.example.com A
5. What TTL is cached at your resolver?
Check the TTL field in the dig output — that's how long until refresh.
6. Is the answer correct but the IP wrong?
→ Change the record and wait for TTL to expire.
7. Is there no answer at all (NXDOMAIN)?
→ Check the record exists in your DNS provider dashboard.
→ Check you're querying the right zone.
DNS problems are almost always in one of three places: the record doesn't exist, the record points to the wrong value, or caches haven't expired yet. Systematic queries to multiple resolvers let you distinguish between them quickly.