Side Router Topology: Trace the Traffic Path First

Running mihomo on OpenWrt involves three separate pieces: the core process, traffic interception, and DNS. The core reads config.yaml and selects an outbound based on rules. Linux routing and firewall rules determine which packets reach the core, while DNS determines how clients resolve addresses and whether domain names can be used for routing decisions. Starting the process handles only one part of the setup; it does not enable transparent proxying for your LAN.

This guide uses a single-interface side router on the same subnet as the main router. The addresses below illustrate the topology; replace them with addresses from your own network and check for conflicts before deploying.

DeviceExample addressRole
Main router192.168.10.1/24Connects to the ISP and provides DHCP
OpenWrt side router192.168.10.2/24Runs mihomo and handles forwarding and DNS
Test computer192.168.10.100/24Validate on its own first; don't change the entire network yet

Set the test computer's IPv4 default gateway to 192.168.10.2, while keeping the side router's default route pointed at the main router, 192.168.10.1. Do not point the two gateways at each other, which would create a routing loop. The side router must have IPv4 forwarding enabled, with forwarding allowed between the relevant interfaces or firewall zones. This is separate from allowing inbound connections to services running on the router itself.

Core Architecture and Configuration Paths

Check the CPU, OS, and Runtime Environment

First, use SSH to check the system details, then choose the matching Linux core build. x86_64 generally corresponds to amd64, and aarch64 to arm64. For 32-bit ARM, also check the instruction set and floating-point ABI. Don't rely on the router's product name alone, and don't assume that an OpenWrt package architecture name matches the filename used for core releases.

uname -m
ubus call system board
cat /etc/openwrt_release
df -h /overlay /tmp
free -m

When choosing an amd64 build, check the CPU instruction-set requirements in the release notes. If startup fails with Illegal instruction, check whether the build requires instructions the device doesn't support. For Exec format error, first check the architecture and file type. For Permission denied, check execute permissions and mount options rather than blaming the subscription.

Keep Persistent Files Separate from Temporary Logs

For a standalone setup, you can place the core at /usr/bin/mihomo, the configuration directory at /etc/mihomo, and the main configuration at /etc/mihomo/config.yaml. These are the paths used in this guide, not defaults shared by every OpenWrt management plugin. If you already use a management plugin, let it manage the process and firewall through a single control point to avoid starting a second instance.

  • /etc/mihomo/config.yaml: Stores the main configuration. Restrict read access if it contains subscription URLs or credentials.
  • /etc/mihomo/providers/: Stores provider files referenced by the configuration. The relevant fields determine their exact relative paths.
  • /tmp/: Suitable for temporary diagnostic files, which are usually lost after a reboot. Don't use it for your only copy of the configuration.
  • Rule sets and geodata: Prepare only what your configuration needs. Check available flash storage and memory before enabling data sources.
mkdir -p /etc/mihomo
chmod 0755 /usr/bin/mihomo
chmod 0600 /etc/mihomo/config.yaml
/usr/bin/mihomo -v
/usr/bin/mihomo -t -d /etc/mihomo -f /etc/mihomo/config.yaml

The commands above assume that the core and configuration are already in place. Use -t to validate the configuration; it doesn't confirm that nodes are reachable or that the firewall is intercepting traffic correctly. Once validation passes, run the core in the foreground and check the startup logs. For a persistent background service, use OpenWrt's procd rather than relying on a temporary job launched from an SSH session.

/usr/bin/mihomo -d /etc/mihomo -f /etc/mihomo/config.yaml

Test mixed-port Before Enabling Transparent Proxying

First, configure one test computer to use the proxy explicitly. This helps isolate subscription, outbound, and rule issues. The basic configuration below can be used to check that the listener is available. It includes no proxy nodes, and its final rules send all traffic direct; use it only to verify the process, port, and request path. To test proxy outbounds, use a valid configuration and check the names referenced by proxies, proxy-providers, proxy-groups, and rules.

