What is leaking, and to whom
Every time you open a site your device asks a name server to turn the domain into an IP address. That query is separate from the traffic it precedes. When the VPN is working, the query goes through the tunnel to the provider’s resolver and nobody outside sees it. When DNS leaks, the query goes to your internet provider’s resolver instead, over the ordinary connection, from your real address.
The result is that your ISP has a timestamped list of every domain you visited, which is precisely the record a VPN is bought to prevent — and in the UK and Australia the record it is legally required to keep.
Confirming it
A web page cannot see which resolver answered its queries, so any browser-based “DNS leak test” is inferring rather than measuring. The leak test on this site says so plainly and gives you the command instead. With the VPN connected:
nslookup -type=txt whoami.cloudflare 1.1.1.1
The address in the reply is the resolver that made the request on your behalf. If it belongs to your ISP, DNS is leaking. If it belongs to your VPN provider or a datacentre range, it is not.
On macOS or Linux you can also check what the system thinks it should be using:
scutil --dns | grep nameserver # macOS
resolvectl status | grep 'DNS Servers' # Linux with systemd
Get-DnsClientServerAddress # Windows PowerShell
Fixing it on Windows
Windows causes most DNS leaks, because Smart Multi-Homed Name Resolution deliberately sends each query out of every adapter at once and takes the first answer. Two fixes, and doing both is reasonable.
In the VPN app: turn on “DNS leak protection”, “Use VPN DNS” or “Block outside DNS”. Every provider recommended on this site has this; on some it is on by default.
In Group Policy (Windows Pro): Computer Configuration → Administrative Templates → Network → DNS Client → Turn off smart multi-homed name resolution → Enabled. On Windows Home, the equivalent registry value is DisableSmartNameResolution under HKLM\SYSTEM\CurrentControlSet\Services\Dnscache\Parameters, set to 1.
Also check that IPv6 is not going around the tunnel: if your VPN does not carry IPv6, disable it on the adapter, or move to a provider that does.
macOS
macOS is better behaved, and the usual cause is a manually configured DNS server left in System Settings → Network → Details → DNS. Remove entries there and let the VPN set them. If the app offers a permanent kill switch, turn it on — it also prevents the wake-from-sleep gap where queries escape before the tunnel is back.
iPhone and iPad
iOS routes DNS through the tunnel when the VPN profile is configured correctly, and the common cause of leaks is a third-party DNS profile installed earlier, from a content blocker or a “secure DNS” app. Settings → General → VPN & Device Management → DNS: set it to Automatic while the VPN is connected. Also turn on the app’s Always-On or on-demand setting so the system, not the app, maintains the tunnel.
Android
Android’s Private DNS setting can override the VPN. Settings → Network & internet → Private DNS → set to Automatic while using a VPN. Then turn on Always-on VPN with “Block connections without VPN”, which stops queries escaping when the app is suspended.
After the fix
Re-run the terminal check with the VPN connected. The resolver should now belong to the provider. If it still does not, the app’s DNS handling is the problem rather than the operating system’s, and that is a reason to change provider: every one on the best VPN list handles this correctly by default.