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

Steps

  1. Step 1.

    Empty the cache and count what is left.

    (Get-DnsClientCache).Count; ipconfig /flushdns; (Get-DnsClientCache).Count
    
    36
    
    Windows IP Configuration
    
    Successfully flushed the DNS Resolver Cache.
    12

    Thirty-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.arpa shows one of them with TimeToLive 300727, about three and a half days.

  2. 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 -AutoSize
    
    Entry       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.154

    The 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.

  3. 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: 4

    Naming 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.

  4. 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        223

    Chrome 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 ipconfig cannot 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

Sign: ipconfig /flushdns runs, the page still loads from the old server, and the zone gets blamed.Cause: Step 4 loaded https://example.com/ in Chrome 152 after a flush and the Windows cache remained empty, then filled the moment PowerShell resolved the same name. The browser is not reading that cache, so emptying it changes nothing the browser does. Clear the browser's own host cache, or restart the browser.
Sign: A change is confirmed with nslookup on the test machine and the application under test still uses the old address.Cause: Step 3 shows two lookups against 8.8.8.8 leaving zero entries in the local cache. Naming a resolver bypasses the machine's resolver completely. To see what the application sees, resolve with no server argument and then read the cache.
Sign: The cache is flushed and ipconfig /displaydns still lists a dozen records.Cause: Entries derived from the hosts file are reloaded immediately after every flush, with a TimeToLive of about three and a half days. Step 1 went from 36 entries to 12, not to 0. Those twelve are not stale DNS answers and no flush will remove them.
Sign: In Git Bash, ipconfig /displaydns prints `unrecognized or incomplete command line` and exits 1.Cause: The MSYS layer rewrites an argument that starts with a slash into a Windows path, so ipconfig receives a directory name instead of a flag. Write `ipconfig //displaydns`, or run the command from PowerShell or cmd.

What to check next

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.

basic6 minpublished updated Maks Verny