Start by tracing where DNS queries go
Before configuring mihomo DNS, distinguish three steps: which resolver the app sends queries to, which upstream mihomo uses, and how the resulting connection is routed. Changing nameserver to encrypted DNS only affects the middle step. If your browser still uses its own Secure DNS, the query may never reach mihomo's DNS module.
This guide covers mihomo, the kernel developed after Clash Meta. Some clients override DNS or TUN settings in their UI, so check the configuration actually loaded at runtime, not just the original subscription file. Older Clash kernels may not support every field described here.
- Back up your current configuration and note the state of the system proxy, TUN, and DNS override settings.
- Check the client’s kernel information to confirm it is running mihomo; don’t rely on the client’s name alone.
- First test resolution through the local
127.0.0.1:1053endpoint, then decide whether to enable TUN routing. - Change one group of fields at a time. After each reload, check the logs and test the same domain.
nameserver and default-nameserver: start with a minimal working setup
This DNS snippet is meant to be merged into an existing configuration; it is not a complete proxy subscription. It contains no proxies or rules, and you don’t need to remove your existing proxies, proxy-groups, or rules. Keep only one top-level dns: section and merge these fields into it to avoid duplicate keys and ambiguous results.
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:
- "localhost"
- "*.lan"
- "*.local"
- "+.home.arpa"
default-nameserver:
- 223.5.5.5
- 119.29.29.29
nameserver:
- https://dns.alidns.com/dns-query
- https://doh.pub/dns-query
| Field | What it does in this configuration |
|---|---|
enable | Enables mihomo’s built-in DNS module. |
listen | Accepts DNS queries on port 1053 at the local loopback address; it is not exposed to your LAN. |
default-nameserver | Resolves the hostnames of upstream DNS services, so their addresses are available before regular DNS resolution begins. |
nameserver | Sets the upstreams for regular resolution; this example uses two HTTPS DNS endpoints. |
ipv6: false | Returns an empty result for AAAA queries in the DNS module, which can help troubleshoot the IPv4 path first. This does not disable IPv6 in the operating system. |
Don’t treat the upstream list as a fixed primary-and-backup order
Two entries in nameserver do not mean “use the second only if the first fails.” The kernel may query them concurrently and use an available response, so don’t rely on list order for strict primary/backup behavior. Start with upstreams that are reachable on your network, then consider latency. Adding a dozen addresses usually just adds more variables to troubleshoot.
default-nameserver does not mean “send all website queries here by default.” Its main purpose is to resolve the hostnames of DNS services themselves. This example uses IP addresses for bootstrap resolution, but those initial queries are not automatically encrypted just because later queries use DoH.
fallback and fallback-filter: filter by response, not just as a timeout backup
fallback is a separate set of DNS upstreams, not a proxy group and not a group of nodes to choose from. When both nameserver and fallback are configured, the kernel may query them in parallel and use fallback-filter to decide whether to accept a fallback response. Thinking of fallback as “backup DNS that starts only when the primary times out” can lead to incorrect assumptions about query traffic.
To filter results by IP geolocation, merge the following fields into the same dns: section above. First make sure these upstreams are reachable on your network. Adding an unreachable DoH endpoint won’t automatically fix DNS poisoning or connectivity problems.
fallback:
- https://1.1.1.1/dns-query
- https://8.8.8.8/dns-query
fallback-filter:
geoip: true
geoip-code: CN
ipcidr:
- 240.0.0.0/4
How to read these three filtering conditions
- Geolocation condition: With
geoip: trueandgeoip-code: CN, mihomo tends to use a fallback response when a regular DNS result’s IP is outside the specified region. This checks the resolved IP, not the domain suffix. - IP range condition:
ipcidrspecifies address ranges to exclude from regular DNS results. The240.0.0.0/4range in this example is a filtering condition, not a proxy routing rule for website traffic. - Data dependency: GeoIP checks rely on the geographic data used by the kernel. Missing or outdated data, or changes in CDN address ownership, can affect which response is selected.
This mechanism does not guarantee “domains in mainland China use mainland Chinese DNS, and domains elsewhere use foreign DNS.” Domains can be hosted in multiple regions, and CDNs can return different addresses. For deterministic routing of internal business domains or known services, nameserver-policy is more direct than relying on geolocation.
nameserver-policy: choose an upstream for specific domains
Office networks, home NAS devices, and printers often use hostnames that only an internal DNS server can resolve. Public resolvers are not a good fit for these domains. The example below is for a home network: use it only if your router is actually at 192.168.1.1 and can resolve internal records under home.arpa.
nameserver-policy:
"+.home.arpa":
- 192.168.1.1
These are still partial fields to merge into your existing dns: section. Matching domains use the upstream selected by this policy; don’t assume they go through the standard nameserver/fallback filtering flow unchanged. +.home.arpa matches the domain and its subdomains. Before substituting a company’s internal domain, confirm the DNS server and access requirements with your network administrator.
nameserver-policy and fake-ip-filter solve different problems: the former chooses which server to query, while the latter controls whether the app receives a fake address. After setting a DNS policy for internal domains, add the relevant domains to the fake-IP exclusion list if device discovery or direct connections also require real IP addresses.
fake-ip vs. redir-host: fake addresses or real addresses
enhanced-mode controls the DNS enhanced mode. fake-ip returns a mapped address from a reserved range to the app. When the connection reaches mihomo again, the kernel uses the mapping to recover the domain, then applies rules and outbound handling. This address is not the website’s real server IP and cannot be used directly by devices that don’t route through mihomo.
| Comparison | fake-ip | redir-host |
|---|---|---|
| A record returned to the app | Usually a mapped address from the configured address pool | The address returned by real DNS resolution |
| How the domain is associated | Domain information is preserved through the fake-IP mapping | Domain recovery depends on DNS cache association and related mechanisms |
| Connection requirement | Connections to fake IPs must return to the corresponding mihomo instance | The real IP is available, but the connection must still enter the intended proxy or routing path |
| Common compatibility issues | LAN discovery, apps that validate real IPs, and connections that bypass interception | Limits on domain detection caused by shared IPs, cache association, and app-level DNS |
When to add domains to fake-ip-filter
Add LAN hostnames and device-discovery services that need real addresses to the exclusion list first. With the default blacklist-style filtering, excluded domains don’t receive fake IPs and are resolved to real addresses instead. Don’t add every domain: the configuration may still say fake-ip, but its behavior will have changed substantially.
If only one app is acting up, exclude the specific domain it depends on instead of immediately switching the global mode. After switching to redir-host, clear the client’s DNS cache and restart the affected apps; if needed, flush the system DNS cache too. Changing the YAML won’t immediately remove old 198.18.x.x records from every app.
TUN DNS hijacking: intercept traditional port 53 queries
Here, DNS hijacking means routing specified DNS traffic passing through TUN to the mihomo DNS module; it does not modify DNS records on remote websites. To capture traffic from desktop apps that don’t use the system proxy, you’ll usually need to configure TUN routing correctly before setting up DNS hijacking.
tun:
enable: true
stack: mixed
auto-route: true
auto-detect-interface: true
dns-hijack:
- any:53
- tcp://any:53
any:53 matches UDP queries on port 53 passing through TUN, while tcp://any:53 adds TCP port 53. auto-route configures routes automatically, and auto-detect-interface detects the outbound interface. The actual interception scope still depends on the operating system, routing, and client implementation; this YAML alone cannot prove that all DNS traffic is intercepted.
- Check permissions: Desktop clients usually need service mode or appropriate administrator privileges to create a virtual network adapter and routes. Check for permission errors before changing DNS fields.
- Know which port is which:
listen: 127.0.0.1:1053is an explicit query endpoint. TUN handles port 53 interception in the kernel; you don’t need to set the system DNS address to a string ending in port 1053. - Check encrypted DNS: DoH typically uses port 443, while DoT typically uses port 853. Intercepting port 53 alone can’t force these protocols through mihomo. For comparison, temporarily turn off Secure DNS in your browser and test again.
- Don’t expose an open resolver: Listen on a LAN address only if you actually need to provide DNS for LAN devices, and configure your firewall accordingly. Never expose it to the public internet just to make testing easier.
Android clients usually capture traffic through the system VPN interface, so desktop auto-routing fields may not apply as-is. iOS clients are also limited by their own network extension implementations. Don’t copy desktop TUN settings across every platform; use the VPN, DNS, and bypass options provided by each client.
Verify and troubleshoot: check resolution, interception, and connections separately
Step 1: Query the local DNS endpoint directly
After enabling the configuration, run the commands below on a system with dig installed. The first queries over UDP and the second over TCP, confirming whether the local DNS endpoint responds. Because they specify the server explicitly, they can’t by themselves prove that TUN is capturing system DNS.
dig @127.0.0.1 -p 1053 example.com A
dig @127.0.0.1 -p 1053 example.com A +tcp
On Windows, use the built-in nslookup and specify the test port:
nslookup -port=1053 example.com 127.0.0.1
In fake-ip mode, an ordinary A query that isn’t filtered usually returns an address from the 198.18.0.1/16 pool. This only confirms that the query matched a fake-IP mapping; it doesn’t prove that upstream resolution or proxy egress is working. Check excluded domains against their real-resolution path instead.
Step 2: Confirm the connection reaches the same kernel
- Open the client’s connections panel, visit an HTTPS site, and check whether the corresponding domain appears.
- Confirm that the expected rule matched, then check whether the final outbound is DIRECT or a node in a proxy group.
- If DNS returns a fake IP but no connection appears, check TUN, routing, and app bypass settings first.
- If the connection reaches the kernel but times out, check the node, rules, and upstream connection. Don’t keep switching DNS modes.
Step 3: Narrow down the cause by symptom
| Symptom | Check first | Next step |
|---|---|---|
| Local query to port 1053 times out | Whether DNS is enabled, the configuration loaded, and the port is in use | Check startup logs, confirm the listening address, and retry the query |
| TLS error in DoH logs | System time, certificates, upstream reachability, and bootstrap resolution | Correct the system time and test one upstream; don’t hide the problem by disabling certificate verification |
| Websites work, but NAS hostnames don’t | Internal DNS policy and fake-IP exclusions | Set the router DNS for internal domains and check whether they need real addresses |
| Results don’t change after editing the configuration | Client overrides, subscription updates, and app or system caches | Check the runtime configuration, reload it, and clear caches |
| Only the browser behaves differently | Browser Secure DNS, proxy extensions, and separate caches | Temporarily disable the relevant feature and test the same domain for comparison |
On Windows, run ipconfig /flushdns to clear the system DNS cache, but browsers and apps may still retain their own records. When sharing diagnostic logs, keep the timestamps, error types, and relevant fields, but remove subscription URLs, credentials, and sensitive internal domains.
Once you’ve confirmed which DNS changes work, put them in a supported client override or merge-config entry so a subscription refresh won’t remove them. Keep two records: a minimal working configuration and your current configuration. If something breaks, roll back first, then restore fallback, domain policies, and TUN interception one at a time. This makes the cause easier to pinpoint than changing every setting at once. For an overview of how configuration files and proxy rules fit together, see the complete guide.