Understanding DNS leaks: separate DNS resolution from connections

Opening a website through a proxy doesn’t mean its domain was resolved through the proxy. An app might first ask your router or ISP’s DNS server for an IP address, then send the HTTPS connection through Clash. Or it might pass the domain to the proxy for remote resolution. The page looks the same either way, but the DNS exposure is different. To troubleshoot, check how the app resolves names, which component handles its requests, and how queries reach the upstream DNS server.

Here, a “DNS leak” means a query that should go through a proxy or a specified encrypted resolver is sent to the local network or another unexpected resolver instead. For sites in mainland China, direct connections using local DNS may be part of your routing policy; seeing a local resolver on a test page alone doesn’t prove something is broken. First decide what you want to protect: only domains routed through the proxy, or DNS queries from all apps except necessary lookups such as proxy node domains.

Four common ways DNS can bypass the proxy

  • System proxy only: Apps that ignore system proxy settings may still send UDP 53 queries directly to your router.
  • Browser Secure DNS: The browser sends HTTPS requests to its own DoH service, bypassing the core’s DNS module. Whether that connection uses the proxy still depends on traffic capture and routing rules.
  • Multiple DNS configurations: Subscription settings, client overrides, and system network settings may all be active, so the file you changed may not be the configuration currently in use.
  • IPv6 or another VPN: A second network adapter, an IPv6 route, or another VPN’s DNS settings may provide a path that isn’t covered by traffic capture.

How to test: compare browser results, core logs, and system queries

Before testing, note the client name, core type, system proxy status, TUN status, and current mode. The respect-rules and proxy-server-nameserver examples below are for mihomo cores that support these fields; don’t copy them unchanged into older versions of Clash. The client version and core version are separate details. Check the client’s About page and startup logs, respectively.

Step 1: Establish a baseline on the same network

  1. Turn off other VPNs, network accelerators, and proxy extensions. Note whether Secure DNS is enabled in your browser.
  2. Temporarily disable Clash traffic capture, then run the Extended test at dnsleaktest.com. Record the resolver IP, network provider, and test time.
  3. Enable the configuration you want to check, leave browser settings unchanged, and run the same test again. You can also cross-check with browserleaks.com/dns.
  4. Next, temporarily turn off browser Secure DNS and test once more. This helps distinguish the browser’s own resolution path from the system and core’s DNS path. Restore the setting afterward if needed.

Test sites typically have your browser request random subdomains, then identify the recursive resolver from the authoritative DNS side. The IP shown is the resolver’s egress IP, not necessarily the proxy node’s IP. With public DNS, it’s common for the resolver and proxy egress to appear in different cities. Anycast or forwarding can also cause multiple addresses to appear in a single test. Check who operates the resolver and whether it matches your expected path, rather than comparing country names alone.

Step 2: Confirm the DNS module is responding

The queries below target the local listening port 1053 used in this example. They verify only the core’s DNS module, not whether the system and browser are using that port. With fake-ip enabled and the domain not filtered, an A query should usually return a mapped address in the 198.18.0.0/16 range.

# Windows PowerShell or Command Prompt
nslookup -port=1053 example.com 127.0.0.1

# macOS / Linux (requires dig)
dig @127.0.0.1 -p 1053 example.com A

Next, open the client’s Logs page, temporarily set the log level to debug, and repeat a query and a page visit. Then check the Connections page for the target domain, matched rule, and final policy. Log formats vary by core version; look for resolution failures, connection timeouts, or messages about resolution loops. When you’re done troubleshooting, restore info to avoid logging too much domain information.

Step 3: Rule out cached results and identify real bypasses

If the old resolver still appears after a change, reload the configuration, clear the system DNS cache, and restart the test browser. On Windows, run ipconfig /flushdns. On Linux with systemd-resolved, run resolvectl flush-caches. These commands don’t clear every app’s internal cache, so rerun the test with random domains.

