mihomo Configuration Reference

Advanced Clash Configuration Guide

Starting with proxy groups, rule providers, and DNS, this guide breaks down TUN, Fake-IP, sniffing, local overrides, multi-subscription merging, and external controllers. The focus is not on collecting parameters, but on showing how they work together to determine where each connection goes.

If installation, subscription import, and the first connection are not complete, follow the Getting Started guide first. This page is for users who can already use the client normally and need to maintain configurations over time. Client packages and platform differences are covered on the downloads page, while common startup errors can be located quickly in Frequently Asked Questions.

mihomo core YAML configuration Rules and DNS coordination Desktop and router use cases
CHAPTER 01

Proxy group types and practical combinations

Start by separating “choosing a node” from “making a decision”

Proxy groups sit between rules and proxy nodes. Rules only hand a connection to a named policy; the proxy group decides which route to use. Mixing these roles often leads to long lists of repeated node names, forcing rule changes whenever nodes change. A more maintainable structure is to create stable groups by purpose, such as “Node Selection,” “Auto Select,” “Streaming,” “Messaging,” and “Final,” then reference those names from rules while subscriptions and proxy groups handle node changes.

select is a manual selection group for situations where you need explicit control over the exit. In the client interface, users can choose a node, another proxy group, or DIRECT; the choice remains until the configuration is reloaded or its persisted state changes. It does not actively test link quality, so it works best as a top-level entry point containing “Auto Select,” “Failover,” and regional groups below it. This preserves automation while allowing quick switching when a particular website has compatibility issues.

url-test periodically tests connectivity to a specified URL and selects a node with a better result. It suits web browsing, software updates, and other latency-sensitive traffic where connections can be re-established. The result reflects connectivity to the test target, not the real speed of every website; a node may reach the test URL quickly while taking a longer route to the actual service. Do not set the interval too short or treat one test as a permanent verdict on link quality.

fallback selects the first available item in list order. It prioritizes stable ordering rather than chasing the lowest latency every time, making it suitable when a fixed primary route should hand over to a backup only on failure. load-balance distributes separate connections across multiple nodes, which can help with many concurrent independent requests. However, seeing multiple exit addresses can affect login state or trigger risk controls. Banking, account management, and persistent sessions should not be casually assigned to a load-balancing group.

Type Decision method Best for Main limitation
select User-selected Top-level exits and regional switching Does not proactively avoid failed nodes
url-test Selects the best test result Web browsing, updates, and general apps The test target cannot represent every service
fallback Uses the first available item in order Primary and backup routes with fixed priority Existing connections may need to be rebuilt after switching
load-balance Distributes connections according to policy Concurrent downloads and independent requests Multiple exits can affect session consistency

Use providers to manage dynamic nodes

Subscriptions frequently add and remove nodes. Writing a complete node list inside every proxy group quickly makes a configuration hard to maintain. mihomo can reference proxy-providers with use, then filter nodes by name with filter or exclude-filter. These expressions process names only and do not verify a node’s actual location, so they should be based on stable naming conventions in the subscription. If the provider changes names often, prefer one automatic group containing all nodes and add only a few exceptional routes manually to dedicated groups.

proxy-providers:
  primary:
    type: http
    url: "https://example.com/subscription"
    path: ./providers/primary.yaml
    interval: 86400
    health-check:
      enable: true
      url: https://www.gstatic.com/generate_204
      interval: 600

proxy-groups:
  - name: Node Selection
    type: select
    proxies:
      - Auto Select
      - Failover
      - DIRECT

  - name: Auto Select
    type: url-test
    use:
      - primary
    url: https://www.gstatic.com/generate_204
    interval: 600
    tolerance: 80

  - name: Failover
    type: fallback
    use:
      - primary
    url: https://www.gstatic.com/generate_204
    interval: 600

tolerance reduces unnecessary switching caused by small fluctuations. When two routes differ by only a few milliseconds, constantly changing the exit can be worse than keeping the current route. The test URL should return a lightweight, stable, accessible response. If the target is routed directly, resolved incorrectly by DNS, or unreachable from the current network, the group’s health status will be misleading. During troubleshooting, first confirm which path the test request actually takes, then decide whether the node is truly unavailable.

Proxy group order also affects usability. Place the top-level manual group where rules reference it directly, and use regional and automatic groups as lower-level capabilities without creating circular references. For example, if group A contains B and B contains A, the core cannot produce a valid exit. After editing, confirm on the client’s proxy page that every group has selectable options, then inspect the connection log’s three-part chain: “rule name — proxy group — actual node.” For a deeper explanation of top-to-bottom rule matching, read Custom Rule Syntax and Priority Explained.

