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.
Fastest fix: run dig blog.example.com @1.1.1.1 +short. If it returns nothing, you never created the DNS row (Case 1). If it returns an IP but the page errors, DNS works and you forgot to attach the subdomain inside your host’s dashboard (Case 3). Those two account for the large majority of these reports. This is almost never a propagation wait or a browser problem.
This article walks the five buckets in the order they actually happen.
How to identify which case you’re in
Start with one query. Ask a public resolver directly so your laptop’s cache can’t lie to you:
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:
dig result | What it means | Go to |
|---|---|---|
Empty / NXDOMAIN | No DNS record exists for the subdomain | Case 1 |
| Returns a CNAME that chains to nothing | Record points at a dead target | Case 2 |
Returns an IP, but the page errors (ERR_SSL_PROTOCOL_ERROR, 404, “not found”) | DNS works; host doesn’t recognize the subdomain | Case 3 |
| Returns the wrong IP | An old or wildcard record is shadowing it | Case 4 |
Right IP from @1.1.1.1 but not your ISP resolver | Propagation / negative-cache lag | 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 does not 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 — this is the host whose nameservers actually answer, which may differ from your registrar), add either an A or a CNAME row:
- For static hosts (Vercel, Netlify, Cloudflare Pages, GitHub Pages): a CNAME, e.g.
CNAME blog -> your-site.netlify.app.— use the exact target 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. See A vs CNAME confusion.
Case 2: Record exists but points 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 it with the new host’s value. After a host migration, also delete any AAAA (IPv6) rows the old platform created; some hosts (Render, for one) are IPv4-only and a stale AAAA row sends part of your traffic into the void.
Case 3: DNS resolves but the host doesn’t recognize the subdomain
The classic Vercel / Netlify / Cloudflare Pages footgun, and the single most common cause after Case 1. DNS points correctly at the host, but the host’s project hasn’t added blog.example.com to its domain list, so its edge returns a generic 404, a “Site not found” page, or an SSL handshake error using the wrong certificate.
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"
# - Netlify "Site not found" / "Not Found" page
# - Cloudflare Error 1000 ("DNS points to prohibited IP") or a 522,
# which on Pages means you added the CNAME before claiming the
# domain in the Pages dashboard
Fix: in the host dashboard, add blog.example.com explicitly, then let it provision the certificate:
- Vercel: Project -> Settings -> Domains -> Add. Note that as of June 2026 Vercel hands each project a unique CNAME target like
d1d4fc829fe7bc7c.vercel-dns-017.com.(the legacycname.vercel-dns.com.still works, but the dashboard shows the per-project value). Runvercel domains inspect example.comto print the exact records for your project. - Cloudflare Pages: Pages project -> Custom domains -> Set up. You must add the domain here first; pointing a CNAME at
your-project.pages.dev.without claiming it in the dashboard returns a 522. - Netlify / Render / Fly: add the custom domain in the project, then click Verify.
This is a two-sided handshake: the DNS row at your provider and the domain attached at the host. Skip either side and it breaks. Add the domain at the host before the certificate can be issued — the host validates ownership via the DNS you just set, so DNS has to be live first.
Case 4: Wildcard or stale record shadowing the subdomain
A wildcard *.example.com CNAME -> some-old-host matches blog.example.com when no more specific record exists. Or you have two conflicting 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 old teammate, or a blog row from a forgotten platform integration.
Fix: an explicit blog row always wins over *, so adding the correct row fixes it. Delete duplicates; keep exactly one row per record type for blog. See Wildcard DNS not matching subdomain.
Case 5: It is actually just propagation lag
Less common than people assume. If the blog row was just added and you had previously queried it while it did not exist, your resolver cached the NXDOMAIN with its own negative TTL (often the zone’s SOA minimum, commonly 5-60 minutes), so it keeps saying NXDOMAIN until that expires.
How to spot it: 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 checks in some regions and X in others.
Fix: wait out the negative TTL, and flush your local cache:
# macOS
sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder
# Linux (systemd-resolved)
sudo resolvectl flush-caches
# Windows
ipconfig /flushdns
Then hard-refresh in a new incognito window. See DNS propagation confusion.
Shortest fix path
In hit-rate order:
dig NS example.com +short— find the authoritative DNS provider. Add the subdomain there, not at the registrar, if those differ.- Add the subdomain in your host dashboard too — Vercel, Netlify, Cloudflare Pages, Render, and Fly all require it. This is the step people skip.
- Wait a few minutes, then
dig blog.example.com @1.1.1.1 +shortand confirm you see the host’s hostname or IP. - Check SSL provisioning in the host dashboard — usually 1-5 minutes after DNS validates, via Let’s Encrypt.
- If still failing, look for a wildcard or duplicate row shadowing the subdomain.
Reference: subdomain values per host
Values verified against vendor docs as of June 2026. Always copy the exact target your own dashboard shows — several hosts now mint per-project values.
| Host | DNS record at provider | Action in host dashboard |
|---|---|---|
| Vercel | CNAME blog -> <unique>.vercel-dns-017.com. (or legacy cname.vercel-dns.com.) | Project -> Settings -> Domains -> Add blog.example.com |
| Netlify | CNAME blog -> your-site.netlify.app. | Site -> Domain management -> Add a domain |
| Cloudflare Pages | CNAME blog -> your-project.pages.dev. | Pages -> Custom domains -> Set up (add domain first) |
| Firebase Hosting | A to the 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. (remove any AAAA) | Service -> Settings -> Custom Domains -> Verify |
CNAME values are case-insensitive but must not carry a leading https:// or a trailing /. The trailing dot makes the name fully qualified; most DNS UIs add it for you. On GitHub Pages, note that the repo’s CNAME file (uppercase, one domain only) must also survive your build — many static-site generators overwrite it, which silently breaks the custom domain on the next deploy.
How to confirm it’s fixed
dig blog.example.com @1.1.1.1 +shortreturns the host’s hostname or IP (not empty, not the wrong IP).curl -sI https://blog.example.com | head -1returnsHTTP/2 200(or your intended redirect), not a 404 / 522 / 1000.- The TLS certificate matches your domain:
echo | openssl s_client -connect blog.example.com:443 -servername blog.example.com 2>/dev/null | openssl x509 -noout -subjectshowsCN = blog.example.com(or a wildcard for your zone), not*.vercel.appor similar. - In your host dashboard, the domain shows a green “Valid configuration” / “Active” state with SSL issued.
Prevention
- Use a CNAME, not an A, for any subdomain pointing at a managed host. When the host rotates IPs (which they do, silently, for capacity), CNAME-followers keep working.
- Don’t deploy a wildcard CNAME
*.example.comunless you actually need it. Wildcards make new subdomains “just work” but also catch typos and shadow explicit rows. - Test new subdomains from a phone or
@1.1.1.1immediately. Your laptop’s DNS cache lies. - Document which host owns which subdomain in a team doc:
blog-> Ghost,app-> Vercel,status-> a status provider. Much faster to debug six months later. - Keep TTL on subdomain CNAMEs around 3600 in steady state — cache-friendly, but switchable within an hour.
FAQ
Q: I added the CNAME 5 minutes ago, why doesn’t it resolve yet?
A: New records reach public resolvers in seconds. But if you queried the subdomain before it existed, your resolver cached an NXDOMAIN with its own negative TTL (often 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” or a Cloudflare 522 page on the subdomain.
A: DNS works (you reached the host’s edge) but the project hasn’t claimed this hostname. Add blog.example.com in the project’s domain settings. On Cloudflare Pages specifically, a 522 means you added the CNAME before claiming the domain under Custom domains.
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, check for a blocking CAA record on the zone, then 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 the SOA / NS records. Use A records, or your DNS provider’s “CNAME flattening” / “ALIAS” / “ANAME” 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 sending Location: https://example.com server-side. Check with curl -I https://blog.example.com — a 301 to the apex means it’s a redirect, not a DNS issue.
Q: Vercel/GitHub says my DNS is wrong but dig looks right.
A: The host validates against the exact target it expects. Vercel now issues per-project CNAME values, so a generic cname.vercel-dns.com. may not match what the dashboard wants — run vercel domains inspect example.com and copy that value. GitHub’s “DNS check unsuccessful” most often means the CNAME points at the wrong username.github.io or the repo’s uppercase CNAME file got overwritten by your build.
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