For a closer look, capture traffic on the physical network adapter and use the Wireshark display filter dns || tcp.port == 853 || udp.port == 853 to check for related traditional DNS, DoT, or DoQ traffic. DoH usually uses port 443, so this filter alone won’t identify it; check destination addresses alongside the core’s connection logs. Seeing a connection to the proxy server on the adapter is normal—don’t treat all outbound traffic as a leak.

DNS fields explained: nameserver, fallback, and respect-rules

FieldPurposeWhat to check
enableEnables the core’s DNS module.Once enabled, make sure app queries can reach it.
listenProvides a local DNS listening address.127.0.0.1:1053 does not automatically change your system DNS settings.
enhanced-modeSelects a handling mode such as fake-ip.Address mapping and the route to the upstream are separate concerns.
nameserverSets the default upstream resolver.Check the protocol, reachability, and whether the connection ultimately uses a direct route or a proxy.
fallbackProvides another set of upstream resolvers.Filters results to choose between them; it doesn’t simply query them only after the primary upstream fails.
fallback-filterFilters results based on criteria such as IP address or domain.Filtering results doesn’t stop the other upstream from receiving queries.
respect-rulesMakes DNS upstream connections follow routing rules.Setting this to true isn’t enough: configure node-domain resolution and check rule order too.
proxy-server-nameserverResolves proxy server domain names.Prevents a loop where the proxy is needed to resolve the node before the proxy connection can be established.
default-nameserverResolves domain names used by DNS upstreams.This is bootstrap resolution, not the default upstream for regular DNS queries; include it in your privacy considerations.

For example, if you configure both an ISP’s UDP DNS and an overseas fallback, the ISP may still receive queries even if the overseas result is ultimately used. To reduce exposure of domain names to local unencrypted DNS, remove unnecessary unencrypted upstreams rather than only changing fallback-filter.

nameserver-policy can also assign different upstreams to specific domains. If your subscription already includes this field, some domains may not use the default nameserver you just changed. Troubleshoot using the final active configuration, not just the snippet you added. Encrypted DNS protects queries between your device and the resolver, but the resolver itself can still process and see the domain names.

mihomo fake-ip configuration: specify upstreams and proxy routing

This DNS snippet can be merged into an existing mihomo configuration; it is not a complete subscription. It uses Cloudflare DoH via an IP address, reducing the need for a bootstrap lookup of the resolver’s domain name. Before using it, make sure the DNS service complies with your network policy and is reachable through your proxy node. If your existing configuration has fallback or nameserver-policy, review those too; pasting this snippet won’t automatically remove old fields.

# DNS snippet: replace or merge with the existing dns section
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"
    - "localhost"
  respect-rules: true
  nameserver:
    - https://1.1.1.1/dns-query
  proxy-server-nameserver:
    - https://1.1.1.1/dns-query

Here, ipv6: false temporarily removes AAAA results from the core’s DNS module as a troubleshooting variable. It does not disable IPv6 system-wide or prevent other apps from obtaining IPv6 addresses. If you need IPv6, turn it back on after confirming traffic capture and egress support, then test the IPv6 path separately.

Route business DNS upstreams through the intended proxy policy

respect-rules: true makes business DNS upstream connections follow routing rules. If your configuration includes direct routes for mainland China, other direct routes, or a direct fallback, specify where this upstream should go. Add the rule below near the top of your existing rules list, before broad IP rules and MATCH. This example assumes your existing policy group is named PROXY. If it has a different name, use that instead; don’t add a second rules key.

# Rule snippet: add to the existing rules list and keep all other rules
- IP-CIDR,1.1.1.1/32,PROXY,no-resolve

On the Proxies page, also confirm that PROXY currently selects a proxy node, not DIRECT. The rule above applies to connections from business DNS upstreams. proxy-server-nameserver handles the node-domain lookups needed to establish a proxy connection, so it can’t depend on that same proxy before the connection exists. This example uses direct encrypted DoH for node bootstrap lookups. Your local network can still see the connection to the resolver, and the resolver can still process the node domain.

Add only necessary domains to fake-ip-filter

