Start by identifying the path your traffic takes
Starting mihomo on OpenWrt only confirms that the proxy core is running—it does not mean LAN devices are using transparent proxying. A complete traffic path has at least three parts: client devices send traffic to the side router, the side router passes eligible connections to the core, and the core selects a direct or proxy route according to its rules. DNS needs its own plan; changing the default gateway alone does not complete the setup.
mihomo is the proxy core that continues the development of Clash Meta; it is not a desktop client with a graphical interface. Router plugins typically handle downloading the core, generating configuration, creating firewall rules, and managing the service. When deploying the core directly, you need to handle these tasks yourself. Avoid layering another set of hand-written transparent proxy rules onto a device whose network is already managed by a plugin.
Test with one client first
Keep the main router's existing DHCP settings and change the gateway and DNS on just one test computer. Once the admin interface, LAN access, and proxy connection all work, consider rolling out the settings to the rest of the network. Keep a local management path available before making changes so you don't lose access by modifying the network over the proxy path you're currently changing.
Three common deployment topologies
| Deployment type | Traffic entry point | What to check |
|---|---|---|
| Run the core on the main router | OpenWrt is the clients' default gateway | WAN egress, forwarding zones, DNS, and route interception |
| Single-interface side router on the same subnet | Clients use the side router as their default gateway | Forwarding on the same interface, return path, and ICMP redirects |
| Side router on a separate subnet | Clients connect to the network behind the side router | Upstream static route or NAT, downstream DHCP, and cross-subnet access |
Using the main router keeps the traffic path straightforward but increases the impact of a failure. A single-interface side router on the same subnet makes staged testing easier, but can send outbound traffic through the side router while replies return directly to the client. A separate subnet provides a clear boundary but requires additional routing or NAT. Choose based on your existing network topology, not the number of ports on the device.
How to route gateway and DNS traffic
The following example addresses are for illustration only, not measured results: the main router is 192.168.10.1, the OpenWrt side router is 192.168.10.2, and the test client is 192.168.10.100. All use the subnet mask 255.255.255.0. The side router's default route still points to the main router, while the test client's default gateway points to the side router.
Test client 192.168.10.100
Default gateway: 192.168.10.2
DNS:192.168.10.2
↓
OpenWrt side router 192.168.10.2
Default gateway: 192.168.10.1
↓
Main router 192.168.10.1
↓
Internet
The gateway determines the traffic path; DNS determines where queries go
- Changing DNS only: Domain lookups may reach the side router, but application traffic can still go directly through the main router. That alone does not confirm that transparent proxying is working.
- Changing the gateway only: Application traffic may pass through the side router, while clients continue querying their original DNS server. Domain-based matching and resolution policies may not work as expected.
- Changing both: This makes it easier to establish a consistent test path, but you still need to confirm that forwarding is allowed on the side router and that traffic interception is actually in place.
In the common LuCI interface, check the static address under Network → Interfaces → LAN → Edit. The exact tabs for the default gateway and DNS vary by firmware version. When the main router handles DHCP, network-wide settings typically use DHCP options 3 (default gateway) and 6 (DNS servers). Don't run uncoordinated DHCP services on both routers in the same broadcast domain, or clients may receive different gateways.
For single-interface forwarding on the same subnet, also check ICMP redirects. Some routing behavior can tell clients to use the main router directly, bypassing the intended path. Whether to use source NAT depends on the return route and interception method; masquerading is not a universal fix. It changes the source address seen by the core or upstream, and can affect rules and logs that distinguish clients.
Check the system and core before deployment
Check the device architecture, available storage, and firewall stack before choosing a binary. OpenWrt package architecture names don't always match the names used in upstream releases, so don't select a build based only on the product name. For ARM devices, distinguish between 32-bit and 64-bit builds and check the specific instruction set. For x86 devices, confirm the CPU can run the selected build.
ubus call system board
uname -m
df -h /overlay
free
command -v fw4
command -v nft
ls -l /dev/net/tun
ip route show
ip -6 route show
In common official OpenWrt 23.05 and 24.10 setups, firewall4 typically manages the firewall with nftables. Third-party firmware may alter related components, so check the device itself. Commands from older iptables tutorials are not automatically suitable for current systems. In particular, don't have two rule managers handling the same connections.
Validate the configuration before starting the service
This article assumes the core is at /usr/bin/mihomo, its working directory is /etc/mihomo, and its configuration file is /etc/mihomo/config.yaml. These are the paths used here, not the default directories for every plugin. Place rule files, geodata, and caches according to your actual configuration; copying the YAML alone may leave runtime dependencies out.
/usr/bin/mihomo -v
/usr/bin/mihomo -t -d /etc/mihomo -f /etc/mihomo/config.yaml
Use -v to check the actual core version and -t to test the configuration. A successful test only confirms that the core accepts the configuration and its dependencies load; it doesn't confirm that client routing or firewall rules are correct. For initial troubleshooting, start the core in the foreground and check the logs before handing it over to OpenWrt's procd. If a plugin manages the core, use the plugin's service controls instead of starting a second process.
Establish a local proxy and DNS baseline
Test an explicit proxy before adding transparent proxying. The configuration below sets up local listeners and a direct-connection baseline; it includes no proxy nodes and won't route client traffic through a proxy. Merge it into a valid configuration rather than appending a second top-level field with the same name.
mixed-port: 7890
allow-lan: false
mode: rule
log-level: info
external-controller: 127.0.0.1:9090
dns:
enable: true
listen: 127.0.0.1:1053
enhanced-mode: redir-host
nameserver:
- 1.1.1.1
- 8.8.8.8
rules:
- MATCH,DIRECT
mixed-port: 7890: For explicit HTTP/SOCKS proxy connections, not a transparent proxy entry point. To let a test computer access this port, configure LAN listening separately and restrict access carefully.allow-lan: false: This baseline does not expose the explicit proxy service to the LAN. TUN interception of forwarded traffic is separate from opening this port.127.0.0.1:9090: The controller listens on the local device only. If you need remote management, configure authentication and access restrictions separately; don't expose it directly to the public internet.127.0.0.1:1053: DNS accepts requests forwarded from the local device only. Port53is not used here to avoid a direct conflict with dnsmasq's listener.MATCH,DIRECT: Send all unmatched connections directly to validate the traffic path first. To use a proxy egress, you still need valid nodes, proxy groups, and the relevant rules.
If the device has curl and nslookup, use them on the side router to test the explicit proxy and LAN DNS separately. The example domain is for connectivity checks only; its response time doesn't indicate proxy node quality.
curl --proxy http://127.0.0.1:7890 -I https://example.com
nslookup example.com 192.168.10.2
Choose a single DNS forwarding chain
One straightforward setup to verify is: clients query port 53 on the side router, and dnsmasq forwards requests to the local mihomo listener on port 1053. In common LuCI builds, go to Network → DHCP/DNS → General Settings → DNS Forwardings and enter 127.0.0.1#1053. Also check whether other DNS servers are still being obtained from upstream. To send all external queries through the core, adjust settings such as “Ignore resolv file” for your firmware and preserve any required local-domain handling.
Avoid DNS loops
Once dnsmasq forwards queries to mihomo, mihomo's upstream DNS must not point back to the same dnsmasq forwarding chain. The numeric upstream IP here is only an example, not a general recommendation. If you use encrypted DNS with a hostname, also account for bootstrap resolution of that hostname and decide whether upstream connections go direct or through the proxy.
Three ways to intercept traffic for transparent proxying
Transparent proxying means client apps don't need to be configured with proxy addresses one by one; it doesn't mean every protocol is automatically supported. Actual coverage depends on the interception method, core support, proxy protocol, and firewall rules. Check UDP, IPv6, and LAN destinations separately.
| Method | How it works | Deployment notes |
|---|---|---|
| REDIRECT | Firewall rules redirect connections to a transparent proxy listener | Commonly used for TCP; TCP redirect rules alone don't provide full UDP interception |
| TPROXY | Transparent sockets combined with traffic marks and policy routing | Can handle TCP and UDP, but requires matching kernel modules, routes, and firewall rules |
| TUN | A virtual layer 3 device passes IP traffic to the core | Requires TUN support and permissions; also verify that routes and LAN forwarding enter the interception path |
Choose one primary method before configuring it. Don't enable a plugin's TPROXY, custom REDIRECT rules, and another TUN auto-route setup at the same time in an attempt to “cover more.” Layering these methods can send connections through the core twice, cause mark conflicts, or create routing loops. The result is often that some sites open while other connections time out indefinitely.
TUN field overview
The snippet below applies to Linux mihomo builds that support the relevant fields. It illustrates how TUN interception works; it is not a one-click configuration for every OpenWrt firmware. Test it with the actual core and check its logs, generated rules, and routes. Older builds may not accept some of these fields.
tun:
enable: true
stack: mixed
auto-route: true
auto-redirect: true
strict-route: true
dns-hijack: []
enable: Enables TUN. The system needs an available TUN device and the required network management permissions.stack: mixed: Selects a mixed network stack; it does not mean multiple transparent proxy entry points are enabled.auto-route: Lets the core configure related automatic routes. It does not replace the gateway settings for LAN clients, or guarantee that all forwarding policies in the firmware allow traffic through.auto-redirect: In supported Linux environments, works with automatic routing to create the rules needed for interception. Check whether it is compatible with firewall4, existing plugins, and policy routing on the device.strict-route: Tightens interception routing behavior, but does not mean IPv6 and every bypass path have been audited.dns-hijack: []: This example does not hijack DNS through TUN. It keeps using the dnsmasq forwarding chain configured earlier to avoid layering two DNS interception methods during initial deployment.
If you switch to TUN-based DNS interception, review the query entry point, upstream resolution, and exclusion rules again. Don't assume adding any:53 is all it takes. Traditional UDP/TCP queries on port 53, app-based DoH, and private DNS on the operating system are separate paths; intercepting port 53 does not automatically cover encrypted DNS inside apps.
IPv6, return paths, and firewall boundaries
Successful IPv4 tests don't mean every connection from a dual-stack client passes through the side router. The main router may still advertise an IPv6 default route to clients, which can send traffic to sites with AAAA records along a different path. Changing only DHCPv4 options 3 and 6 won't control that IPv6 route.
- Check the IPv4 and IPv6 default routes on a test client first to confirm the actual next hop.
- Decide whether to intercept both stacks or initially limit testing to IPv4. Disabling IPv6 DNS results in mihomo alone does not disable IPv6 on the system.
- Check forwarding permissions from LAN to upstream, forwarding on the same interface, and the relevant firewall zones. Don't set input and forwarding to allow in every zone just to troubleshoot.
- Check how router management addresses, private network ranges, and connections from the proxy server itself bypass interception, so its egress traffic isn't sent back into the interception path.
Use nft list ruleset to inspect firewall rules, and ip rule show and ip route show table all to inspect policy routing. Interpret the output in the context of the existing plugins and firmware. Don't rely on a fixed table number or rule name to decide whether setup succeeded; names and priorities vary between implementations.
Verify each layer and plan a safe rollback
Troubleshoot from the entry point to the exit, checking each layer in order and changing one variable at a time. Opening a webpage only shows that some path works; it doesn't tell you whether the connection is direct, using an explicit proxy, or transparently proxied. Check client routes, core connection logs, and the policy that actually matched.
| Symptom | Check first | Next step |
|---|---|---|
| Client can't open the side router's admin page | Address, subnet mask, address conflicts, and input rules | Restore LAN management access first; don't continue changing transparent proxy settings |
| IP connections work but domain lookups fail | Listeners on 53 and 1053, dnsmasq forwarding, and DNS loops | Test the LAN DNS entry point and the core's upstream separately |
| Explicit proxy works on the router, but the client has no connection logs | Default gateway, IPv6, forwarding, and interception rules | Confirm that traffic actually reaches the side router |
| TCP works, but games or voice calls fail | UDP interception, node UDP support, and firewall rules | Retest with the relevant protocol instead of relying on a webpage test |
| Connections fail after the firewall reloads | Check whether automatic interception rules are still in place | Use the service that owns the rules to recreate them, then check service startup order |
Rollback order: client first, then service
If testing fails, restore the test client's default gateway to 192.168.10.1 and its DNS to the previous working settings. Then stop the newly added service and remove the interception rules it created. Also check that TUN routes are cleaned up when the service exits; deleting the binary alone is not enough. If you changed network-wide DHCP settings, restore the original values and renew client leases.
Once deployment is stable, configure procd startup, log rotation or size limits, and a configuration update process. Test each configuration update before applying it, and confirm that node names match their proxy group references before reloading the service. After changes involving the firewall or routes, recheck client DNS, TCP, UDP, IPv6, and LAN access. See the configuration reference for field details, and the installation and configuration guide for basic imports and client setup.