You set up WireGuard, the tunnel is up, traffic is flowing through the VPN, and then you run a DNS leak test — and your ISP's DNS server is right there, visible to every site you visit. That single misconfiguration defeats half the point of running a VPN. WireGuard is fast and modern, but unlike OpenVPN it does absolutely nothing to manage DNS on your behalf. If you do not explicitly tell the operating system which DNS server to use and lock it down, the OS will happily keep using whatever server it had before the tunnel came up. This guide explains exactly why leaks happen and gives you concrete, copy-paste fixes for Windows, Linux, and macOS.

What a DNS Leak Actually Is

When you type a domain name into a browser, the OS sends a DNS query before any TCP connection is made. If that query travels outside the encrypted WireGuard tunnel — even while all HTTP/HTTPS traffic goes through it — the DNS resolver your ISP assigned via DHCP sees every hostname you visit. Your real location and identity are exposed at the DNS layer even though your IP traffic is hidden.

WireGuard operates at Layer 3. It tunnels IP packets according to its AllowedIPs directive. DNS is just UDP port 53, and unless the DNS server's IP address is covered by AllowedIPs, those packets route around the tunnel entirely. That is the root cause of almost every WireGuard DNS leak.

The Three Most Common Causes

  • DNS directive missing from the WireGuard config: The [Interface] section has no DNS = line, so the OS keeps its existing resolver.
  • AllowedIPs does not cover the DNS server IP: You use a split-tunnel setup (e.g., AllowedIPs = 10.0.0.0/8) but your VPN DNS server is at, say, 1.1.1.1, which is not in that range.
  • OS-level DNS leaks on Windows: Windows 8 and later use "Smart Multi-Homed Name Resolution" — it sends DNS queries to every adapter simultaneously and uses whichever responds first. WireGuard's DNS setting alone does not disable this.

Step 1: Set the DNS Directive in Your WireGuard Config

Open your WireGuard config file. On Linux this is typically /etc/wireguard/wg0.conf. On Windows and macOS it is managed through the WireGuard GUI app. Look at the [Interface] block and add or correct the DNS line:

[Interface] PrivateKey = YOUR_PRIVATE_KEY Address = 10.8.0.2/32 DNS = 10.8.0.1 [Peer] PublicKey = SERVER_PUBLIC_KEY Endpoint = vpn.example.com:51820 AllowedIPs = 0.0.0.0/0, ::/0

In this example, 10.8.0.1 is the VPN server's internal IP address, which should be running a DNS resolver (Pi-hole, Unbound, or a simple forwarder). If your VPN provider gave you a specific DNS IP, use that instead. You can also use a public resolver like 1.1.1.1 or 9.9.9.9, but note the tradeoff: those queries will be encrypted inside the WireGuard tunnel but will ultimately leave through the VPN server's IP.

Using AllowedIPs = 0.0.0.0/0, ::/0 routes all traffic (IPv4 and IPv6) through the tunnel. This is a full-tunnel configuration and is the simplest way to prevent DNS leaks. If you are on a split-tunnel setup, continue to Step 2.

Step 2: Fix Split-Tunnel DNS Leaks with Correct AllowedIPs

If you only want certain subnets through the VPN, you must make sure the DNS server's IP is explicitly included in AllowedIPs. Otherwise DNS packets skip the tunnel.

AllowedIPs = 10.8.0.0/24, 192.168.100.0/24, 1.1.1.1/32, 1.0.0.1/32

This routes the VPN subnet, a private subnet, and both Cloudflare DNS server IPs through the tunnel. Adjust the IP addresses to match whatever DNS server you specified in the DNS = directive. If you are using 9.9.9.9 (Quad9), add 9.9.9.9/32 and 149.112.112.112/32 instead.

You can verify which routes are active after bringing up the interface with:

# Linux ip route show table main # macOS netstat -rn

Step 3: Fix DNS Leaks on Windows

Windows is the most problematic platform for DNS leaks with WireGuard because of Smart Multi-Homed Name Resolution (SMHNR). Even with a correct DNS directive, Windows may still query the physical adapter's DNS server in parallel.

Option A: Disable Smart Multi-Homed Name Resolution via Group Policy

  1. Press Win + R, type gpedit.msc, press Enter.
  2. Navigate to Computer Configuration > Administrative Templates > Network > DNS Client.
  3. Double-click Turn off smart multi-homed name resolution.
  4. Set it to Enabled and click OK.
  5. Restart the WireGuard tunnel (deactivate and reactivate in the WireGuard app).

Option B: Disable SMHNR via Registry (Home editions)

If you are on Windows Home and do not have Group Policy Editor, use the registry instead:

reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows NT\DNSClient" /v DisableSmartNameResolution /t REG_DWORD /d 1 /f reg add "HKLM\SYSTEM\CurrentControlSet\Services\Dnscache\Parameters" /v DisableParallelAandAAAA /t REG_DWORD /d 1 /f

Run those two commands in an elevated Command Prompt (right-click, Run as administrator), then reboot.

Option C: Set DNS per-adapter via PowerShell

After the WireGuard tunnel is active, force the physical adapter to use no DNS or a non-functional address so it cannot respond to DNS queries:

# Find your physical adapter name Get-NetAdapter # Set DNS on physical adapter to a dummy address while VPN is up Set-DnsClientServerAddress -InterfaceAlias "Ethernet" -ServerAddresses "127.0.0.2" # Restore it when VPN goes down Set-DnsClientServerAddress -InterfaceAlias "Ethernet" -ResetServerAddresses

Step 4: Fix DNS Leaks on Linux with systemd-resolved