mixed-port: 7890
allow-lan: true
bind-address: "192.168.10.2"
mode: rule
log-level: info
ipv6: false

rules:
  - MATCH,DIRECT

mixed-port: 7890 provides HTTP and SOCKS proxy access. allow-lan: true allows other devices to connect, while bind-address limits the listening address; that address must already be configured on the side router. In the OpenWrt firewall, allow TCP 7890 only from trusted LAN devices. Never expose the proxy port to the WAN.

Run the following request from the test computer. With the basic configuration, it should succeed using a direct connection. After switching to valid nodes and rules, check mihomo logs to confirm the actual outbound. A single successful HTTP request does not prove that UDP, DNS, or transparent proxying for the whole network is working.

curl -I --proxy http://192.168.10.2:7890 https://example.com/

If this test fails, check the listening address, firewall input rules, and core logs before changing TProxy settings. If the explicit proxy works on the computer but connections fail after changing its gateway, focus on forwarding, firewall interception, policy routing, and DNS.

Redir, TProxy, and TUN Compared

Methodmihomo optionTypical useRequired system setup
Redirredir-port: 7892Transparent TCP redirection on LinuxFirewall REDIRECT rules and traffic exclusions
TProxytproxy-port: 7893Transparent TCP and UDP proxying on LinuxTPROXY rules, packet marking, and policy routing
TUNtun.enable: trueReceives IP traffic through a virtual network interfaceTUN support, with compatible routing and firewall rules

Redir: A Listening Port Doesn't Intercept Traffic by Itself

redir-port is an entry point for transparent proxying, not an HTTP proxy port for apps to use. On Linux, Redir is commonly used for TCP; that alone does not mean UDP is proxied. With TCP redirection only, QUIC, gaming, or voice traffic over UDP may go direct or fail, depending on the remaining firewall rules.

TProxy: Policy Routing Must Deliver Packets to the Local Host

TProxy can handle TCP and UDP while preserving the original destination. To deploy it, mark the selected traffic, then use ip rule and the corresponding routing table to deliver it to a local transparent listener. Setting tproxy-port: 7893 without the necessary system rules won't route client traffic into mihomo.

The firewall must also exclude LAN, broadcast, multicast, required local services, and the core's own outbound connections to prevent proxy loops. Choose packet marks and routing table IDs that don't conflict with existing VPN, policy-routing, or multi-WAN rules. Don't assume a set of numbers copied from another device will work on yours.

TUN: An Alternative Traffic-Interception Method, Not an Add-On Switch

TUN requires a working system TUN device and the necessary permissions. tun.auto-route configures routes automatically; on supported Linux systems, tun.auto-redirect also involves firewall interception. Don't blindly combine these settings with transparent proxy rules already managed by a plugin. Choose one primary interception method for the side router, then configure exclusions and a rollback plan for that method.

DNS Forwarding: Give dnsmasq and mihomo Separate Listening Ports

OpenWrt commonly uses dnsmasq to provide DNS on port 53 to LAN clients. If mihomo also binds to port 53 on the same address, the two services will conflict. One easy-to-check setup is for clients to query the side router on port 53, with dnsmasq forwarding requests to mihomo on localhost port 1053.

dns:
  enable: true
  listen: 127.0.0.1:1053
  enhanced-mode: redir-host
  nameserver:
    - 223.5.5.5
    - 119.29.29.29

This configuration illustrates local forwarding; the upstreams shown are regular DNS servers and may not work on every network. You can point dnsmasq upstream to 127.0.0.1#1053. Also check whether it continues to use other upstream servers, which could bypass the intended path. Don't point mihomo's upstream back to dnsmasq on port 53, or DNS queries will loop.

Match redir-host and fake-ip to Your Interception Method

