What DNS leaks are, and why a working proxy can still leak
Being able to load pages, run speed tests, and see normal latency doesn't mean the whole request went through the proxy. A DNS leak means domain resolution requests bypass the proxy tunnel and go straight to whatever DNS server your system is configured with (usually your ISP's DNS or the one handed out by your router). The result: page data travels through the proxy, but the information about "which domain you're visiting" is exposed to the local network's DNS server — an outside observer can still piece together a rough picture of your browsing.
This is especially common in pure rule-based setups (system proxy + app-level splitting). Most clients set the system proxy to HTTP/SOCKS5, but DNS queries initiated by the OS itself (over UDP port 53) don't go through that proxy chain by default — they follow the network adapter's own DNS settings. Some apps even use their own resolution logic (like a browser's DoH), which isn't bound by the system proxy at all. That's why "the connection works" and "DNS is clean" are two things that need separate verification.
Another common cause is incomplete routing rules. If domain-based split rules don't cover everything and some domains fall through to the DIRECT policy, the DNS queries for those domains will very likely also take the default local resolution path — a narrower, more subtle leak that's hard to notice day to day.
Two verification methods: online testing and local packet capture
To check whether DNS is leaking, it's best to combine an online tool with local packet capture — the former gives you a quick verdict, the latter pinpoints exactly which query got out.
Online testing: check where the DNS server is located
Open any site that offers DNS leak detection, run the test while Clash is on and the proxy is active, and the page will list the actual server IPs handling your DNS queries along with their location. Normally these servers should belong to the data center hosting your proxy node, or to the public DNS you specified in your config (such as Cloudflare or Google Public DNS IP ranges) — not your local ISP's DNS block. If your local ISP's DNS shows up in the results, that confirms a leak.
Two things to keep in mind while testing: wait a few seconds after switching nodes before testing, to give the policy group and DNS cache time to settle; and run the test multiple times across different detection sites, since a single result can produce a false negative due to caching.
Local packet capture: find exactly which domains are leaking
Online testing only tells you whether a leak exists — to find out which domains are leaking, you need packet capture. On Windows, use Wireshark to capture your network adapter's traffic with the filter udp.port == 53 || tcp.port == 853, covering plain DNS and DNS over TLS respectively. Under normal operation you should see almost no port-53 traffic heading to your ISP's DNS or your router's gateway address; if you do, expand that packet and check the Query field to see exactly which domain got out.
On macOS and Linux, you can use a command-line tool directly:
sudo tcpdump -i any port 53 -n
Browse a few sites afterward and check whether every destination address in the output points to the DNS you configured in Clash (for example, under fake-ip mode, what you should see at the system level is internal virtual DNS traffic, not direct traffic to an external port 53). If you catch a lot of packets going straight to external DNS servers, there's almost certainly a configuration gap.
How enhanced-mode: fake-ip works, and how to configure it
The enhanced DNS resolution provided by Clash (and its Meta-derived core, mihomo) is the most direct fix for leaks, and the core setting is dns.enhanced-mode, most commonly set to fake-ip. Here's how it works: the client takes over the system's DNS request entry point. When an app resolves a domain, instead of returning the real public IP to the system, Clash hands back a "fake IP" allocated from a private address pool. The app connects to that fake IP as usual, and as the traffic passes through the proxy chain, the Clash core reverse-looks-up the fake IP to recover the real domain, then routes it to the right policy group and real server address.
As a result, the system and application layer never see a real domain resolution request going out to an external DNS server — the whole DNS lookup step is folded entirely into Clash's own processing pipeline, closing off the leak at the source. A typical config looks like this:
dns:
enable: true
listen: 0.0.0.0:1053
enhanced-mode: fake-ip
fake-ip-range: 198.18.0.1/16
fake-ip-filter:
- '*.lan'
- localhost.ptlogin2.qq.com
- +.market.xiaomi.com
Breaking this down: enable turns on the built-in DNS server; listen sets the local port that server listens on, where the system or TUN adapter forwards resolution requests; enhanced-mode set to fake-ip turns on fake-address mapping; fake-ip-range sets the subnet used for the fake address pool, usually a reserved range that won't collide with your real LAN; fake-ip-filter is an allowlist — domains listed there (commonly LAN device discovery, or certificate pinning checks in certain banking apps) get real IPs instead of fake ones, so those features don't break.
It's worth stressing that fake-ip only works once the system actually hands its DNS queries to Clash's built-in server. Under TUN mode, the virtual adapter takes over all traffic at the network layer, so DNS requests are naturally intercepted — this gives the most thorough leak protection. Under pure system-proxy mode, the client relies on pointing the system's DNS setting at its own listening port; if that step never gets correctly written into the system network settings, a perfectly correct fake-ip config still won't take effect — which is a common reason people still test positive for leaks even with a "correct" config.
nameserver groups and fallback-filter: the anti-leak details
Fake addresses solve "what the app side sees," but the actual work of resolving a domain into a real IP still falls to the upstream DNS servers set in nameserver — and that step carries its own leak risk: if the upstream DNS is itself an untrustworthy ISP server, or the query travels unencrypted, it can still get logged by devices in between. It's a good idea to configure two DNS groups split along domestic/international lines, and use fallback-filter as a backstop:
dns:
enable: true
enhanced-mode: fake-ip
nameserver:
- 223.5.5.5
- 119.29.29.29
fallback:
- tls://1.1.1.1:853
- tls://8.8.8.8:853
fallback-filter:
geoip: true
geoip-code: CN
ipcidr:
- 240.0.0.0/4
domain:
- +.google.com
- +.facebook.com
nameserver is the default resolver used first, typically filled with public DNS servers inside mainland China to keep resolution of local sites fast; fallback is the backup resolver, and it's worth using DNS-over-TLS addresses (as shown with the tls:// prefix) so the query itself travels encrypted, protecting against snooping or tampering by devices along the path; fallback-filter decides when the fallback kicks in — geoip-code: CN means that if the result from nameserver falls within a mainland China IP range, it's trusted; otherwise the result is treated as potentially tampered with, and Clash re-resolves using the encrypted DNS under fallback instead. Domains listed under domain are forced straight to fallback, bypassing domestic resolution entirely.
This two-group setup is essentially "resolve nearby, fall back on tampering" — it keeps domestic sites from being slowed down by unnecessary round trips to overseas DNS, while making sure domains that need the proxy don't get pointed to wrong (or monitored) addresses by a tainted domestic resolution. Once it's set up, you can verify it with the packet capture method described earlier: resolution traffic for overseas domains should go to the encrypted DNS configured under fallback, not plaintext port 53.
Common troubleshooting checklist
- Check whether the client actually has enhanced DNS turned on — some clients ship with the DNS module disabled by default, and it needs to be enabled manually in settings.
- Under TUN mode, confirm the virtual adapter has successfully taken over routing — the DNS entries in your system network settings should point to Clash's built-in listening address, or be overridden by the virtual adapter.
- If a browser has its own hardcoded DoH (DNS over HTTPS) resolver enabled, it may bypass the system DNS settings entirely — disable that feature in browser settings, or switch it back to system DNS.
- A router or certain security software that force-assigns a DNS server may override Clash's DNS hijacking — you'll need to allow forwarding to Clash's listening port at the system or router level.
- Gaps in your rule set can send some domains through the DIRECT policy — periodically review your split rules and cross-check the logs to confirm queries hit the expected policy group.