CHAPTER 02

Managing rule subscriptions with providers

From individual rules to rule providers

A small number of custom rules can go directly in rules, but once domains, networks, and application categories reach hundreds of entries, the main configuration becomes difficult to review. Rule providers separate this content into independent files that rule-providers downloads, caches, and updates periodically, leaving only the reference order in the main rule list. This allows advertising filters, private networks, specific services, and regional network rules to be maintained separately and disabled independently when a source has problems, without replacing the entire subscription.

A provider’s behavior determines how its file is interpreted. domain is for domain collections and works well for plain domain categories; ipcidr is for IPv4 and IPv6 networks; classical accepts complete typed rules such as DOMAIN-SUFFIX, PROCESS-NAME, and IP-CIDR. They are not interchangeable. If a file declared as domain contains full rule syntax, loading may fail with a format error or the entries may not match as expected.

format is commonly yaml, text, or a binary rule format. YAML is easy to review manually, while text suits simple one-rule-per-line sources. Whichever format you choose, the declaration must match the remote file’s actual contents. path is the local cache location; different providers should not share one file or the later download will overwrite the earlier one. Desktop clients usually manage the configuration directory, so relative paths are easier to migrate than hard-coded user directories.

rule-providers:
  private-network:
    type: http
    behavior: classical
    format: yaml
    path: ./rules/private-network.yaml
    url: "https://example.com/rules/private-network.yaml"
    interval: 86400

  service-domains:
    type: http
    behavior: domain
    format: yaml
    path: ./rules/service-domains.yaml
    url: "https://example.com/rules/service-domains.yaml"
    interval: 86400

  regional-cidr:
    type: http
    behavior: ipcidr
    format: yaml
    path: ./rules/regional-cidr.yaml
    url: "https://example.com/rules/regional-cidr.yaml"
    interval: 86400

rules:
  - RULE-SET,private-network,DIRECT
  - RULE-SET,service-domains,Node Selection
  - RULE-SET,regional-cidr,DIRECT,no-resolve
  - MATCH,Final

Order, parsing, and no-resolve

Clash evaluates rules from top to bottom and stops after the first match. So “the rule provider contains this domain” does not mean it will be used: if an earlier rule matches, the later provider never runs. In general, put narrow, intentional local rules first, business rule providers in the middle, and regional network ranges and catch-all rules last. When temporarily fixing a website, add a clear rule near the top of the main configuration instead of immediately editing a large remote collection.

IP-based rules may trigger DNS resolution. When a connection initially contains only a domain and the rule engine must determine whether the destination IP belongs to a network range, the core needs to obtain an address first. no-resolve tells that rule not to initiate resolution for matching, making it useful for IP rules placed after domain rules to reduce extra DNS queries and prevent resolution from changing the original decision. If the traffic entry contains only an IP, the IP rule can still match directly; no-resolve does not disable the rule.

When a remote rule provider is unavailable, distinguish between an update failure and an unusable local cache. If a valid cache exists, a temporary network outage usually will not make every rule disappear immediately. First load, a damaged cache file, or a format change can prevent the provider from becoming ready. Check the provider name, HTTP status, parse errors, and local path in the logs instead of looking only at the final connection failure. Use a stable HTTPS URL and set the interval according to the source’s update frequency. A collection that changes once a day does not need to be downloaded every few minutes.

Symptom Check first Action
Rule provider shown as unavailable URL, network, and cache directory permissions Update it manually and inspect the provider log
Rule exists but does not match Main rule order and behavior type Use the connection log to identify the earlier matching rule
Many domains fail after an update File format and content structure Restore the cached version and validate the new file separately
DNS requests suddenly increase Whether IP rules are triggering resolution Reorder domain rules and evaluate no-resolve

Using rule providers does not mean every rule should be maintained by a third party. LAN domains, home servers, company test environments, and personal exceptions are local facts and are best kept in your own override file. Public providers handle broad classification; local rules provide precise corrections. Before replacing a major rule source, retain the old cache and a copy of the main configuration, then test clear direct, proxied, and rejected outcomes. Migration is complete only when every test connection’s route to its current exit can be explained.

CHAPTER 03

DNS configuration and resolution-path optimization

Understand where requests originate