For LAN devices or specific apps that need real addresses, add the relevant domains to fake-ip-filter. Filtering returns real addresses; it doesn’t automatically route traffic directly or prevent DNS leaks. Don’t add every domain to the filter to fix a single device, or you’ll lose the main benefit of fake-ip’s domain mapping.

TUN and browser settings: bring DNS queries into the capture path

A system proxy mainly affects apps that respect proxy settings; it can’t capture UDP DNS across the board. To capture traffic from more apps, use TUN if you have the required system permissions. Windows typically requires client service mode or administrator privileges; macOS and Linux also need the appropriate authorization or permissions. First confirm that the virtual network adapter was created successfully. Don’t assume traffic capture is working just because you switched it on in the UI.

# Optional TUN snippet: use alongside the dns section above
tun:
  enable: true
  stack: system
  auto-route: true
  auto-detect-interface: true
  dns-hijack:
    - any:53
    - tcp://any:53

This snippet routes regular UDP and TCP queries on port 53 entering TUN to the core’s DNS module. The actual behavior still depends on the operating system, routing, and core support. System DNS settings usually accept only a server IP, not an address with a port such as 127.0.0.1:1053. Without TUN, using a local resolver directly from the system requires a separate listener on port 53, with permissions and port conflicts handled.

  • Chrome: Settings → Privacy and security → Security → Use secure DNS. Temporarily turn it off for diagnosis, then compare the test results.
  • Firefox: Settings → Privacy & Security → DNS over HTTPS. Check the protection level and selected provider. Names may vary slightly between versions.
  • Android: Check Private DNS in system Settings. Menu locations vary by manufacturer, and its encrypted queries aren’t covered by ordinary UDP 53 hijacking.

Browser DoH and system DoT use separate encrypted connections; dns-hijack won’t turn them into regular DNS queries handled by the core. You can keep these features enabled, but verify that their connections use the intended route. Turning them off is a diagnostic step, not the only long-term solution.

Can’t connect after a change? Troubleshoot in dependency order

SymptomCheck firstWhat to do
Local query to port 1053 times outConfiguration load errors, whether DNS is enabled, and port conflicts.Get local queries working before checking browser test results.
Can’t connect when the node uses a domain nameCheck whether proxy-server-nameserver is reachable.Fix node bootstrap resolution and avoid a dependency loop between DNS and the proxy.
Node connects, but domain requests time outDoH upstream routing and the policy group’s current selection.Confirm the upstream connection uses the proxy and that the proxy can reach the DNS service.
Only some LAN apps are affectedCheck whether they receive fake-ip and whether they need real LAN addresses.Add narrowly scoped filters and LAN bypass rules.
Unexpected resolvers still appear in test resultsBrowser DoH, Private DNS, other VPNs, and IPv6.Disable each one in turn to compare results, identify the bypass, then decide whether to keep it and how to route it.

Change one layer at a time: start with DNS upstreams, then rules, then TUN, and finally independent browser and system resolvers. Changing nodes, enabling TUN, and editing system DNS all at once makes it hard to tell what worked. If enabling TUN cuts off all network access, turn TUN off to restore connectivity, then check the default route, permissions, and conflicts with other VPNs. Don’t keep adding different DNS addresses.

Final verification checklist

  1. A local query to 1053 returns the expected mapped address, and the core logs show no persistent DNS timeouts.
  2. Business DoH connections use the specified proxy policy, and node domains have a separate, working bootstrap resolution path.
  3. The DNS service shown in browser test results matches your expectations; the resolver IP does not have to match the node IP.
  4. Test browser Secure DNS, system Private DNS, IPv6, and LAN apps separately.
  5. Retest after restarting the client and updating the subscription to confirm overrides persist.

The key to preventing DNS leaks isn’t setting a single field to true; it’s building a verifiable path for traffic capture, address mapping, upstream resolution, and routing. Repeat these checks whenever you change networks, nodes, or client cores. For port, policy group, and startup settings, see the port guide in the advanced manual and Troubleshooting in the Help Center.