You created a public zone in Azure DNS, added the records, and the portal looks correct. But www.contoso.com still returns NXDOMAIN, SERVFAIL or the IP of the old hosting provider. In almost every case the zone itself is fine and the problem is in the chain that leads resolvers to it.
The fix order is always the same: prove Azure answers correctly, then prove the parent sends resolvers to Azure, then deal with caching.
Quick map: symptom, cause, fix
| Symptom | Likely cause | Fix |
|---|---|---|
NXDOMAIN or old answer from public resolvers, correct answer from ns1-0x.azure-dns.com | Registrar still points at the old name servers | Update NS at the registrar to the four Azure name servers |
| Correct answer only from some resolvers | Old NS records cached (TTL up to 48 hours at the TLD) | Wait out the TTL; don't change anything else |
SERVFAIL from validating resolvers, dig +cd works | DS record at the registrar doesn't match the zone's DNSSEC keys | Remove or correct the DS record |
Subdomain (dev.contoso.com) NXDOMAIN, parent works | Child zone created but no NS record set in the parent zone | Add the NS delegation in the parent |
| Record missing even when asking Azure directly | Record created in the wrong zone, wrong name, or in a private zone | Check az network dns record-set list on the right zone |
Apex (contoso.com) can't point at Front Door/App Service | CNAME not allowed at the apex | Use an alias A record or the service's apex method |
| Changes in Azure not visible for hours | Old record TTL still cached by resolvers | Lower TTL before changes next time |
Step 1: Ask Azure directly
Get the name servers assigned to your zone. Each zone gets four, and they differ between zones, so never copy them from another zone or from a document.
az network dns zone show -g rg-dns -n contoso.com --query nameServers -o tsv
# ns1-03.azure-dns.com.
# ns2-03.azure-dns.net.
# ns3-03.azure-dns.org.
# ns4-03.azure-dns.info.
Query one of them directly. This bypasses delegation and caching completely:
dig @ns1-03.azure-dns.com www.contoso.com A +norecurse
Resolve-DnsName www.contoso.com -Type A -Server ns1-03.azure-dns.com -DnsOnly
- Correct answer: the zone is fine. Go to Step 2.
- NXDOMAIN or wrong answer: the problem is in the zone. List what is actually there:
az network dns record-set list -g rg-dns -z contoso.com \
--query "[].{name:name, type:type, ttl:ttl, fqdn:fqdn}" -o table
Common mistakes here: the record name includes the zone (www.contoso.com in a zone contoso.com creates www.contoso.com.contoso.com), the record is in a private DNS zone with the same name (az network private-dns ...), or there are two public zones with the same name in different subscriptions and you edited the one that isn't delegated.
Tip
Duplicate public zones are allowed in Azure, each with its own name server set. If Step 1 works but the registrar's name servers don't match
nameServersfrom this zone, you are probably looking at the wrong copy.
Step 2: Check the delegation from the parent
Ask the TLD servers which name servers they hand out for your domain. dig +trace walks the chain from the root:
dig +trace www.contoso.com A
Or ask the TLD directly:
dig NS com. +short # pick one TLD server
dig @a.gtld-servers.net contoso.com NS +norecurse
Resolve-DnsName contoso.com -Type NS -Server a.gtld-servers.net -DnsOnly
Compare the result with the four Azure name servers from Step 1.
| What the TLD returns | Meaning |
|---|---|
| The four Azure servers for this zone | Delegation is correct. Go to Step 3 |
| The old provider's servers | Registrar not updated. Change NS at the registrar |
Azure servers, but a different number (ns1-01 instead of ns1-03) | Pointed at another zone's name servers. Fix at the registrar |
| A mix of old and Azure servers | Partial update. Resolvers will get different answers at random. Set all four Azure servers and remove the rest |
The change is made at the registrar (the company you bought the domain from), not in Azure. Enter all four Azure name servers. Some registrar portals reject the trailing dot; leave it off if so.
Child zones (subdomain delegation)
If dev.contoso.com is a separate Azure zone, the parent zone contoso.com needs an NS record set named dev pointing at the child's name servers.
# Name servers of the child zone
az network dns zone show -g rg-dns -n dev.contoso.com --query nameServers -o tsv
# Create the delegation in the parent
for ns in $(az network dns zone show -g rg-dns -n dev.contoso.com --query "nameServers[]" -o tsv); do
az network dns record-set ns add-record -g rg-dns -z contoso.com -n dev -d "$ns"
done
# Check it
az network dns record-set ns show -g rg-dns -z contoso.com -n dev -o json
If both zones are in the same subscription, az network dns zone create ... --parent-name contoso.com creates the delegation for you when you create the child.
Step 3: Check DNSSEC
A validating resolver returns SERVFAIL when the DS record at the registrar doesn't match the DNSKEY the zone is signed with. This often happens after a migration: the old provider's DS record was left at the registrar, and Azure is either not signing the zone or signing it with different keys.
dig contoso.com DS +short # DS at the parent
dig @ns1-03.azure-dns.com contoso.com DNSKEY +short
dig www.contoso.com A # SERVFAIL?
dig www.contoso.com A +cd # works with checking disabled -> DNSSEC problem
If +cd works and the normal query fails, it is DNSSEC. Either remove the DS record at the registrar or, if you've enabled DNSSEC signing on the Azure zone, publish the DS values Azure gives you for that zone (shown on the zone's DNSSEC page in the portal).
Caution
When moving a signed domain to Azure DNS, remove the old DS record and wait for its TTL to expire before changing NS. Changing NS with a stale DS in place makes the whole domain fail for every validating resolver, which includes most large public resolvers.
Step 4: Deal with caching
If the delegation is right and DNSSEC is clean, different resolvers may still give different answers for a while.
dig @8.8.8.8 www.contoso.com A # note the TTL in the answer section
dig @1.1.1.1 www.contoso.com A
The TTL in the answer counts down to when that resolver will ask again. TLD NS records for .com are commonly cached for up to two days, so a delegation change can take that long to reach every resolver. Some public resolvers offer a cache flush page; your own corporate DNS servers can be flushed directly:
Clear-DnsServerCache -ComputerName dns01 -Force # Windows DNS Server
Clear-DnsClientCache # local client
Step 5: Apex records for Azure services
You cannot put a CNAME at the zone apex. For an Azure resource with a public IP, Traffic Manager or Front Door, use an alias record so the record follows the resource:
az network dns record-set a create -g rg-dns -z contoso.com -n "@" \
--target-resource /subscriptions/<sub>/resourceGroups/rg-web/providers/Microsoft.Network/publicIPAddresses/pip-web
Check the target service's documentation for apex support; App Service, for example, uses an A record plus a TXT validation record instead.
Prevent it next time
- Lower TTLs two days before a cutover. Set record TTLs to 300 seconds ahead of a migration, then raise them after.
- Copy name servers from the zone, not from memory. Script it with
az network dns zone show --query nameServers. - One public zone per domain. Use Azure Policy or a Resource Graph query to find duplicates:
resources
| where type =~ 'microsoft.network/dnszones'
| summarize zones = count(), subs = make_set(subscriptionId) by name
| where zones > 1
- Protect the zone. Put a
CanNotDeletelock on production DNS zones and limit write access with the DNS Zone Contributor role. - Monitor resolution from outside. An Application Insights availability test or a simple external probe catches a broken delegation within minutes, not when customers call.