Most modern Linux distributions use systemd-resolved. WireGuard's DNS = directive sets the resolver for the interface, but without telling resolved to use it exclusively, other interfaces may still handle queries.

# /etc/systemd/resolved.conf [Resolve] DNS=10.8.0.1 FallbackDNS= Domains=~. DNSOverTLS=yes

The Domains=~. line is critical — it tells systemd-resolved to use this DNS server for all domains (the tilde dot is a catch-all). Set FallbackDNS to empty so there is no fallback that could leak. Apply the changes:

sudo systemctl restart systemd-resolved sudo resolvectl status

Confirm the WireGuard interface shows the correct DNS server in the output of resolvectl status. If you are using wg-quick, it handles the resolvectl call automatically when you have a DNS = line, but the Domains=~. trick via PostUp ensures exclusivity:

[Interface] PrivateKey = YOUR_PRIVATE_KEY Address = 10.8.0.2/32 DNS = 10.8.0.1 PostUp = resolvectl domain %i "~." PreDown = resolvectl domain %i ""

Step 5: Fix IPv6 DNS Leaks

Many people fix IPv4 DNS leaks and forget about IPv6. If your machine has a global IPv6 address and your VPN does not route IPv6, DNS queries may go out over IPv6 to your ISP's IPv6 DNS server. The cleanest fix if your VPN does not support IPv6 is to include the IPv6 blackhole routes in AllowedIPs:

AllowedIPs = 0.0.0.0/0, ::/0

This drops all IPv6 traffic into the tunnel. If the VPN server does not have IPv6 configured, IPv6 connections will simply fail, which is acceptable for privacy — it is better than leaking. Alternatively, disable IPv6 on the physical adapter entirely:

# Linux — disable IPv6 on eth0 sudo sysctl -w net.ipv6.conf.eth0.disable_ipv6=1 # Make permanent in /etc/sysctl.conf net.ipv6.conf.eth0.disable_ipv6 = 1

Step 6: Add a Kill Switch to Prevent Leaks on Reconnect

Even with correct DNS configuration, a brief moment between tunnel drop and reconnect can expose DNS. A kill switch blocks all traffic outside the tunnel. On Linux with wg-quick, add these lines to your config:

[Interface] PrivateKey = YOUR_PRIVATE_KEY Address = 10.8.0.2/32 DNS = 10.8.0.1 PostUp = iptables -I OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT && ip6tables -I OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT PreDown = iptables -D OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT && ip6tables -D OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT

This iptables rule blocks any packet that is not marked by WireGuard and is not destined for a local address. As soon as the tunnel drops, all outbound traffic is rejected until it comes back up.

💡 After making any configuration change, always verify with a live DNS leak test. Use our DNS Propagation Checker to confirm your VPN's DNS resolver is the one actually resolving your queries — not your ISP's server.

How to Verify the Fix

Do not rely on a single test. Run all three of these checks:

Command-Line Verification

# Linux / macOS — check which server answers dig +short whoami.cloudflare.com TXT @1.1.1.1 # Should return the VPN server's exit IP, not your home IP nslookup myip.opendns.com resolver1.opendns.com # Check active DNS server on Linux resolvectl status | grep "DNS Servers"

Windows Command-Line Verification

nslookup whoami.cloudflare.com 1.1.1.1 ipconfig /all | findstr "DNS Servers"

In the output of ipconfig /all, only the WireGuard adapter should show your intended DNS server. Every other adapter should either show no DNS server or an unusable address.

Web-Based Test

Navigate to a DNS leak test site while the WireGuard tunnel is active. You should see only the IP addresses of the DNS server you specified in your config. If you see any address belonging to your ISP, one of the steps above was not applied correctly. Use our DNS Lookup tool to cross-check which resolver is answering queries for a domain you just looked up inside the tunnel.

How to Prevent DNS Leaks Long-Term

  • Always use full-tunnel routing (0.0.0.0/0, ::/0) unless you have a specific operational reason for split-tunnel. Full tunnel is the default-safe option.
  • Use DNS over the tunnel only: Set DNS = to an IP that is inside your VPN subnet or is explicitly routed through AllowedIPs.
  • Run your own resolver on the VPN server: A local Unbound or Pi-hole instance means DNS never leaves the server you control.
  • Automate the kill switch: Use the PostUp/PreDown iptables approach shown above so the protection is always active when the tunnel is, and never needs to be remembered manually.
  • Test after every config change: Any time you update the WireGuard config, run resolvectl status and a web leak test before considering the job done.
  • Keep WireGuard updated: Run sudo apt upgrade wireguard (Debian/Ubuntu) or the equivalent for your distro regularly. Bugs in the DNS handling of wg-quick have been fixed in past releases.

Quick Reference: Minimal Leak-Free WireGuard Config

If you want a clean template that incorporates all the fixes above for a full-tunnel Linux client, here it is:

[Interface] PrivateKey = CLIENT_PRIVATE_KEY Address = 10.8.0.2/32, fd00::2/128 DNS = 10.8.0.1 PostUp = resolvectl domain %i "~." PostUp = iptables -I OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT PostUp = ip6tables -I OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT PreDown = resolvectl domain %i "" PreDown = iptables -D OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT PreDown = ip6tables -D OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT [Peer] PublicKey = SERVER_PUBLIC_KEY Endpoint = vpn.example.com:51820 AllowedIPs = 0.0.0.0/0, ::/0 PersistentKeepalive = 25

This configuration routes all IPv4 and IPv6 traffic through the tunnel, sets DNS to the VPN server's internal address, locks systemd-resolved to use only that server, and enforces an iptables kill switch. It is the baseline every WireGuard deployment should start from when privacy and leak prevention are the goal.