The hard part of DNS configuration is not adding more servers, but knowing who makes each query, which network path it takes, and how the result enters rule evaluation. System applications may ask the operating system’s DNS, or use encrypted DNS themselves. With TUN and DNS hijacking enabled, ordinary port 53 queries can be handled by mihomo, while an app’s built-in encrypted resolver may still bypass it. When a browser works but another app does not, first compare the resolution methods they actually use instead of repeatedly changing nodes.

nameserver is the primary upstream for ordinary domain resolution. default-nameserver resolves the domains of DoH or DoT upstreams themselves, so it should normally contain directly reachable IP addresses. Otherwise, a loop can occur: resolving a DNS upstream’s hostname requires an upstream that has not connected yet. proxy-server-nameserver can resolve proxy server addresses separately, which is useful when a node hostname needs an IP through a stable resolution path.

nameserver-policy routes queries to specific upstreams by domain. For example, internal domains can use LAN DNS while a particular service uses another resolver group. It answers “which resolver handles this domain,” not “which proxy group handles the resulting connection”; rules still decide the latter. An overly broad policy can send many domains around the primary nameserver. After editing, test policy-matched domains, ordinary domains, and node domains separately to confirm all three paths work independently.

dns:
  enable: true
  listen: 0.0.0.0:1053
  ipv6: true
  enhanced-mode: fake-ip
  fake-ip-range: 198.18.0.1/16
  default-nameserver:
    - 223.5.5.5
    - 1.1.1.1
  nameserver:
    - https://dns.alidns.com/dns-query
    - https://cloudflare-dns.com/dns-query
  proxy-server-nameserver:
    - https://dns.alidns.com/dns-query
  nameserver-policy:
    "+.lan":
      - 192.168.1.1
    "+.internal.example":
      - 192.168.1.1
  fake-ip-filter:
    - "*.lan"
    - "*.local"
    - "time.*.com"
    - "time.*.gov"

More upstreams are not a simple numbers game

Adding upstreams does not automatically improve reliability. Different resolvers may return different CDN addresses, IPv6 results, or regional routes, making the same domain behave inconsistently within a short period. More importantly, their query paths may differ. The fastest answer may not suit the final exit: a direct DNS result may fit the local network, while the connection is later established through a remote node and reaches a CDN that is not optimal. First decide the primary exit, then choose upstreams that match the network model.

ipv6 controls whether the DNS module returns AAAA results, but the system’s ability to connect over IPv6 also depends on the physical network, TUN stack, routes, and proxy node. Returning IPv6 addresses without a working IPv6 path may make an app try an incomplete connection before falling back to IPv4, slowing the initial load. Disabling IPv6 is a troubleshooting measure, not a substitute for assessing link capability. If the broadband connection, server, and node support IPv6, keep it enabled and test direct and proxied results separately.

Resolution caching reduces repeated requests but can also keep incorrect results alive. After changing hosts, nameserver-policy, or fake-ip-filter, old entries may still be used by the app, operating system, or core. A standard troubleshooting sequence is to reload the configuration, clear the client’s DNS cache, clear the OS cache if needed, and restart the target app. Refreshing a page alone rarely clears every cache layer. Browsers may also keep their own connection pools and DNS cache, so a new window or full restart is more reliable.

Filtering local domains and special services

In Fake-IP mode, some device discovery, LAN services, time synchronization, and apps that validate real addresses are not suitable for mapped addresses and should be added to fake-ip-filter. Keep the filter as narrow as possible. An overly broad wildcard sends many domains back to real-IP mode and weakens the stability of domain rules. For every exception, record the symptom it fixes—such as casting not finding a device, a LAN hostname being unreachable, or a time service rejecting the response—so its necessity can be reassessed later.

The DNS listen address also has boundaries. When a desktop client is for local use only, there is no need to expose its port to the entire LAN. When a router serves other devices as a gateway, it must listen on a reachable address and use firewall rules to restrict sources. A successful bind does not prove the system is using it; still check system DNS, TUN interception, and port occupancy. If startup logs report a bind error, follow the port-conflict troubleshooting process to identify the existing process before changing the listen port.

CHAPTER 04

Coordinating TUN mode and Fake-IP

TUN covers connections that system proxy settings miss

System proxy settings work only for apps that actively read them. Command-line programs, games, some app-store applications, and software that sends UDP directly may ignore them completely. TUN creates a virtual network interface and captures traffic routed through the system at the IP layer, giving it broader coverage. It is not simply a “stronger global mode”: rule mode, global mode, and direct mode determine how captured traffic is split, while TUN only sends otherwise unseen connections into the core.

