How to check dns cache
ipconfig /displaydns lists what the Windows resolver holds and ipconfig /flushdns empties it. Read one entry with Get-DnsClientCache -Entry example.com and watch its TimeToLive count down. A browser keeps a separate cache, so flushing the Windows one does not clear the name Chrome is using.
Why check this
Run this when a tester reports that a change has not reached them and everyone else sees it. The machine in front of you is a cache like any other, and it is the one cache your test client actually uses. The failure it prevents is an hour spent re-checking a zone that is already correct, because the browser on the test machine is still using an address it resolved before the change.
The check also tells you which of your tools touch that cache. A command that names a resolver bypasses it entirely, so the reading it gives you says nothing about what the application on the same machine will do.
Prerequisites
- Windows.
ipconfigand theDnsClientmodule are both present by default. On macOS the equivalent flush issudo dscacheutil -flushcache, and on systemd-resolved it isresolvectl flush-caches. - A shell that passes the flag unchanged. In Git Bash,
/displaydnsis rewritten as a Windows path and the command fails. - Get-DnsClientCache documentation for the field names used below.
Steps
- Step 1.
Empty the cache and count what is left.
(Get-DnsClientCache).Count; ipconfig /flushdns; (Get-DnsClientCache).Count36 Windows IP Configuration Successfully flushed the DNS Resolver Cache. 12Thirty-six entries went down to twelve, not to zero. The twelve come from the hosts file, which the resolver loads into the cache and reloads after every flush.
Get-DnsClientCache -Entry 1.0.0.127.in-addr.arpashows one of them withTimeToLive 300727, about three and a half days. - Step 2.
Put one name in the cache through the operating system resolver, then read it twice.
ipconfig /flushdns | Out-Null; Resolve-DnsName example.com -Type A | Out-Null; Get-DnsClientCache -Entry example.com | Format-Table Entry,Type,TimeToLive,Data -AutoSizeEntry Type TimeToLive Data ----- ---- ---------- ---- example.com 1 112 172.66.147.243 example.com 1 112 104.20.23.154 Entry Type TimeToLive Data ----- ---- ---------- ---- example.com 1 92 172.66.147.243 example.com 1 92 104.20.23.154The second table is the same read twenty seconds later. 112 to 92, one second per second. A local cache counts down cleanly, which a large public resolver does not. The record was fetched through an upstream resolver that was already 188 seconds into its own copy, which is why the number starts below the 300 the zone publishes.
- Step 3.
Find out which of your commands write to that cache.
$n = { (Get-DnsClientCache | Where-Object Entry -eq 'example.com').Count } ipconfig /flushdns | Out-Null; "after flush: " + (& $n) nslookup -type=A example.com 8.8.8.8 | Out-Null; "after nslookup -> 8.8.8.8: " + (& $n) Resolve-DnsName example.com -Type A -Server 8.8.8.8 | Out-Null; "after Resolve-DnsName -Server 8.8.8.8: " + (& $n) Resolve-DnsName example.com -Type A | Out-Null; "after Resolve-DnsName with no -Server: " + (& $n)after flush: 0 Non-authoritative answer: after nslookup -> 8.8.8.8: 0 after Resolve-DnsName -Server 8.8.8.8: 0 after Resolve-DnsName with no -Server: 4Naming a resolver sends the query straight to it and leaves the local cache untouched. Only the third lookup, the one with no
-Server, went through the resolver the applications use. - Step 4.
Load a page in a browser and look for it in the same cache. This run uses the shared Chrome session helper.
import { execSync } from 'node:child_process'; import { open } from '../scripts/browser/session.mjs'; const NAME = 'example.com'; const ps = (cmd) => execSync(`powershell -NoProfile -Command "${cmd}"`, { encoding: 'utf8' }).trim(); const cache = (label) => { const out = ps(`Get-DnsClientCache | Where-Object Entry -eq '${NAME}' | Format-Table Entry,Type,TimeToLive -AutoSize | Out-String`); console.log(`${label.padEnd(34)} ${out === '' ? '(no entry)' : '\n' + out}`); }; ps('ipconfig /flushdns'); cache('after flushdns'); const s = await open(); try { console.log('chrome version'.padEnd(34) + (await s.browser.version())); await s.goto(`https://${NAME}/`); console.log('page loaded'.padEnd(34) + JSON.stringify(s.requests[0])); cache('after the browser loaded it'); } finally { await s.close(); } ps(`Resolve-DnsName ${NAME} -Type A | Out-Null`); cache('after an OS-resolver lookup');after flushdns (no entry) chrome version Chrome/152.0.7977.76 page loaded {"url":"https://example.com/","status":200,"type":"document"} after the browser loaded it (no entry) after an OS-resolver lookup Entry Type TimeToLive ----- ---- ---------- example.com 1 223 example.com 1 223Chrome fetched the page and returned 200, so it resolved the name. The Windows cache stayed empty through that, and filled only when PowerShell asked. The browser holds its own answer somewhere
ipconfigcannot see or clear.
How to read the result
| What you see | What it means | What to do |
| --- | --- | --- |
| TimeToLive falling one per second | A live countdown on the local copy | Wait it out, or flush |
| An entry with a TimeToLive in the hundreds of thousands | Loaded from the hosts file, not from DNS | Edit the hosts file. A flush reloads it |
| Status is not 0 | A cached negative answer, held under the SOA minimum | Flush, or wait out that minimum |
| Nothing appears after a lookup | The command named its own resolver and skipped the cache | Repeat without -Server to test what applications see |
| The cache is empty and the browser still shows the old host | The browser resolved it itself and kept the answer | Clear the browser's own host cache, or restart it |
| The cached address differs from what the zone returns | This machine is mid-TTL on an old answer | Flush, then re-read |
Common mistakes
What to check next
- How to check dns ttl: the number this countdown started from.
- How to check dns propagation: the same question asked of caches you do not control.
- How to check a record of domain: the record the cached answer belongs to.
- How to check DNS lookup time: what a cache miss costs once the entry expires.
- How to check dns records: every type at the name, when more than the address moved.
FAQ
How to check dns cache in windows?
ipconfig /displaydns prints every entry with its record type, remaining TTL and section. Get-DnsClientCache returns the same data as objects, so Get-DnsClientCache -Entry example.com narrows it to one name without paging through the rest.
Does flushing the DNS cache fix a stale page?
Only if the stale answer is in that cache. Step 4 shows a browser resolving a name without touching it. Clear the browser's host cache as well, and remember that an open connection is reused regardless of what either cache holds.
Why is a record still cached after its TTL looks expired?
Check whether it came from the hosts file. Those entries carry a TTL of about three and a half days and are reloaded after every flush, so they never age out the way a DNS answer does.
Can I see the cache on macOS or Linux?
Not as a list on macOS: sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder clears it without printing it. With systemd-resolved, resolvectl statistics reports cache hits and resolvectl flush-caches empties it.
Verified
Verified by Maks VernyPowerShell 5.1node 22.23.2Chrome 152.0.7977.76
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
basic6 minpublished updated Maks Verny