What Counts as a DNS Leak?
A DNS leak occurs when a domain lookup takes a different path from the one you intended. For example, your browser may connect through a proxy while sending DNS queries to your local internet provider's recursive resolver. A working webpage or a changed exit IP alone does not prove that DNS requests also went through the proxy.
Check three separate steps when troubleshooting: which resolver the app sends its query to, which upstream mihomo selects, and which route the connection to that upstream takes. fake-ip mainly changes the address the core returns to the app; nameserver-policy selects the resolver; respect-rules determines whether the resolver connection follows routing rules. Each addresses a different part of the problem.
| What You See | What It Tells You | What You Can't Conclude From It |
|---|---|---|
| The website's exit IP belongs to a proxy server | That website connection used a proxy | System DNS and other apps also use the proxy |
| The query returns an address in 198.18.0.0/16 | That query may have been handled by a Fake-IP mapping | The upstream DNS connection definitely went through the proxy |
| The test page lists a public DNS provider | The test domain was handled by that provider's resolver infrastructure | Traffic between your device and the resolver is encrypted or uses a proxy |
| Logs show the DNS upstream connection matched a proxy policy | That upstream connection was routed according to the specified policy | The browser's built-in DoH also uses the core's DNS module |
Test in Order: System, Core, and Browser Queries
Step 1: Record the Current Setup
Note your current network, client mode, whether the system proxy is enabled, whether TUN is enabled, and whether your browser uses Secure DNS. Keep the same proxy node throughout testing and temporarily disable automatic proxy-group switching; otherwise, results may come from different exit nodes. Don't run a corporate VPN or another proxy client at the same time.
- Windows: Run
Get-DnsClientServerAddressin PowerShell and note the active network adapter and DNS addresses. - macOS: Run
scutil --dnsto inspect the default and per-domain resolvers. Your system may have several resolver sets. - Linux with systemd-resolved: Run
resolvectl statusto check DNS for each link, rather than relying only on/etc/resolv.conf.
Seeing your router's address as the network adapter's DNS server doesn't necessarily mean there's a leak. With TUN enabled, the core may intercept queries to port 53 on that address. Conversely, a public DNS address shown on the adapter does not mean queries are encrypted. Check the actual connections next.
Step 2: Query the Core's Listening Port Directly
The examples below configure the core to listen for DNS queries at 127.0.0.1:1053. After applying the configuration, run one of these commands. The first works on systems with nslookup; the second requires dig to be installed.
nslookup -port=1053 example.com 127.0.0.1
dig @127.0.0.1 -p 1053 example.com A
If the domain isn't matched by a Fake-IP filter, an A query should return a mapped address from the configured pool. This is the expected result, not a claim that the example was tested. If the query times out, first check that the DNS module is enabled, the configuration loaded, and port 1053 is available. Don't change all your routing rules at once.
Then run nslookup example.com to compare the system's default query with a direct query to the core. It's common for the results to differ when only the system proxy is enabled, since command-line resolvers typically don't use the HTTP system proxy. Test again with TUN enabled to see whether system queries are being intercepted.
Step 3: Check the Browser's Own DNS Settings
In Chrome, check DoH under Settings → Privacy and security → Security → Use secure DNS. Menu labels may vary slightly by version. For an initial baseline test, temporarily turn this feature off so the browser uses system DNS. Turn it back on afterward and check the route taken by the browser's DoH connection separately.
DNS leak test sites typically use random subdomains to identify recursive resolvers. Compare the providers shown before and after enabling the proxy, then check the core's connection logs. A resolver's location doesn't have to match the proxy's exit location: public DNS Anycast and recursive egress may be in different places. Judging by country or city alone can be misleading.
Example: Configure Fake-IP and DNS Upstreams
The following dns section can be merged into a mihomo configuration. Keep your existing proxies, subscription providers, and proxy groups. If the file already has a top-level dns: section, replace or merge its fields instead of adding a second section with the same name. Review the upstream routing requirements below before loading the configuration.
dns:
enable: true
listen: 127.0.0.1:1053
ipv6: false
enhanced-mode: fake-ip
fake-ip-range: 198.18.0.1/16
fake-ip-filter:
- "*.lan"
- "*.local"
- "localhost"
respect-rules: true
default-nameserver:
- 223.5.5.5
- 119.29.29.29
proxy-server-nameserver:
- https://dns.alidns.com/dns-query
nameserver:
- https://cloudflare-dns.com/dns-query
nameserver-policy:
"+.qq.com":
- https://dns.alidns.com/dns-query#DIRECT
"+.weixin.qq.com":
- https://dns.alidns.com/dns-query#DIRECT
What Each Field Does—and Doesn't Do
listenbinds only to the local loopback address. Port 1053 is an example listening port; it won't automatically change your operating system's DNS port, and it isn't intended to serve devices on your LAN.enhanced-mode: fake-ipassigns mapped addresses to domains, allowing the core to retain domain information for subsequent connections and rule matching. It does not encrypt upstream queries.- Domains listed under
fake-ip-filterdon't receive the usual Fake-IP mappings. Filter entries are not routing rules for direct connections and can't replacerules. ipv6: falsenarrows the scope of this DNS investigation. It does not disable IPv6 in your operating system or prevent apps from accessing existing IPv6 addresses.proxy-server-nameserverresolves proxy node hostnames, so connecting to a node doesn't depend on that same node for DNS. Node resolution is a bootstrap path that needs to be considered separately.
The example keeps the bootstrap path explicit to make initial troubleshooting easier. If your network can't reach the selected resolver, switch to an upstream that is reachable and verify its route again. DoH protects DNS traffic between your device and the resolver, but direct DoH still reveals the resolver's address to the network.
How nameserver-policy and respect-rules Work Together
Choose the Resolver, Then Set the Route
nameserver-policy selects an upstream based on the queried domain; it doesn't decide which node a website connection uses. The example sends qq.com and its subdomains to a specified resolver in mainland China, with #DIRECT explicitly routing the resolver connection directly. The weixin.qq.com entry is redundant here; it's included to demonstrate a more specific domain rule and can be removed if you don't need one.
Other queries go to cloudflare-dns.com. With respect-rules enabled, upstream DNS connections follow routing rules—but following rules doesn't automatically mean using a proxy. If the domain matches DIRECT, DoH still connects directly.
Assume your existing configuration has a proxy group named Proxy and that it currently selects a working proxy node. Add the two rules below before broader direct-connection rules and the final MATCH rule. The group name must match your configuration; if your group is named Node Selection, update the references accordingly.
rules:
- DOMAIN,dns.alidns.com,DIRECT
- DOMAIN,cloudflare-dns.com,Proxy
# Keep your existing routing rules and final MATCH rule below
This shows where to merge the rules; it is not a complete replacement for rules. Don't delete the other rules from your subscription just to add these two. If your proxy group allows selecting DIRECT, check the currently selected option too; a rule can look correct while its traffic still goes direct.
Avoiding DNS Bootstrap Loops
A common loop occurs when resolving the DoH server's hostname requires a proxy, but resolving the proxy server's hostname depends on that same DoH connection—which hasn't been established yet. This often causes widespread DNS timeouts at startup and prevents proxy nodes from connecting. The example assigns node hostnames to proxy-server-nameserver and routes its upstream connection directly, breaking that dependency loop.
- If your proxy node uses a hostname, first confirm that it can be resolved through an independent path.
- If your node uses an IP address, its hostname doesn't need resolving, but the DoH service may still require a bootstrap lookup.
- Don't force every bootstrap resolver through a proxy chain that isn't available yet and expect it to recover automatically.
- Change one dependency at a time. Save the configuration, check that it loads, then inspect the connection logs.
Use TUN to Intercept System DNS
The system proxy mainly affects apps that honor system proxy settings; it doesn't automatically intercept UDP DNS queries sent by the operating system. To route more apps' regular DNS requests through the core, enable TUN in your client. Desktop systems may require permissions, a service, or virtual network adapter support. Prefer the client's built-in controls to avoid conflicts between its UI and configuration file.
Here's an example of merging TUN settings in mihomo. If your client already generates a tun section, check the effective configuration before making changes.
tun:
enable: true
stack: system
auto-route: true
auto-detect-interface: true
dns-hijack:
- any:53
- tcp://any:53
any:53 intercepts UDP queries on port 53 entering TUN; the second entry covers TCP port 53. The key limitation is that traffic must enter TUN in the first place. Route exclusions, other VPNs, virtual machine networks, and platform limitations can bypass this path. Test interception with actual queries, especially for LAN DNS addresses.
DoH typically uses HTTPS on port 443, while DoT typically uses port 853. Configuring interception for port 53 won't automatically send either through the core's DNS module. TUN can route these network connections, but port 53 settings alone can't replace the queries inside them. Check Android Private DNS and browser Secure DNS separately too.
Verify the Changes and Roll Back if Needed
Retest Under the Same Conditions
- Confirm that the configuration loads successfully. If you see an unknown field or missing proxy group error, fix the configuration before testing in a browser.
- Keep the same proxy node and query
127.0.0.1:1053directly to confirm that the DNS module responds. - With TUN enabled, query the system's default DNS while watching the core's DNS logs and connection records.
- Visit a new domain and confirm that the upstream connection to
cloudflare-dns.commatches the expected proxy group. Don't check only the website connection. - Run the browser DNS test again. Then restore settings such as Secure DNS and test once more, noting any differences.
On Windows, clear the system DNS cache with ipconfig /flushdns. On Linux with systemd-resolved, use resolvectl flush-caches. Browsers and the core may also cache results; restart the browser and reload the configuration if needed. Fake-IP mappings may persist, so an old address alone doesn't prove the configuration failed to apply.
| Problem | Check First | What to Do |
|---|---|---|
| Core DNS query times out | Port 1053 listener, configuration load, port conflicts | Restore DNS responses on the local machine, then check the upstream |
| Fake-IP works, but websites won't load | Proxy node, whether mapped connections return to the core, TUN routing | Confirm connections are intercepted, then check proxy reachability |
| Test page still shows a local resolver | Browser DoH, system queries bypassing the core, other network adapters | Identify which app made the query, then check its route |
| All nodes time out after enabling respect-rules | Node hostname resolution and DoH routing dependencies | Restore independent node resolution, then enable rules one at a time |
| LAN device names no longer resolve | Local domain suffixes, mDNS, LAN resolvers | Set a resolution policy for your actual local domains; don't route all domains directly |
To roll back, first restore your saved DNS and routing rules, then disable the TUN settings added for this test. Finally, check that the operating system proxy and network adapter DNS haven't been left with test values. Don't delete the client's entire data directory; that also removes subscriptions, custom rules, and logs, leaving you with less information to troubleshoot.
Frequently Asked Questions
Why does the test page still show my ISP's DNS after I enable Fake-IP?
Your browser may be using its own resolver, or the core may still be sending upstream queries over a direct route. Fake-IP only shows that address mapping was involved in a particular lookup; it doesn't prove all DNS traffic uses the proxy. Check browser settings, upstream connections, and bootstrap resolution separately.
Is it enough to replace every nameserver with DoH?
No. DoH encrypts DNS queries in transit; routing determines whether they use a proxy. The system or an app may also bypass the core entirely. Verify interception, upstream selection, routing rules, and node resolution dependencies together.
Does sending queries for mainland China domains to a direct resolver always count as a leak?
Judge against your defined goal. If your policy explicitly allows these domains to use a specified direct resolver, that's intentional routing. If your goal is to send every query through a proxy, the configuration doesn't meet it. Define permitted exceptions first, then verify them in the logs; that's more reliable than trying to make a test page show just one address.
Where should I change settings that disappear after a subscription update?
Don't keep editing the original file if remote subscription updates will overwrite it. Use the client's local overrides, configuration merging, or scripting features; the exact controls vary by client version. After updating, check the effective DNS and TUN settings and rule order—not just whether the override file was saved.