Enabling TUN usually requires administrator privileges or system authorization. Platforms implement virtual interfaces, routing tables, and DNS settings differently. Clients try to configure them automatically, but sleep recovery, network changes, VPN coexistence, and security software can leave stale routes behind. If enabling TUN cuts off all connectivity, disable it first and confirm the base network recovers, then inspect interface creation, route insertion, and DNS interception errors in the logs. Avoid switching several related toggles repeatedly, or it will be difficult to tell which step changed the system state.

auto-route lets the core write routes automatically and suits ordinary desktop environments. auto-detect-interface identifies the current default exit so proxy traffic is not sent back into TUN and looped. With multiple network adapters, virtual machines, hotspot sharing, or wired and wireless connections at once, automatic detection may choose the wrong interface. Check the routing table and the actual interface in the logs instead of guessing from its name. strict-route restricts bypass traffic more aggressively, which can reduce leaks but may also affect LAN access and other virtual networks.

tun:
  enable: true
  stack: mixed
  dns-hijack:
    - any:53
    - tcp://any:53
  auto-route: true
  auto-detect-interface: true
  strict-route: false
  mtu: 1500

dns:
  enable: true
  enhanced-mode: fake-ip
  fake-ip-range: 198.18.0.1/16

Preserving domain semantics with Fake-IP

Traditional redir-host mode obtains the real IP first and then passes the connection to the rule engine. When several domains share a CDN address, the original destination is difficult to identify from the IP alone. Fake-IP assigns each queried domain a mapped address from a reserved range. When the app connects to that address, the core can look up the original domain, then apply domain rules and remote resolution. Its main value is preserving domain semantics, not accelerating every DNS query.

Mapped addresses are meaningful only inside the running core and its cache. If an app connects to an address such as 198.18.0.0/16, that does not mean the destination is actually on that network. If DNS queries bypass mihomo but the app receives a previous mapped address, or the connection is not captured by TUN, the result can be “resolves but cannot connect.” Fake-IP, DNS interception, and traffic capture must therefore be checked as one chain. Enabling enhanced-mode while the system continues using another DNS resolver does not produce a complete setup.

UDP is an often-missed layer when troubleshooting TUN. Voice, games, QUIC, and some DNS queries rely on UDP, and the node protocol, client settings, and destination service must all support the path. A working webpage proves only that some TCP traffic works; it does not prove the TUN UDP path is complete. Test ordinary TCP first, then DNS over UDP, QUIC, or the actual app. If only UDP fails, inspect node capabilities, rule policy, and the system firewall instead of immediately rejecting the entire TUN configuration.

Combination Domain-rule capability Typical use Considerations
System proxy + conventional DNS Depends on whether the app submits a domain or an IP Browsers and conventional desktop software Apps that ignore system proxy settings are not covered
TUN + redir-host Adds decisions based on real resolution results Compatibility cases requiring real IPs Shared IPs can reduce domain-identification accuracy
TUN + Fake-IP Preserves the queried domain for consistent matching Rule-based routing and whole-device capture DNS and connections must enter the core together

MTU, LAN access, and other tunnels

On some networks, small requests work while uploads, video, or large pages stall; MTU and fragmentation may be involved. TUN encapsulation adds overhead, and if the underlying network cannot carry packets of the required size, connections may stall under specific loads. Adjust MTU gradually and verify with repeatable large-file requests rather than blaming every interruption on MTU. If the issue appears only with one node, also compare the overhead of its protocol and transport layer.

When LAN access fails, first confirm that private-network rules appear early and point to DIRECT, then check strict-route, the firewall, and whether the target device permits access from the current interface. When another VPN runs at the same time, both programs may modify the default route and DNS, with the result determined by route priority. The most reliable test is to enable Clash alone, then restore other tunnels one at a time. If coexistence is required, define which interface owns which networks instead of relying on startup order.

CHAPTER 05

Domain sniffing and destination recovery

Sniffing handles connections that expose only an IP

The rule engine works best with a domain because it usually expresses the service more clearly than a shared IP. Some connections enter the core with only a destination IP because an app uses its own DNS, the system cached a real address, or the transparent-proxy stage carried no domain. Domain sniffing reads protocol information visible at the start of a connection and recovers the domain from fields such as HTTP Host and TLS SNI, then passes it to domain-rule evaluation. It restores missing information; it does not replace DNS.

