Why a cached answer can mislead you

When a DNS update doesn't take effect, you typically have two possibilities: your authoritative nameserver holds the wrong record, or an old record is still being served from a cache and you simply need to wait for it to expire. To distinguish between those two cases, you need to query the authoritative nameserver directly and check what it actually has.

Finding that authoritative nameserver sounds straightforward, but a lot of the common advice floating around can hand you a stale or misleading answer. The reliable method is to walk the delegation chain from the root down, which is exactly what happens behind the scenes when any resolver processes your query.

The fast route: dig +short ns

If you haven't touched your authoritative DNS configuration in roughly a week, the quickest check is dig +short ns DOMAIN.

$ dig +short ns jvns.ca
art.ns.cloudflare.com.
roxy.ns.cloudflare.com.

That gives the right result here, and for most settled domains it will be fine. The danger appears right after you change nameservers — the response can be a cached record from before the change, so you can't trust it during a migration.

The reliable two-step walk

To get an answer that is always current, start at a root server and follow the referrals. We'll use jvns.ca as the example domain.

First, query one of the 13 root nameservers — say, h.root-servers.net — using dig @h.root-servers.net jvns.ca.

$ dig @h.root-servers.net jvns.ca
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 42165
;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 4, ADDITIONAL: 9
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;jvns.ca.			IN	A

;; AUTHORITY SECTION: <------------ this is the section we're interested in
ca.			172800	IN	NS	c.ca-servers.ca. <------- we'll use this record
ca.			172800	IN	NS	j.ca-servers.ca.
ca.			172800	IN	NS	x.ca-servers.ca.
ca.			172800	IN	NS	any.ca-servers.ca.

;; ADDITIONAL SECTION:
c.ca-servers.ca.	172800	IN	A	185.159.196.2
j.ca-servers.ca.	172800	IN	A	198.182.167.1
x.ca-servers.ca.	172800	IN	A	199.253.250.68
any.ca-servers.ca.	172800	IN	A	199.4.144.2
c.ca-servers.ca.	172800	IN	AAAA	2620:10a:8053::2
j.ca-servers.ca.	172800	IN	AAAA	2001:500:83::1
x.ca-servers.ca.	172800	IN	AAAA	2620:10a:80ba::68
any.ca-servers.ca.	172800	IN	AAAA	2001:500:a7::2

;; Query time: 96 msec
;; SERVER: 198.97.190.53#53(198.97.190.53)
;; WHEN: Tue Jan 11 08:30:57 EST 2022
;; MSG SIZE  rcvd: 289

The relevant output lives in the AUTHORITY SECTION:

ca.			172800	IN	NS	c.ca-servers.ca.

Any server listed there works; for this domain, that means the next hop is c.ca-servers.ca.. If you're querying a well-known TLD often enough, you can skip the root query and go straight to the TLD's nameservers, but starting at the root is the general approach.

Next, query that server with dig @c.ca-servers.ca jvns.ca.

$ dig @c.ca-servers.ca jvns.ca
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 24920
;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 2, ADDITIONAL: 1
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;jvns.ca.			IN	A

;; AUTHORITY SECTION: <------------ this is the section we're interested in
jvns.ca.		86400	IN	NS	art.ns.cloudflare.com. <---- we'll use this record
jvns.ca.		86400	IN	NS	roxy.ns.cloudflare.com.

;; Query time: 26 msec
;; SERVER: 185.159.196.2#53(185.159.196.2)
;; WHEN: Tue Jan 11 08:32:44 EST 2022
;; MSG SIZE  rcvd: 90

Again, look at the AUTHORITY SECTION:

jvns.ca.		86400	IN	NS	art.ns.cloudflare.com.

The result here is art.ns.cloudflare.com., which is the actual authoritative nameserver for jvns.ca. You can now query it directly to see the records it holds:

$ dig @art.ns.cloudflare.com. jvns.ca
jvns.ca.		292	IN	A	172.64.80.1

This works reliably because it uses exactly the same source of truth that a real recursive resolver consults — the TLD nameserver that got the referral from the root. There is no cache in the path to inject an old record.

This method is particularly handy during a nameserver change at your registrar. Registrars don't propagate that change instantly; it can take roughly an hour. Walking the delegation chain this way shows you whether the registrar has actually published the new nameservers yet.

Other tools and why they can be wrong

A few other commands often get recommended for this job. Each has a caveat:

  • dig +trace jvns.ca performs the same root-to-TLD walk internally, so it's always accurate. The output is just noisier and harder to parse at a glance.

  • dig ns jvns.ca usually works, but has two failure modes. The first is a cached response. The second is subtler: the answer comes from the authoritative nameserver itself (here, art.ns.cloudflare.com) rather than from the TLD server that issued the delegation. Normally those match, but in edge cases they may not.

  • dig soa jvns.ca exposes nameservers inside the SOA record.

$ dig SOA jvns.ca
jvns.ca.   3600    IN    SOA    art.ns.cloudflare.com. dns.cloudflare.com. 2267173366 10000 2400 604800 3600
                                ^^^^^^^^^^^^^^^^^^^^^
                                    here it is
  • whois jvns.ca returns registry data, which can be cached and lag behind actual changes. On this domain it happens to be current:
$ whois jvns.ca | grep 'Name Server'
Name Server: art.ns.cloudflare.com
Name Server: roxy.ns.cloudflare.com

The SOA and whois approaches share the same fundamental weakness: they're one step removed from the delegation data held by the TLD server. If you're mid-migration, your resolver might still route to the old authoritative server, or the record you're reading simply hasn't been updated yet. Walking the chain from the root avoids both problems by always asking the server that owns the current delegation.