What Is a DNS Leak

Your proxy is on and pages load fine, but your machine is still sending DNS queries straight to your ISP's servers through the system resolver — those queries never touch the proxy tunnel, so every domain you visit sits in plaintext in your ISP's or router's logs. That's a DNS leak.

There are two consequences. First, privacy: whoever handles resolution has your full browsing history, and the encrypted tunnel your proxy builds does nothing to hide this step. Second, reliability: plaintext queries on port 53 are easy for middleboxes to hijack, returning poisoned or wrong addresses — which shows up as "the node looks fine but the site won't load" or getting redirected to some unrelated page.

A common myth is that turning on system proxy mode is enough. System proxy settings only change an app's HTTP proxy config — they don't touch DNS queries at all. Plenty of apps ignore the system proxy and resolve on their own, and browsers may also fire off their own encrypted DNS queries outside that path. DNS traffic is still traffic, and it needs to be captured just as completely as web traffic for the leak to actually be closed.

How to Test: Confirm Whether You're Leaking

Before testing, close any other proxy or VPN tools. Keep Clash running in the proxy mode you want to verify, so nothing else interferes with the result.

  1. Open any IP lookup page and confirm your outbound address already matches your node — this proves the proxy tunnel itself is working.
  2. Open a DNS leak test page and run the full test, then note the DNS server addresses, ISPs, and regions it reports.
  3. In a terminal, run nslookup example.com and note the server address in the response. On macOS or Linux, cross-check with dig example.com.
  4. In Clash, switch the log level to debug and run nslookup again, then check whether that query actually shows up in the log.
  5. Check the DNS your system is currently using: on Windows run ipconfig /all, on macOS run scutil --dns, on Linux run resolvectl status or check /etc/resolv.conf directly.

Compare all of these results together using the criteria below:

What you seeWhat it means
The test page lists your ISP's or router's DNSLeaking — resolution is happening locally, unproxied
The debug log has no record of your nslookup queryLeaking — the query never went through Clash
nslookup on a proxied domain returns a 198.18.x.x addressNormal — the query was handled by fake-ip
The test page lists DNS in the region of your proxy exitNormal — resolution is going out through the proxy

Cross-checking is what makes it reliable

A test page alone isn't enough. It tells you where the resolution ultimately came from, while the Clash log tells you whether the query went through Clash at all. Only when both agree can you trust the verdict.

Common Causes: Five Frequent Culprits

Once you've confirmed a leak, check these five causes in order, from most to least common.

  1. Only system proxy mode is on — system proxy settings only affect apps that respect them, not UDP 53 DNS traffic. Plenty of desktop apps still query the system DNS directly.
  2. The browser has "secure DNS" enabled — a browser's built-in DoH bypasses the system proxy entirely and sends queries straight to the browser's own DoH server, invisible at the system level.
  3. IPv6 isn't covered — if only IPv4 is being captured, AAAA queries and IPv6 traffic can leak out through the local network, which is especially common on dual-stack connections.
  4. A rule is letting DNS through — a direct rule like DST-PORT,53, or a domain that should be proxied being misrouted into the DIRECT policy.
  5. Your ISP or gateway is hijacking port 53 — even with a public DNS set manually, plaintext queries can still be redirected in transit, which looks identical to an actual leak.

Fix the Config: Route Every Query Through Clash

There's really one core principle for fixing leaks: every DNS query needs to go into Clash's DNS module first, so Clash decides whether it's resolved locally or sent out through the proxy. That takes two steps: turn on TUN to capture system-wide traffic, then set up the dns block correctly.

Step 1: Enable TUN Mode

TUN captures all system traffic through a virtual network adapter, including UDP 53, so every app's DNS queries land in Clash first — this is the most thorough way to close a leak. On Windows you'll need service mode installed and to run as administrator; on macOS you need to authorize the helper install; on Linux you need root or the right capabilities. Here's an example config for the mihomo core:

tun:
  enable: true
  stack: mixed
  auto-route: true
  auto-detect-interface: true
  dns-hijack:
    - any:53

