First, distinguish DNS resolution, traffic routing, and DNS hijacking
A website failing to load doesn’t necessarily mean your proxy node is down. The domain may not have resolved, may have resolved to the wrong address, or the app may not be using the client’s DNS at all. Before troubleshooting, identify three things: what receives the DNS request, which upstream returns an address, and which proxy rule handles the resulting connection.
- DNS resolution: The
dnssettings determine which resolvers the core queries and how it handles their responses. - Traffic routing:
rulesand proxy groups determine whether a connection uses DIRECT, a proxy node, or another outbound. A DNS response containing an overseas IP doesn’t automatically send the connection through a proxy. - DNS hijacking: This article uses the term for the TUN core intercepting DNS traffic on specified ports and forwarding it to the built-in resolver. It does not mean an ISP tampering with DNS responses.
Export a working configuration before making changes. If it comes from a subscription, use the client’s override, extension, or merge feature where possible. Editing a generated subscription file directly may mean your changes are overwritten on the next update. Change one thing at a time, then check the effective configuration after reloading.
nameserver vs. default-nameserver
nameserver is the list of upstream resolvers used for regular domain lookups. A bare IP usually means standard DNS on port 53; https:// indicates DoH, usually over TCP 443; and tls:// indicates DoT, usually over TCP 853. Encryption protects queries in transit to the resolver, but doesn’t guarantee that the resolver is reachable or replace connection routing.
| Setting | Purpose | What to check |
|---|---|---|
enable | Enable the built-in DNS | Even when set to true, confirm that requests actually reach the core |
listen | Provide a local DNS listening endpoint | Check the address, port, and system DNS settings |
default-nameserver | Bootstrap resolution for DNS upstreams specified by hostname | Prefer IP addresses reachable on the current network |
nameserver | Handle regular domain lookups | Upstream reachability, responses, and latency |
nameserver-policy | Choose a resolver for specific domains | Check that rules aren’t too broad and internal domains use the right upstream |
Upstream hostnames also need to be resolved first
When using https://dns.alidns.com/dns-query, the core first needs the address of dns.alidns.com—that’s what bootstrap resolution is for. Don’t treat default-nameserver as a fallback list for failed queries; it serves a different purpose from fallback.
Don’t point it at the core’s own listening address, such as 127.0.0.1:1053. The upstream query may loop back to the same resolver. If you specify another local resolver, check that it doesn’t forward requests back to Clash.
fallback isn’t just a timeout backup
With fallback configured, the core typically queries the primary and fallback resolvers in parallel for applicable requests, then uses fallback-filter to choose which response to keep. It doesn’t mean “wait five seconds for nameserver to fail, then try fallback.” The exact behavior also depends on caching, domain policies, and the core implementation.
How fallback-filter selects responses
geoip: true: Enables GeoIP-based checks. Combined withgeoip-code: CN, it uses the fallback response if the primary resolver returns an address outside the specified region.ipcidr: Uses the fallback response when the primary result matches one of the listed IP ranges. In this example,240.0.0.0/4identifies reserved addresses that shouldn’t be used as ordinary public destinations.domain: Sends listed domains directly to the fallback resolver, useful for a small number of domains where the primary resolver is known to be unreliable.
GeoIP checks rely on geographic data available to the core. They identify where an address is registered, not whether a response is genuine. An overseas address can be a valid CDN result, and an address in your region isn’t necessarily correct. Before enabling this filter, confirm that the GeoIP database is available and understand that more queries will depend on fallback reachability.
A mihomo DNS configuration you can merge
This example shows only the top-level dns section; it doesn’t include nodes, subscriptions, or proxy groups. Keep your working outbound settings and replace the existing DNS section or merge these settings with an override. Don’t define two top-level dns: sections in the same YAML file. The example listens on local port 1053 to avoid conflicts with existing services on port 53.
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"
default-nameserver:
- 223.5.5.5
- 119.29.29.29
nameserver:
- https://dns.alidns.com/dns-query
- https://doh.pub/dns-query
fallback:
- https://1.1.1.1/dns-query
fallback-filter:
geoip: true
geoip-code: CN
ipcidr:
- 240.0.0.0/4
When this configuration applies
The public resolvers in this example are specific configuration choices, not guaranteed to be reachable directly on every network. If 1.1.1.1:443 is unreachable, don’t enable this fallback setup yet. Temporarily remove fallback and fallback-filter, confirm that primary resolution works, then add a fallback resolver you’ve verified is reachable.
dns.ipv6: false prevents the built-in DNS from returning regular AAAA records. It doesn’t disable IPv6 in the operating system or stop every app from making IPv6 connections. If you need dual-stack access, verify IPv6 routing, node support, and TUN compatibility before enabling it.
In Fake-IP mode, the core can first return a virtual address from 198.18.0.0/16 to the app, while retaining the domain in its mapping. Seeing one of these addresses usually doesn’t indicate DNS interference; the actual resolution and connection flow also depend on the outbound type and rules. Domains in fake-ip-filter don’t receive virtual responses, but still need a suitable real resolver. The filter won’t make public DNS servers suddenly recognize internal hosts.
Put dns-hijack under tun, not dns
Setting listen: 127.0.0.1:1053 alone won’t make the system send all DNS requests to that port. System DNS settings generally accept only an IP address, not a custom port like 1053. For testing, you can specify the port; for everyday use, DNS needs a clear route into the core, such as TUN interception or a system forwarding service.
The snippet below is a standalone top-level tun section, at the same level as dns. If your client already generates TUN settings, merge only the fields you need—don’t create a duplicate section with the same name.
tun:
enable: true
stack: mixed
auto-route: true
auto-detect-interface: true
dns-hijack:
- any:53
- tcp://any:53
any:53intercepts regular UDP DNS requests entering TUN; the second entry adds TCP port 53.auto-routeconfigures routes automatically, but the scope of interception still depends on system permissions, interfaces, and platform-specific behavior.- Windows usually requires a client service or administrator privileges. macOS requires approval for the relevant network component. Android typically establishes the interception path through system VPN authorization.
- If your client has a TUN toggle, check both its UI status and the runtime logs to make sure the YAML and UI overrides aren’t conflicting.
Routing DNS upstreams through a proxy: bootstrap node hostnames first
Using DoH doesn’t guarantee that requests go through a proxy. In mihomo, respect-rules makes DNS upstream connections follow routing rules. If you enable it, also configure proxy-server-nameserver to resolve the proxy nodes’ own hostnames. Otherwise, you can end up with a dependency loop: you need the proxy to reach DNS, but DNS to reach the proxy.
dns:
respect-rules: true
proxy-server-nameserver:
- https://dns.alidns.com/dns-query
These are additional fields to merge into your existing DNS section, not a replacement for the complete configuration above. The resolver for node hostnames must also work before the proxy is connected. After enabling this, check rules: if the DoH destination still matches DIRECT, it will connect directly; if it matches a proxy group, a working node must be available in that group.
Use a separate resolver for internal domains
Company domains, NAS devices, and router admin domains should usually use internal DNS. Point nameserver-policy to a reachable internal resolver, using an address provided by your current network rather than copying a gateway from another setup. Also check whether TUN’s port 53 interception will send requests to that upstream back into the core, creating a loop.
For a small number of fixed devices, you can also use explicit hosts mappings. Don’t route all public domains through a local router just to resolve one internal domain. Likewise, don’t expect public DoH services to resolve private corporate zones.
Run these four checks to distinguish timeouts, DNS interference, and failed interception
Step 1: Confirm the configuration loaded
After reloading, check the logs and address YAML indentation, field types, port conflicts, and unknown options first. YAML uses spaces for indentation, not tabs. If startup logs report a listening error, check whether another process is using port 1053. If the UI says the change succeeded but the logs show no difference, check whether a subscription override replaced your manual settings.
Step 2: Query the built-in DNS directly
If dig is installed on macOS or Linux, specify the listening address and port. The first command checks the A record; the second checks AAAA behavior. In Fake-IP mode, a virtual address returned by the first command only confirms that the local endpoint responded—it doesn’t prove that fallback is working.
dig @127.0.0.1 -p 1053 example.com A
dig @127.0.0.1 -p 1053 example.com AAAA
On Windows, use nslookup’s interactive mode to specify a port. Enter the commands below one line at a time, then type exit when you’re done.
nslookup
server 127.0.0.1
set port=1053
set type=A
example.com
Step 3: Verify that system requests reach the core
After enabling TUN, run a system lookup without specifying a server and watch the core logs. If a direct query to port 1053 works but system lookups fail, check routing, TUN authorization, and the app’s DNS settings before changing nameservers again. A webpage loading in your browser isn’t conclusive either—it may be using its own DoH or a cached result.
Step 4: Check resolution and connections separately
Choose a domain that was failing and check the DNS error, matched rule, and final outbound. If you need more detail, temporarily increase the log level and restore it when you’re done. Remove subscription URLs, tokens, and node credentials before sharing logs. After changing upstreams, clear the relevant cache or restart the core, then compare the first and repeat requests for the same domain. This helps avoid mistaking a cache hit for a faster resolver.
| Symptom | Check first | Next step |
|---|---|---|
| Local query to port 1053 times out | DNS toggle, listening address, port conflict | Fix the local endpoint first, then check external resolvers |
| The core receives the query, but the upstream times out | Reachability on ports 443 or 853, bootstrap resolution, outbound rules | Keep only one upstream you’ve confirmed is reachable |
| Only some domains slow down after enabling fallback | GeoIP filter and fallback upstream reachability | Verify the filter conditions, then test the fallback resolver |
| Fake-IP is returned, but the connection fails | Mapping, TUN routing, rules, and node status | Trace the connection in the logs; don’t treat the virtual address as an error |
| Public sites work, but internal domains fail | Internal DNS and domain policies | Assign a reachable internal resolver to the internal zone |
A reliable order of operations is: get one nameserver working, confirm that system DNS reaches the core, add fallback filters, then configure proxy routing for DNS upstreams. Verify each step on its own to pinpoint whether the problem is the DNS entry point, upstream transport, or subsequent connection.