Sniffing is limited by the protocol itself. The Host header in plain HTTP is usually visible, and SNI in a TLS handshake commonly provides a domain. If an app uses a connection method without a domain, the protocol cannot be recognized, or encryption hides the handshake, sniffing cannot recover it. UDP protocols also differ in visibility, so do not assume every flow yields a domain. Keep reasonable IP rules and fallback policies in the configuration.

override-destination determines whether a recognized domain replaces the original destination during connection handling. Enabling it helps domain rules and remote resolution take effect, but can cause compatibility issues for apps that rely on a fixed IP, unusual certificate behavior, or an intentional mismatch between the domain and connection address. A safer approach is to enable it for common ports first, then create narrow exceptions with skip-domain or by excluding known-problem applications by source.

sniffer:
  enable: true
  force-dns-mapping: true
  parse-pure-ip: true
  override-destination: true
  sniff:
    HTTP:
      ports:
        - 80
        - 8080-8880
      override-destination: true
    TLS:
      ports:
        - 443
        - 8443
    QUIC:
      ports:
        - 443
  skip-domain:
    - "Mijia Cloud"
    - "+.push.apple.com"

force-dns-mapping and parse-pure-ip

force-dns-mapping makes the sniffer pay attention to connections associated with DNS mappings and suits Fake-IP paths. parse-pure-ip allows attempts to analyze traffic whose destination appears as a bare IP. Enabling both broadens coverage and sends more connections through inspection. Modern devices can usually handle the overhead, but resource-constrained routers should be monitored for connection count, CPU use, and log volume. Do not enable every option simply because it exists.

The port range is an important boundary for sniffing accuracy. If HTTP sniffing covers every port, initial data from non-HTTP protocols may be repeatedly parsed, increasing overhead and the chance of false positives. Start with explicit ports such as 80, 8080, and common web ports; TLS is usually concentrated on 443 and a few custom ports. Add unusual ports only when connection logs show an application needs them. The more specific the configuration, the easier it is to explain a match later.

Both sniffing and Fake-IP can restore a domain, but they activate at different stages. Fake-IP creates a mapping during the DNS query, after which the app connects to the mapped address. Sniffing extracts information from the protocol handshake after the connection has arrived. The former is usually more stable; the latter is an important supplement for connections that bypass core DNS, use real IPs, or pass through a transparent proxy. If both mapped and sniffed domains exist for one connection, check which one the log ultimately uses, especially with CDN redirects or certificates using generic domains.

Do not disable all sniffing at the first sign of a false match

When an app behaves unexpectedly after sniffing is enabled, isolate the specific connection first. Check the original destination IP, recovered domain, matched rule, and final policy in the log. If the recovered domain clearly does not belong to the intended service, add it to the skip list or narrow the protocol’s port range. If only one LAN service is affected, make an exception for its domain or network. Disabling sniffing globally sends other domain-dependent connections back to IP rules and can introduce more subtle routing changes.

Use domains with unambiguous rule outcomes when testing sniffing. Prepare one service that should go direct and one that should use a proxy, clear the app’s connection cache, visit both, and check whether the connection details show domains. If only IPs appear, verify that traffic passes through TUN, the protocol is recognizable, and the port is within the sniff range. If the domain appears but the policy is wrong, the issue is rule order or the proxy group, not sniffing. Separating “identify the destination” from “choose the exit” prevents repeated changes at the wrong layer.

Domain sniffing cannot fix incorrect DNS. If an app already received an unreachable address before connecting, sniffing may identify the domain, but routing, certificates, or the destination service may still be affected. A stable setup normally relies on correct DNS interception, uses sniffing to supplement real-IP connections, and uses IP rules for traffic whose domain cannot be recovered. Each layer should solve one problem; together they form a complete decision chain.

CHAPTER 06

Local overrides and multiple subscriptions

Treat upstream configuration as an updateable input

Subscription configurations are maintained by providers and may replace proxy nodes, groups, and rules during updates. Editing the generated subscription file directly usually means losing those changes at the next update. Treat the subscription as input and put local requirements in a separate override layer: the subscription supplies nodes and base structure, while the local layer handles ports, DNS, TUN, rule priority, and dedicated groups. Graphical clients such as Clash Plus commonly provide configuration overrides, scripts, or merge entry points. Names vary, but the goal is repeatable application of local changes.