Here, any:53 under dns-hijack means any port-53 query, to any destination, gets hijacked into Clash's DNS module — this is the single line that actually closes the leak.

Step 2: Configure the dns Block

dns:
  enable: true
  ipv6: false
  listen: 0.0.0.0:53
  enhanced-mode: fake-ip
  fake-ip-range: 198.18.0.1/16
  fake-ip-filter:
    - "+.lan"
    - "+.local"
  default-nameserver:
    - 192.0.2.53
  nameserver:
    - https://192.0.2.53/dns-query
  proxy-server-nameserver:
    - https://192.0.2.53/dns-query
  • enable and listen: Clash serves DNS on port 53, working together with TUN's dns-hijack to capture every query.
  • Set enhanced-mode to fake-ip: for domains that should be proxied, Clash returns a fake address in 198.18.0.0/16, the app connects using that fake address, and Clash maps it back to the real domain before handing it to the proxy — the actual domain never gets resolved locally, which is the strongest leak protection available. redir-host, by contrast, does a real resolution locally first, leaving a window for a plaintext query.
  • Set ipv6 to false: this stops AAAA queries from being answered, avoiding an IPv6 bypass. If you actually need IPv6, make sure TUN is capturing IPv6 traffic too.
  • default-nameserver: the bootstrap resolver, used only to resolve the addresses of the DoH/DoT servers listed under nameserver — it must be a plain IP.
  • nameserver: your main upstream resolver — use DoH or DoT here so queries leave over an encrypted channel, and your local network only sees one encrypted connection to the DoH server.
  • proxy-server-nameserver: dedicated to resolving your node's own domain, so it doesn't end up going through an untrusted local DNS.

The addresses in this config are placeholders

192.0.2.53 above is a documentation placeholder — swap it for an encrypted DNS provider you actually trust. Subscription configs usually already ship with a dns block, so back up the original file before editing.

If you can't turn on TUN right now, a fallback is to keep system proxy mode on and point your system DNS at 127.0.0.1, letting Clash's dns module listen on port 53 and handle system-level resolution. This covers apps that respect the system DNS, but not apps that hardcode their own resolver — it's noticeably weaker than the TUN approach.

Verify the Fix: Run the Test Again

After changing the config, restart Clash or reload it, then run through the same test steps above and check each item again:

  • Run nslookup example.com — the server address should show as 127.0.0.1 or a local address, confirming Clash has taken over the query.
  • Run nslookup on a proxied domain — it should return an address in the 198.18.x.x range, confirming fake-ip is working.
  • The debug log should show that query, along with which upstream and policy it hit.
  • Re-run the DNS leak test page — your ISP's or gateway's DNS should no longer show up, only resolution tied to your proxy exit region.
  • Visit a handful of sites, both regional and international, to confirm direct sites still resolve fine and proxied sites still load — making sure the config change didn't break anything new.

The Final Pass/Fail Bar

The leak is closed only when all three hold at once: every local query shows up in the Clash log, the test page no longer reports local DNS, and proxied domains return fake-ip addresses.

FAQ

Some devices on my LAN stop working after enabling fake-ip?

Add your LAN domains to fake-ip-filter — for example +.lan, +.local, and your router's admin address. These will skip fake-ip and resolve normally.

My whole system loses internet access after turning on TUN?

First confirm service mode or the required permissions are properly installed, then check whether auto-route and auto-detect-interface are enabled. If it's still broken, try switching stack between mixed, system, and gvisor, and check the log for TUN-related errors.

The test page shows DNS in several different regions — is that a leak?

Not necessarily. As long as all of those DNS servers belong to your proxy exit region and every query shows up in the Clash log, that's just normal behavior for resolution going out through the proxy. It only counts as a leak if you see your actual ISP's or gateway's DNS.

Do I need to turn off my browser's secure DNS?

In TUN mode, your browser's DoH traffic is captured and routed by your rules too, so it's fine to leave it on. If you're only using system proxy mode, turn it off — otherwise the browser can bypass Clash and resolve on its own.

Download the Clash Client

Pick the client for your platform, install it, and apply the TUN and DNS config from this guide to close the leak.