How to check dns propagation
Nothing propagates. Each resolver holds its own copy until the TTL runs out, then fetches again. Ask several resolvers the same question and compare the answers. Agreement on the data with different TTLs is normal. Disagreement on the data is either a cache still counting down or a server that answers differently by design.
Checker offline. Follow the manual steps below, they give the same answer.
Why check this
Run this after every zone edit that a release depends on: a verification token a provider is waiting for, an A record moved to a new load balancer, an MX pointed at a new mail platform. The failure it prevents is the support ticket that says the change did not work, filed twenty minutes after an edit with a one-hour TTL, followed by a second edit that makes the state harder to reason about.
The word propagation describes a push that does not exist. Your provider publishes the change on its own nameservers at once. Every other resolver keeps serving its cached copy until that copy expires, and no two of them expire at the same moment. What you are checking is how many caches have refreshed yet.
Prerequisites
- The resolver addresses you want to compare.
8.8.8.8is Google,1.1.1.1is Cloudflare,9.9.9.9is Quad9,208.67.222.222is OpenDNS. - An authoritative nameserver for the zone, from
nslookup -type=NS example.com 8.8.8.8. This is the only server that reports the new value the moment it exists. - Node 22 for steps 2 to 4. Each query sets its own server, so the operating system resolver is never involved.
Steps
- Step 1.
Ask one named resolver, rather than whatever the machine is configured with.
nslookup -type=A example.com 1.1.1.1Non-authoritative answer: Server: one.one.one.one Address: 1.1.1.1 Name: example.com Addresses: 104.20.23.154 172.66.147.243Non-authoritative answermeans this is a cached copy. Every recursive resolver labels its answers that way, including the correct ones. - Step 2.
Ask four resolvers the same question in one run. Save this as
spread.jsand runnode spread.js example.com A.const { Resolver } = require('node:dns'); const [name, type = 'A'] = process.argv.slice(2); const PUBLIC = [['8.8.8.8', 'Google'], ['1.1.1.1', 'Cloudflare'], ['9.9.9.9', 'Quad9'], ['208.67.222.222', 'OpenDNS']]; const boot = new Resolver(); boot.setServers(['8.8.8.8']); const ask = (ip, label) => new Promise((d) => { const r = new Resolver({ timeout: 5000, tries: 2 }); r.setServers([ip]); const done = (e, v) => { const text = e ? e.code : type === 'A' ? v.map((x) => `${x.address} ttl=${x.ttl}`).sort().join(' ') : JSON.stringify(v.flat().sort()); console.log(label.padEnd(20) + ip.padEnd(17) + text.slice(0, 80)); d(); }; if (type === 'A') r.resolve4(name, { ttl: true }, done); else r.resolve(name, type, done); }); (async () => { const zone = name.split('.').slice(-2).join('.'); for (const [ip, label] of PUBLIC) await ask(ip, 'cache ' + label); const ns = await new Promise((d) => boot.resolveNs(zone, (e, v) => d(e ? [] : v.sort()))); for (const n of ns) { const ip = await new Promise((d) => boot.resolve4(n, (e, a) => d(e ? null : a[0]))); if (ip) await ask(ip, 'authoritative'); } })();cache Google 8.8.8.8 104.20.23.154 ttl=300 172.66.147.243 ttl=300 cache Cloudflare 1.1.1.1 104.20.23.154 ttl=177 172.66.147.243 ttl=177 cache Quad9 9.9.9.9 104.20.23.154 ttl=300 172.66.147.243 ttl=300 cache OpenDNS 208.67.222.222 104.20.23.154 ttl=214 172.66.147.243 ttl=214 authoritative 108.162.195.228 104.20.23.154 ttl=300 172.66.147.243 ttl=300 authoritative 173.245.58.162 104.20.23.154 ttl=300 172.66.147.243 ttl=300Six servers, the same two addresses, four different TTL remainders in the caches and the configured 300 on both authoritative rows. The data has converged. The countdowns have not, and never will, because each cache started its own at a different moment.
- Step 3.
Run the same comparison on the record type a provider is usually waiting for, and include the zone's own nameservers.
node spread.js example.com TXTcache Google 8.8.8.8 ["_k2n1y4vw3qtb4skdx9e7dxt97qrmmq9","v=spf1 -all"] cache Cloudflare 1.1.1.1 ["_k2n1y4vw3qtb4skdx9e7dxt97qrmmq9","v=spf1 -all"] cache Quad9 9.9.9.9 ["_k2n1y4vw3qtb4skdx9e7dxt97qrmmq9","v=spf1 -all"] cache OpenDNS 208.67.222.222 ["_k2n1y4vw3qtb4skdx9e7dxt97qrmmq9","v=spf1 -all"] authoritative 172.64.35.228 ["_k2n1y4vw3qtb4skdx9e7dxt97qrmmq9","v=spf1 -all"] authoritative 108.162.192.162 ["_k2n1y4vw3qtb4skdx9e7dxt97qrmmq9","v=spf1 -all"]Six rows, one value. When the caches match the authoritative rows, the change is everywhere it is going to be. This is the state a verification button is waiting for.
- Step 4.
Read a name where the resolvers disagree on the data, and run it twice.
node spread.js aspmx.l.google.com A### run 1 cache Google 8.8.8.8 142.251.127.27 ttl=293 cache Cloudflare 1.1.1.1 142.250.147.26 ttl=20 cache Quad9 9.9.9.9 142.251.127.26 ttl=144 cache OpenDNS 208.67.222.222 142.251.127.26 ttl=293 authoritative 216.239.32.10 173.194.220.27 ttl=293 authoritative 216.239.34.10 74.125.131.27 ttl=293 authoritative 216.239.36.10 209.85.233.27 ttl=293 authoritative 216.239.38.10 74.125.205.27 ttl=293 ### run 2 cache Google 8.8.8.8 142.251.127.27 ttl=293 cache Cloudflare 1.1.1.1 142.251.9.26 ttl=173 cache Quad9 9.9.9.9 142.251.127.26 ttl=144 cache OpenDNS 208.67.222.222 142.251.127.26 ttl=293 authoritative 216.239.32.10 64.233.163.27 ttl=293 authoritative 216.239.34.10 64.233.162.26 ttl=293 authoritative 216.239.36.10 142.250.150.27 ttl=293 authoritative 216.239.38.10 64.233.165.27 ttl=293Eight servers and eight different addresses, twice, with nothing edited in between. The four authoritative rows disagree with each other and return a fresh set on the second run, so the zone is answering per query. Cloudflare's cached answer changed too, because its copy expired and it asked again. A difference like this never converges, and waiting for it to is a wasted afternoon.
How to read the result
| What you see | What it means | What to do |
| --- | --- | --- |
| Same data everywhere, different TTLs | Converged. The countdowns are supposed to differ | Nothing. The change is live |
| Some resolvers on the old value, the rest on the new | Caches partway through the old TTL | Wait out the old TTL from the moment of the edit |
| Every cache on the old value, the nameserver on the new | The edit is published and nothing has expired yet | Wait. A second edit restarts nothing and helps nothing |
| The nameserver still returns the old value | The edit was not applied, or was applied to a different zone | Check the zone at the provider, and the delegation |
| Answers differ every run, including from one resolver | The zone answers per client by design | Stop comparing. Verify the behaviour, not the address |
| One resolver returns ENODATA and the rest return data | A negative answer is cached under the SOA minimum | Wait out the SOA minimum, which is often longer than the record TTL |
Common mistakes
What to check next
- How to check dns ttl: the number that decides how long any of this takes.
- How to check dns cache: the copy on the machine you are testing from, which is a cache too.
- How to check ns record: where to find the authoritative servers step 3 compares against.
- How to check txt record of a domain: the type most often waited on, and how its strings are joined.
- How to check dns records: the full inventory when more than one record moved.
FAQ
How to check if dns has propagated?
Query the zone's authoritative nameservers and several public resolvers for the same name and type, as in step 3. When every cache returns what the nameservers return, there is nothing left to wait for. Differing TTLs between caches are expected and mean nothing.
Nslookup with specific dns server, what is the syntax?
The resolver goes last: nslookup -type=TXT example.com 9.9.9.9. Without it, nslookup uses the resolver the operating system supplies, which during a change is the least informative one you can ask.
How to check if txt record has propagated?
Run step 3 with the exact name the provider gave you, underscore label included. Read the strings as a set. A record printed as several quoted strings is one record, and the value is those strings joined end to end.
How long does a DNS change take?
As long as the old TTL, counted from the moment a resolver last fetched the record, which can be up to the old TTL before your edit. Plan for the old TTL plus your provider's publish delay, and lower the TTL one full old TTL in advance.
Why do two checking websites disagree?
They query from different places through different caches. Both can be right at the same second. Compare the caches you care about against the authoritative servers instead of comparing two third parties against each other.
Verified
Verified by Maks Vernynslookup Windows 11 build 22631node 22.23.2
Each output block is what the command above it printed on that date, on the host named in the step. Figures read from a live site move between runs. Compare the shape of the answer rather than the digits, and see the methodology for how a page is re-verified.
Related on this site
- Checker: dns-records A, AAAA, CNAME, MX, NS, TXT with TTL from two public resolvers, mismatch between resolvers
- DNS migration checklist
- All email and dns checks
intermediate8 minpublished updated Maks Verny