Overrides have two semantics: replacement and merge. Scalar fields such as mixed-port and mode are usually replaced directly. Mapping fields such as dns may merge by key or be replaced as a whole. Array fields such as rules and proxy-groups are the most ambiguous. Some clients append new arrays, some merge by name, and others replace them entirely. Before using any merge script, export the final configuration and confirm the actual result instead of checking only the input fragments.

Rule arrays especially require a clear distinction between prepending and appending. A custom direct rule placed after MATCH will never take effect; a correction for a specific service should usually be inserted before a broad remote rule. If the merge tool supports prepend and append, choose deliberately. When proxy groups merge by name, avoid giving a local group the same name as an upstream group with a different type, or an update may preserve stale fields and create a confusing hybrid structure.

# Local override example: the specific merge entry point is provided by the client
mixed-port: 7890
allow-lan: false
mode: rule
log-level: info

dns:
  enable: true
  enhanced-mode: fake-ip

tun:
  enable: true
  stack: mixed
  auto-route: true
  auto-detect-interface: true

rules:
  - DOMAIN-SUFFIX,internal.example,DIRECT
  - IP-CIDR,192.168.0.0/16,DIRECT,no-resolve

Multiple subscriptions are not simple concatenation

When merging subscriptions, name conflicts appear first. Different sources may all contain “Auto Select,” “Node Selection,” or identical node names. If the client deduplicates by name, later entries may overwrite earlier ones; without deduplication, the interface fills with indistinguishable duplicates. Keep source-specific names at the provider layer, then create your own unified proxy groups using use to reference multiple providers. This avoids expanding every node into a static array and lets you pause one source independently.

The second issue is the update schedule. If several subscriptions update frequently at the same time, they increase request volume and may interrupt existing connections during configuration reloads. Node lists rarely need minute-level refreshes. Set providers to longer intervals and use manual updates when a provider announces a change or a node needs verification. Health checks and subscription downloads are different: the former tests existing nodes, while the latter retrieves a new node list. Do not use an extremely short subscription interval as a substitute for health checks.

The third issue is differences in provider capabilities. Some nodes support UDP and others do not; some routes suit fixed-region services while others are better for general browsing. A single automatic group containing every node may choose an exit that does not meet the application’s needs. Build dedicated groups by stable names or source, such as “Source A Auto” and “Source B Backup,” then have the top-level “Node Selection” group reference them. If names cannot reliably describe capabilities, maintain groups through real connection tests and manual classification.

Merge target Recommended approach Common risk
Ports and operating mode Override local scalar values Port conflicts and changes lost after client reload
DNS and TUN Maintain the complete module locally Conflicting semantics after partial merges
Rules Define explicit prepend, append, and fallback positions Custom rules placed after MATCH
Multiple node subscriptions Create separate proxy providers Duplicate node names and updates overwriting one another
Proxy groups Build a stable local business layer Same upstream name with a different type

Create a reversible change process

Keep the last working final configuration before every change, not just the override fragment. The final configuration reflects the real merged order and makes changes easier to compare. Adjust one module at a time: proxy groups first, then rules, DNS, and finally TUN and sniffing. At each step, verify that the configuration loads, a basic webpage is reachable, and one direct target and one proxied target behave as expected. Changing several things at once may save clicks, but makes failures difficult to isolate.

If problems appear after a subscription update, first check whether the provider succeeded, whether proxy groups are empty, and whether names referenced by rules still exist. Many failures are not caused by dead nodes but by an upstream rename that leaves local rules pointing to an old policy name. The core usually reports a missing policy or provider during loading; if the client only says “configuration failed,” open detailed logs and look for the exact key name. Do not immediately delete every local override just to restore connectivity, or you will lose the most useful clues about what changed.

Keep local overrides short and explainable. Add comments by module to document the specific issue each rule solves, but remove experimental parameters that no longer matter. Review exceptions periodically: confirm the service still exists, check whether a remote provider now covers it, and remove entries tied to an old network environment. The goal of configuration maintenance is not more fields, but a clear purpose and boundary for every field retained.

CHAPTER 07

External controllers and API boundaries

What the controller port can do

mihomo’s external controller lets graphical clients and web panels read proxy groups, switch nodes, inspect connections, trigger provider updates, and adjust parts of the runtime state. The proxy page, connection list, and log window in desktop clients often rely on this interface. It is a control plane, not a proxy entry point: mixed-port, the HTTP port, and the SOCKS port forward traffic, while external-controller handles management requests only.