redir-host returns real addresses to clients, while fake-ip returns mapped addresses and lets the core restore the domain association when it intercepts a connection. With fake-ip, clients receiving mapped addresses must route subsequent connections back through mihomo. If you switch DNS before transparent interception is ready, domains may resolve successfully while all connections fail.

  • Test the DNS path with one client first, and confirm its queries appear in the logs before expanding the setup.
  • If you use encrypted DNS upstreams specified by hostname, configure bootstrap resolution for those upstream hostnames as needed.
  • Private DNS on clients and Secure DNS in browsers may bypass the server provided by DHCP. Check each setting separately.
  • After changing DNS or fake-ip mode, clear the test client's DNS cache and reconnect to avoid stale results skewing your tests.

Gateway Routing, Return Paths, and IPv6

Change One Client First, Then Update DHCP

In LuCI, open Network → Interfaces and confirm that the side router's LAN address is 192.168.10.2 and its IPv4 gateway is 192.168.10.1. Interface labels may vary by version or management plugin; use the interface address and routing table as the source of truth. First set the test computer's gateway and DNS manually to the side router, and consider updating DHCP for the whole network only after testing succeeds.

If the main router still provides DHCP, configure it to advertise option 3 as 192.168.10.2 and option 6 as 192.168.10.2, for the IPv4 gateway and DNS server respectively. Don't run two unplanned DHCP servers on the same broadcast domain. After making changes, renew client leases; existing leases won't all update immediately.

Same-Subnet Return Traffic May Bypass the Side Router

In a single-interface side-router setup, traffic sent from the side router to the main router may get a reply sent directly back to the client, creating an asymmetric path. Some deployments use source NAT from the side router toward the main router so replies return through the side router, but this hides the client's source address. Whether SNAT or masquerading is needed depends on the topology, connection tracking, and interception method. There is no universal rule to enable it everywhere.

For same-subnet forwarding, also check for ICMP Redirects: clients may receive a hint to use a more direct path, changing where subsequent traffic goes. If traffic passes through the side router at first but later bypasses it, check the client's route cache and the relevant kernel settings on the side router.

An IPv4 Gateway Does Not Intercept IPv6

An IPv6 default route advertised by the main router through RA may still be active. Setting ipv6: false in the configuration does not disable IPv6 across the entire LAN. To proxy IPv6, plan IPv6 routing, firewall rules, and DNS separately. If you haven't set that up yet, limit IPv6 use on the test client and test IPv4 and IPv6 independently so bypassed traffic isn't mistaken for a broken rule.

Validation Steps and Rollback Checks

Troubleshoot in this order: the router itself, explicit proxy, transparent proxying on one client, then network-wide DHCP settings. Change one layer at a time and record the gateway, DNS, and firewall state before each change. Being able to open the management interface proves only that the local connection to the side router works; it doesn't prove that internet forwarding is working.

  1. Check the process and configuration. Run mihomo -t and watch for startup errors in the foreground. Make sure no second instance is using ports 7890, 7892, 7893, or 1053.
  2. Check the side router's internet connection. Run ip -4 route and verify the default gateway. Confirm that the system clock and DNS work so HTTPS requests don't fail because of an incorrect system time.
  3. Check the explicit proxy. Run the earlier request with curl --proxy and inspect the logs for the selected rule and actual outbound, rather than checking only whether the page loads.
  4. Check transparent interception. Turn off the explicit proxy on the test client and set its gateway to the side router. Check ip rule show, ip route show table all, and the firewall rule counters.
  5. Check DNS and protocol coverage. Run nslookup example.com 192.168.10.2. After TCP works, test the UDP apps you need, then test IPv6 separately.

Once the core is running as a service, use procd to manage startup, crashes, and logs, and make sure its network and file dependencies are ready. Rule updates, provider refreshes, and subscription downloads can generate additional network requests; check that these requests aren't mistakenly intercepted again. On devices with limited flash storage, avoid retaining high-volume debug logs.

A side-router deployment isn't validated by a single switch showing “Running.” The gateway, DNS forwarding, transparent proxy entry point, and outbound rules must each work together, layer by layer. For configuration field details, see the configuration reference. To test the same subscription on a computer first, start with a client, confirm the configuration works, then move it to OpenWrt.