DNS is the section of a Clash config that produces the most mysterious problems. Inaccurate routing, streaming that will not unlock, internal services that stop working — eight times out of ten it traces back here.
First, why a proxy client handles DNS at all
Normally your system resolves names and the application then connects to the resulting IP.
Under a proxy there is a conflict: rules are written against domains, but by the time traffic reaches the core there may be nothing left but an IP.
So the core needs to handle DNS itself. There are two ways to do that.
redir-host: resolve for real
The core receives the query, actually resolves the name, returns the real IP to the application, and internally notes "this IP corresponds to that domain".
Upside: a real IP is returned, so compatibility is best — internal services and LAN devices all behave.
Downsides:
- Every lookup waits for the upstream, which is slow
- If an overseas name's resolution is poisoned, you get the wrong IP
- The answer may be an in-region CDN address, skewing rule decisions
fake-ip: hand back a placeholder
The core receives the query and does not resolve it at all. It hands out a fake address from a reserved range like 198.18.0.0/16 and records "this fake IP = that domain".
The application connects to the fake address, the core recognises it, looks the domain back up, matches rules by domain, and then lets the exit node do the real resolution.
Upsides:
- DNS responses are effectively instant (nothing to wait for)
- Poisoning is eliminated at the root
- Domain information is fully preserved, so rule matching is at its most accurate
- Resolution at the exit node makes streaming region checks more reliable
Downsides:
- The returned address is fake, so anything depending on a real IP breaks
ping example.comshows198.18.x.x, which looks odd (harmless in practice)- Internal names must go into
fake-ip-filteror they will not resolve
Conclusion: use fake-ip almost always
A DNS configuration that works
dns:
enable: true
listen: 0.0.0.0:1053
ipv6: false
prefer-h3: false
enhanced-mode: fake-ip
fake-ip-range: 198.18.0.1/16
fake-ip-filter:
- "*.lan"
- "*.local"
- "*.localdomain"
- "+.internal"
- "+.home.arpa"
- "+.pool.ntp.org"
- "time.*.com"
- "ntp.*.com"
- "+.market.xiaomi.com"
- "localhost.ptlogin2.qq.com"
- "+.msftconnecttest.com"
- "+.msftncsi.com"
# used to resolve the hostnames of the DoH/DoT servers below
default-nameserver:
- 223.5.5.5
- 119.29.29.29
# primary DNS: resolves in-region names
nameserver:
- https://doh.pub/dns-query
- https://dns.alidns.com/dns-query
# fallback DNS: resolves overseas names
fallback:
- https://1.1.1.1/dns-query
- tls://8.8.4.4:853
# when to take the fallback answer
fallback-filter:
geoip: true
geoip-code: CN
ipcidr:
- 240.0.0.0/4
- 0.0.0.0/32
# send specific names to specific resolvers
nameserver-policy:
"geosite:cn": [223.5.5.5, 119.29.29.29]
"+.mycompany.com": "10.10.0.53"What each of the four fields does
How fallback-filter decides
It queries nameserver and fallback at the same time, then:
- The address from
nameserveris in the region → use it (so this is a local name) - The address from
nameserveris not in the region → use thefallbackanswer (an overseas name, where the local resolver may be poisoned)
This arrangement sends local names to local resolvers (fast, and CDNs land nearby) and overseas names to overseas resolvers (accurate, and not poisoned).
What a DNS leak actually is
A "DNS leak" means: you believe your traffic is going through the proxy, but name resolution is still going to your ISP's resolver. The consequence is that your ISP can see which domains you visit.
The browser's built-in DoH is the most commonly missed leak source: Chrome and Firefox may enable their own encrypted DNS by default, bypassing both system and core.
- Chrome/Edge: Settings → Privacy and security → Security → turn off "Use secure DNS"
- Firefox: Settings → Privacy & Security → at the very bottom, "DNS over HTTPS" → off
A table of common faults
| Symptom | Cause | Fix |
|---|---|---|
| Internal names will not resolve | fake-ip caught them | Add them to fake-ip-filter |
ping example.com shows 198.18.x.x | Expected behaviour | That is what fake-ip does; harmless |
| Cannot reach a LAN device by name (a NAS) | .local / .lan went through fake-ip | Add "*.lan" and "*.local" |
| An app spins forever | It has its own resolver and got intercepted | Check dns-hijack under TUN |
| Local sites got slower | An overseas resolver sent the CDN somewhere distant | Use nameserver-policy to keep local names on local resolvers |
| Streaming detects the wrong region | Resolution happened locally | Use fake-ip so the exit node resolves |
| Clock sync fails | NTP names went through fake-ip | Add "+.pool.ntp.org" and "time.*.com" |
| A messenger will not log in | A specific name needs a real IP | Add "localhost.ptlogin2.qq.com" |
What belongs in fake-ip-filter
The principle: any name that needs a real IP to function has to go in.
Verifying that the configuration took effect
Is fake-ip working?
nslookup google.com 127.0.0.1Getting 198.18.x.x back means fake-ip is running.
Are internal names going to the right resolver?
nslookup gitlab.mycompany.com 127.0.0.1It should return the real internal IP, not 198.18.x.x. A fake address means fake-ip-filter is wrong.
How long are lookups taking?
Switch the log level to debug and you can see the duration and the server used for each query. Lots of queries above 200 ms means your upstream resolvers are a poor choice.
In short
- Use fake-ip by default; it is faster, more accurate and immune to poisoning
- fake-ip-filter has to be maintained: internal names, NTP and connectivity checks are all mandatory
- nameserver-policy has the highest priority — use it for precise DNS routing
- Turn off the browser's own DoH, or everything above is wasted
- default-nameserver takes plain IPs only
Related: TUN internals and split tunnelling in practice.
Related docs
What TUN mode does, from the packet's point of view: creating a virtual adapter, rewriting the routing table, hijacking port 53, and choosing between the gvisor, system and mixed stacks.
What makes game traffic different: why UDP needs TUN, what NAT type actually means, why packet loss hurts more than latency, and a configuration checklist aimed at low latency.
What each column means and how to use it: tracking down the program eating your data, why the connection count explodes, where throughput is bottlenecked, and continuous monitoring through the API.