For local-only use, bind the controller address to the loopback interface. With 127.0.0.1, other LAN devices cannot access it directly. If you need to manage a core on a router from another device, listen on a LAN address, but also set credentials, restrict firewall sources, and never map the port to the public internet. The management interface can inspect destinations and change exits, so its privileges should be treated as equivalent to local administrator access.

external-controller: 127.0.0.1:9090
secret: "your-password"

# Optional: let the core serve local controller-panel files
external-ui: ./ui

When secret is empty, a program that can reach the controller port may call the API without authentication. Even on the local loopback interface, consider access from other processes on the same machine; LAN listeners must have credentials. The value in the example is for teaching only. Replace it with a unique credential in real use, and do not share it with a subscription URL, system account, or another service. If the panel cannot connect, check that the address, port, protocol, and credential match exactly.

external-ui points to the directory containing static panel files. After the core serves them, the interface can be opened through the controller address; you can also use a client’s built-in panel. The panel files and core API must be compatible. If the page loads but proxy groups are empty, inspect browser network requests and controller responses before downloading the node subscription again. Successful static-file loading proves only that web resources are reachable, not that API authentication succeeded.

LAN access and reverse proxies

On a home server or router, a common design is to bind the controller port locally and expose an HTTPS entry point through a controlled reverse proxy. This centralizes access control and certificates while avoiding direct exposure of the management port. A misconfigured reverse proxy can drop WebSocket connections or authentication headers, leaving the panel home page working while the connection list stops updating. Test the static page, a regular API request, and the real-time channel separately to identify the failing layer.

If you listen on 0.0.0.0, check the device firewall. allow-lan mainly controls whether LAN devices may use proxy ports; it should not be mistaken for the controller interface’s firewall. Controller reachability depends on its own listen address and system network rules. You can test the port from another LAN device, but restrict the source network afterward. Do not turn temporary convenience into a permanently open management port.

Connection information shown in the panel comes from the core’s current state. Switching a policy usually affects new connections only; existing TCP sessions may continue using the old node until they close. When testing a node switch, close old connections in the panel or restart the target app, then observe the new connection path. The currently selected proxy-group option alone cannot prove that an active session has migrated.

Access scenario Recommended listener Additional controls
Desktop client, local management 127.0.0.1:9090 Use separate credentials and avoid port conflicts
Home LAN management The device’s LAN address Restrict source devices or networks with the firewall
Reverse-proxy access Keep the controller port bound locally HTTPS, authentication, and real-time connection forwarding
Container deployment The interface inside the container Map only the required addresses and ports

API troubleshooting order

When the panel cannot connect, first confirm on the device running the core that the controller port is listening, then test network reachability from the client side, and only then check authentication. Do not reverse this order: changing browser settings is pointless when nothing is listening, and changing credentials repeatedly will not help when a firewall blocks the network. If another program owns the port, update the client or panel address after changing the controller port.

A panel improves visibility but should not be the only source of configuration. Important changes should live in a backup-friendly YAML file, override file, or client configuration. State changed only through the runtime API may revert after a restart or reload. Persistent policy choices can rely on client persistence; structural changes should return to the configuration file. Separating temporary actions from durable configuration prevents changes made in the interface from disappearing after a restart.

Remote management also requires limiting log exposure. Connection lists may contain destination domains, source addresses, and traffic information, which should not be published on an uncontrolled network. Use a normal log level for everyday operation, raise verbosity temporarily to reproduce a problem, and restore it afterward. The value of an external panel is making rules and connections visible, not retaining every access record indefinitely.

CHAPTER 08

Configuration validation, coordination, and troubleshooting

Validate syntax before behavior

Configuration troubleshooting has two layers. First, confirm that the core can read the structure, including YAML indentation, field types, policy references, and provider formats. Second, after loading succeeds, confirm that real connections follow the expected DNS, rule, and proxy-group paths. Passing syntax checks only means the file can be parsed; it does not prove routing is correct. Conversely, an app that cannot connect does not necessarily indicate invalid syntax; a rule may simply have selected an unavailable node.

YAML uses spaces to express hierarchy; tabs, incorrect indentation, and formatting after colons can all change the structure. List-item hyphens must be at the correct level. Boolean values and numbers do not need quotes, while names or URLs containing special characters may be quoted. Proxy-group names are exact references: “Node Selection” and “Node Selection ” look similar, but the trailing space makes them different strings. For a long configuration, start near the line reported in the log and then move upward to find the containing module.

