example.com works. You point blog.example.com to your blog host, wait 10 minutes, and it still gives DNS_PROBE_FINISHED_NXDOMAIN, This site can't be reached, or ERR_NAME_NOT_RESOLVED. The apex domain resolves; the subdomain doesn’t. In 95% of cases this is one of five issues at the DNS or host layer — not a propagation wait, not a browser problem. This article walks the diagnosis in the order they actually happen.
How to identify which case you’re in
Start with one query:
dig blog.example.com +short
dig blog.example.com @1.1.1.1 +short
nslookup blog.example.com 8.8.8.8
Compare the result to expectations:
- Empty / NXDOMAIN → no DNS record exists (Case 1 or Case 2)
- Returns an IP, but the page errors with
SSL_ERROR_NO_CYPHER_OVERLAPorERR_SSL_PROTOCOL_ERROR→ DNS works, host doesn’t recognize the subdomain (Case 3) - Returns the wrong IP → an old or wildcard record is in the way (Case 4)
- Returns the right IP but only from some resolvers → propagation in progress (Case 5)
Case 1: No DNS record exists for the subdomain
You added example.com (the apex) but never created a row for blog. DNS doesn’t auto-create subdomains.
How to spot it: dig blog.example.com +short returns nothing. The DNS provider’s UI shows no row for blog (or blog.example.com depending on display style).
Fix: at your authoritative DNS provider (find it with dig NS example.com +short), add either an A or a CNAME row:
- For static hosts (Vercel, Netlify, Cloudflare Pages, GitHub Pages):
CNAME blog → cname.vercel-dns.com.(or whatever the host shows in its custom-domain panel). - For a server IP:
A blog → 203.0.113.42. - Apex (
@) cannot be a CNAME, but subdomains can — use CNAME whenever the host gives you a hostname rather than an IP.
Case 2: Record exists but pointed at a wrong / dead target
dig returns a CNAME, but that CNAME chains to nothing, or to an IP that no longer hosts anything.
How to spot it:
dig blog.example.com +trace
# Watch the chain. If it ends with "no servers could be reached"
# or "NXDOMAIN", the final target died.
Fix: update the CNAME / A to the current target. If you migrated hosts, the old cname.heroku-app.com or similar is dead — replace with the new host’s value.
Case 3: DNS resolves but the host doesn’t recognize the subdomain
The classic Vercel / Netlify / Cloudflare Pages footgun. DNS points correctly at the host, but the host’s project hasn’t added blog.example.com to its allowed domains list, so its load balancer returns a generic 404 or an SSL handshake error.
How to spot it:
curl -vI https://blog.example.com 2>&1 | head -30
# Symptoms:
# - SSL cert is for *.vercel.app instead of your domain
# - HTTP/2 404 with body "The deployment could not be found on Vercel"
# - "Site not found" Netlify page
Fix: in the host dashboard’s project settings → Domains, add blog.example.com explicitly. The host will then provision an SSL cert and route traffic. This is a two-sided handshake: DNS at registrar + domain attached at host. Skipping either side breaks it.
Case 4: Wildcard or stale record shadowing the subdomain
A wildcard *.example.com CNAME → some-old-host matches blog.example.com before any more specific record. Or you have two rows for blog (e.g., A + CNAME, which DNS forbids; or two A rows with different IPs).
How to spot it:
dig blog.example.com +short
# Returns an unexpected IP that matches another project of yours
Then check the DNS panel for: a * row, a blog row created by an older user, or a blog row from a forgotten platform integration.
Fix: an explicit blog row always beats *. Delete duplicates, ensure exactly one row per record type for blog.
Case 5: It is actually just propagation lag
Less common than people assume. If the subdomain row was just added and the previous TTL on the negative response (NXDOMAIN cache) was high, some resolvers will keep saying NXDOMAIN for up to an hour.
How to spot it: works from dig @1.1.1.1 and @8.8.8.8 but not from your ISP’s resolver, or vice versa. whatsmydns.net shows green check marks in some regions but X in others.
Fix: wait. Flush your local cache:
# macOS
sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder
# Linux (systemd-resolved)
sudo resolvectl flush-caches
# Windows
ipconfig /flushdns
Hard-refresh in a new incognito window.
Shortest fix path
In hit-rate order:
dig NS example.com +short: find the authoritative DNS provider. Then add the subdomain there, not at the registrar (if those are different).- Add the subdomain in your host dashboard too: Vercel, Netlify, Cloudflare Pages, Render, Fly all require it.
- Wait 10 minutes, query
dig blog.example.com @1.1.1.1 +short, expect the host’s hostname or IP. - Check SSL provisioning in the host dashboard — usually 1–5 minutes after DNS validates.
- If still failing, look for a wildcard or duplicate row shadowing the subdomain.
Reference: subdomain values per host
| Host | DNS record at provider | Action in host dashboard |
|---|---|---|
| Vercel | CNAME blog → cname.vercel-dns.com. | Project → Settings → Domains → Add blog.example.com |
| Netlify | CNAME blog → your-site.netlify.app. | Site → Domain settings → Add custom domain |
| Cloudflare Pages | CNAME blog → your-project.pages.dev. | Pages → Custom domains → Set up |
| Firebase Hosting | A to two IPs shown in the Add Custom Domain wizard | Hosting → Add custom domain |
| GitHub Pages | CNAME blog → username.github.io. | Repo Settings → Pages → Custom domain |
| Render | CNAME blog → your-service.onrender.com. | Service → Settings → Custom Domain |
CNAME values are case-insensitive but should not have an extra leading https:// or trailing /. The DNS UI usually adds the trailing dot for you.
Prevention
- Use a CNAME, not an A, for any subdomain pointing at a managed host — when the host changes IPs (which they do, silently, for capacity), CNAME-followers keep working.
- Never deploy a wildcard CNAME
*.example.comunless you actually need it. Wildcards make adding new subdomains “just work” but also silently catch typos and shadow explicit rows. - Test new subdomains from a phone or
@1.1.1.1resolver immediately: your laptop’s DNS cache lies. - Document which host owns which subdomain in a team doc.
blog→ Ghost;app→ Vercel;status→ BetterUptime. Easier to debug 6 months later. - Keep TTL on subdomain CNAMEs around 3600 in steady state — long enough to be cache-friendly, short enough to switch within an hour.
FAQ
Q: I added the CNAME 5 minutes ago, why doesn’t it resolve yet?
A: New records propagate to public resolvers in seconds, but if you queried the subdomain before it existed, your local resolver cached an NXDOMAIN with its own TTL (usually 5–60 minutes). Wait it out, or query a fresh resolver: dig blog.example.com @1.0.0.1 +short.
Q: I get a Vercel/Netlify “Site not found” page when visiting the subdomain.
A: DNS works (you hit Vercel’s edge) but the project hasn’t claimed this hostname. Add blog.example.com in the project’s domain settings.
Q: Does the subdomain need its own SSL certificate? A: Yes, but every modern host provisions it automatically via Let’s Encrypt within minutes of DNS being valid. If SSL is stuck after an hour, see custom domain SSL delay.
Q: Can I CNAME the apex (example.com) the same way I CNAME a subdomain?
A: No. RFC 1034 forbids CNAME at the zone apex because it would conflict with SOA / NS. Use A records (or your DNS provider’s “CNAME flattening” / “ALIAS” feature if they offer one — Cloudflare and DNSimple do).
Q: My subdomain works but redirects me to the apex domain.
A: Either the host has a “force root domain” redirect setting, or your CMS / framework is doing Location: https://example.com server-side. Check the response with curl -I https://blog.example.com — a 301 to the apex means it’s a redirect, not a DNS issue.
Related articles
- DNS propagation confusion
- Wildcard DNS not matching subdomain
- A vs CNAME confusion
- Custom domain SSL delay
- Domain points to wrong hosting provider
Tags: #Troubleshooting #DNS #Debug #Subdomain