mihomo can check a configuration directory from the command line, although the executable name depends on the platform and installation method. The command below shows the common approach: specify the configuration directory and test it without starting a long-running service. GUI users can also use the client’s configuration checker and inspect the first core error in its logs. Later errors often cascade from the first structural error, so fix the earliest one first.

mihomo -t -d /path/to/config-directory

# If you need to observe the loading process in the foreground
mihomo -d /path/to/config-directory

Build a minimal validation matrix

After every change, test at least four target types: a LAN address that should go direct, a clearly defined ordinary site using the expected policy, a target available only through the proxy exit, and a target not covered by a dedicated rule that should fall into the fallback group. If TUN is enabled, add an app that ignores system proxy settings. If IPv6 is enabled, test A and AAAA connections separately. Fixed test targets make different configurations comparable.

Read connection logs as a processing chain. First check the inbound type to confirm whether the connection came from HTTP, SOCKS, or TUN. Then check whether the target is a domain or IP to determine whether DNS mapping or sniffing worked. Next inspect the matched rule and proxy group, and finally the actual node or DIRECT. If the entry point is wrong, changing rules is pointless. If the rule is right but the node is wrong, inspect proxy-group selection. If the node is right but the connection still fails, check node capabilities, the destination service, and the underlying network.

Validate DNS separately. Record the queried domain, upstream, response type, and connection destination instead of inferring a DNS error solely from an unreachable webpage. Temporarily use a simple nameserver configuration to establish a baseline, then restore policy, Fake-IP filters, and multiple upstreams step by step. Repeat the same tests after each change. Replacing a complex DNS configuration all at once can mix an unreachable upstream, an overly broad policy, and stale cache into one hard-to-isolate problem.

Failure symptom Most likely layer First check
Configuration will not load YAML or field references Read the first core error and check the corresponding hierarchy
Browser works, other apps fail System proxy coverage or TUN Confirm whether the failing app’s connection enters the core
Domain rule does not match DNS, sniffing, or rule order Check whether the connection target appears as a domain or IP
Old route remains after switching nodes Existing connection was not closed Close the old connection and start a new request
Small requests work, large transfers stall MTU, UDP, or the transport path Compare different nodes and check for fragmentation symptoms
Proxy groups empty after subscription update Provider or name merging Confirm provider status and the names referenced by proxy groups

Change one variable at a time

Effective troubleshooting depends on reproducibility. Disable advanced features to restore a basic connection, then enable DNS, Fake-IP, TUN, sniffing, and rule providers one by one. Record each change and result, and return to the last working configuration when something fails. If the client supports configuration profiles, create separate “Basic,” “TUN Test,” and “Full Rules” profiles instead of repeatedly overwriting one file.

For port errors, identify exactly what is listening. mixed-port, the DNS listener, and external-controller can all conflict with other programs; the port number in the error log tells you which one to inspect. After changing a port, update system proxy settings, the panel address, or any other dependent endpoint as well. For platform-specific commands and details, see How to Troubleshoot a Clash Port Conflict.

When every node times out, first determine whether the health-check URL is reachable, then test one node manually. If all providers fail at once, the cause is more likely the local network, DNS, subscription update, or system clock than every node failing simultaneously. If only one source fails, inspect that provider’s URL, cache, and node protocol. For initial connection checks, follow Choose a Node, Test Latency, and Confirm the Proxy Works.

Tune performance only after correctness

Shorter health-check intervals, more DNS upstreams, broader sniffing, and additional rule providers all increase workload without necessarily improving the experience. First establish a correct, stable processing chain with conservative defaults, then adjust based on a clear symptom. If the first page load is slow, measure DNS and connection time separately. If nodes switch too often, raise tolerance or lengthen the test interval. If a router is overloaded, reduce the number of providers, lower check frequency, and narrow the sniffing port range.

Log level affects both visibility and overhead. Keep ordinary information for daily use, raise verbosity temporarily while reproducing an issue, and restore it afterward. Persistently recording extensive connection details increases disk writes and accumulates unnecessary access data. Troubleshooting logs should cover the full chain around the incident, but do not need to be retained forever.

A mature configuration is not measured by its number of fields. What matters is that its five stages—entry, resolution, matching, decision, and exit—can all be explained, and that every advanced feature can be disabled independently without breaking the basic connection. After completing this modular setup, export the final YAML and override files and record the key client toggles. When moving to another device, choose the appropriate platform client from the downloads page, with Clash Plus recommended first for multi-platform support, then restore each module step by step instead of copying an oversized configuration no one can explain.