MIHOMO / CONFIGURATION REFERENCE
Clash YAML Configuration Reference
Structure overview · Field meanings · References · Runtime checks
This page is for users who edit configurations, maintain rules, or troubleshoot conflicting fields. First, identify which client generated the configuration, then check what the core actually loads. For installation, importing a subscription, and connection checks, start with the Getting Started guide. This reference follows field dependencies and is designed for looking up specific sections as you make changes.
The examples follow mihomo configuration semantics; not every Clash fork supports the same fields. GUI clients may manage ports, DNS, TUN, or the controller interface themselves. If you haven’t chosen a client yet, visit the download page to see the recommended Clash Plus and other options for each platform, then follow the configuration steps for your client.
1. YAML Structure and Configuration Loading
A configuration file is structured, typed data—not a set of commands executed line by line. The top-level mixed-port field defines a listening port, dns is a mapping, and proxies, proxy-groups, and rules are usually lists. The core parses the data first, then creates listeners and resolves proxies and groups before accepting connections. So field order in the file doesn’t dictate startup order, but the order of rules in the list does affect matching.
Indentation, Types, and Name References
Use two spaces for indentation throughout; never use tabs. Fields under the same parent must line up. A hyphen marks a new list item—it isn’t decorative. Indenting enable at the same level as dns changes which section it belongs to; indenting rules under a proxy group won’t create the expected top-level rules. An editor’s indentation guides are more reliable than judging alignment by eye.
Write Boolean values as true or false, and use integers for ports and intervals. Quote node names, passwords, and values containing special characters, especially strings with a colon followed by a space, a hash sign, or leading zeros. Quotes won’t fix bad indentation or turn a list into a mapping. When parsing fails, check the structure first, then the value types.
Proxies and groups refer to one another by name. Spaces, capitalization, and punctuation are all significant: "手动选择" and "手动 选择" are different names. Before adding a rule, make sure its target exists. Before deleting a proxy, search for groups that still reference it. Remove duplicate names, too, or it may be hard to tell what the UI displays from what the core actually resolves.
Build a Working Local Baseline
This configuration creates a local mixed-proxy listener, with all traffic ultimately sent direct. Use it to check the file path, port binding, and rule parsing; it does not provide remote proxy access. To test it, configure your browser to use the local mixed port or temporarily enable the client’s system proxy. A core that starts successfully is not the same as a connection to a remote proxy.
mixed-port: 7890
allow-lan: false
mode: rule
log-level: info
ipv6: false
proxies: []
proxy-groups:
- name: "Manual Selection"
type: select
proxies:
- DIRECT
rules:
- MATCH,Manual Selection
Keep a verified baseline, then add real proxies, automatic test groups, and DNS one step at a time. Reload and check the logs after each type of change to narrow down the cause if something breaks. Replacing the entire configuration in one go can introduce syntax, network, reference, and client override issues all at once, making them much harder to troubleshoot.
Don’t define the same key more than once in a YAML mapping. If dns appears in both the first and last parts of a file, the two sections won’t necessarily be merged. Some parsers reject duplicate keys; others keep only the later value. Relying on either behavior can produce different results across clients. When sections need to be combined, generate a single top-level key.
Distinguish Source Files from Runtime Files
The original subscription text, the configuration you edit, and the runtime configuration generated by the client may all be different files. Before startup, clients often add controller settings or replace ports, so manual edits in the runtime directory may disappear on the next update. Use the client’s local configuration, extension configuration, or override feature whenever possible, and check exactly what each one affects.
The line number in a configuration error refers to the file actually read by the core, which may not match the original subscription. For missing references or invalid fields, check the active configuration name and runtime configuration in the client first. Before sharing troubleshooting details, remove subscription URLs, proxy passwords, and controller secrets. Keep only the structure needed to reproduce the issue; don’t publish the full runtime file.
2. Common Fields, Listening Ports, and Operating Modes
Common fields determine where traffic enters the core and how the core handles it. They don’t create remote proxies or automatically change network settings for every app. When troubleshooting, distinguish between “the core is listening,” “the system proxy is configured,” and “the app uses the system proxy” so you don’t mistake an entry-point issue for a proxy failure. GUI toggles may be linked to YAML fields, but they operate at a different layer.
Choose Ports and Listening Addresses
| Field | Primary purpose | Limitations |
|---|---|---|
port | HTTP proxy port | For apps that support HTTP proxies |
socks-port | SOCKS proxy port | Apps must use the matching proxy protocol |
mixed-port | Accepts both HTTP and SOCKS traffic on one port | A common all-in-one entry point for desktop clients |
allow-lan | Allow connections from devices on the local network | Also check the bind address and firewall |
bind-address | Local address range to listen on | Does not configure port forwarding on your router |
A mixed port lets multiple apps share one entry point; separate ports make it easier to distinguish HTTP and SOCKS traffic. There’s no need to enable every listener unless you have a specific use for it. Two listeners can’t bind to the same port on the same address. A port conflict may come from another proxy app, an old core process, or a client service still running in the background—not necessarily from an error in the current configuration.
mixed-port: 7890
allow-lan: false
bind-address: "127.0.0.1"
mode: rule
log-level: info
ipv6: false
For local use, bind to a loopback address. If you need to route traffic from a phone or another computer through this device, enable LAN access and configure the firewall and authentication for your network. Setting allow-lan to true alone doesn’t guarantee that other devices can connect; opening the firewall alone won’t make a service bound to loopback accept external connections.
Rule, Global, and Direct Modes
mode: rule matches requests against rules from top to bottom and is suitable for everyday routing. global uses a global proxy selection instead of choosing a target through the usual rule list; direct sends traffic directly. Changing modes in the UI may not update mode in the file, as the client may change the runtime state through the controller interface. Check the current UI state and connection logs.
Global mode can’t fix traffic that never reaches the core. If an app bypasses the system proxy and generates no connection logs in rule mode, switching to global mode usually won’t make those logs appear. Check the app’s own proxy settings first, or use TUN if needed. If the connection does reach the core but matches a direct rule, then investigate the mode and rule order.
log-level controls how much detail appears in logs; it doesn’t change routing. Keep it at info for everyday use and switch to debug temporarily to investigate hard-to-reproduce issues, then change it back. Detailed logs may contain domain names, destination addresses, and access times, so limit what you retain or share. A timeout log doesn’t mean all traffic has failed; check whether it refers to a proxy handshake, DNS, or a health check.
Controller Interface and Persistent State
external-controller is the controller interface, not a proxy port to enter in your browser. It’s usually used to read connections, switch proxy groups, or update runtime configuration. Entering it as an app’s proxy address causes a protocol mismatch. When running the core manually, enable it if needed. GUI clients usually generate and manage these settings themselves, so avoid overriding them with a conflicting address.
If you enable the controller interface, bind it to a loopback address and set a unique secret. Don’t expose it directly to the public internet. The example secret is for illustration only; generate your own for actual use. If your client has built-in credential management, use that instead of keeping an example value in a public configuration repository.
external-controller: "127.0.0.1:9090"
secret: "replace-with-a-local-random-secret"
profile:
store-selected: true
store-fake-ip: true
store-selected persists proxy group selections; store-fake-ip persists fake IP mappings. Persistence doesn’t replace the configuration itself: after renaming a group, its saved selection under the old name may no longer apply. After changing the mode, ports, or storage settings, verify the entry point, routing, and behavior after restart separately. Don’t assume everything is working just because a toggle looks enabled.
3. Proxy Fields and Proxy Providers
A proxy defines a specific remote connection; a proxy group selects among available connections. The proxy name is only a label used for references. Its protocol, server, port, and authentication determine whether it can connect. Renaming a proxy doesn’t change its location or reduce latency. Rename proxies with duplicate names from different services before adding them to the same configuration, so group references remain unambiguous.
Required Fields by Protocol
Each item in proxies is a proxy object with at least a type and connection parameters recognized by the current protocol. Don’t copy fields mechanically from another protocol: a Shadowsocks cipher, a Trojan password, and a user ID in another protocol serve different purposes. A successful parse confirms only that the syntax is valid; it doesn’t mean the remote server will accept the parameters.
proxies:
- name: "Example SS"
type: ss
server: "proxy.example.com"
port: 8388
cipher: chacha20-ietf-poly1305
password: "your-password"
udp: true
- name: "Example Trojan"
type: trojan
server: "gateway.example.net"
port: 443
password: "your-password"
sni: "gateway.example.net"
skip-cert-verify: false
udp: true
server is the address of the remote server, and port is its port—not your local mixed port. If the server is a hostname, it must be resolved before the proxy can connect, which is one reason DNS settings can affect every proxy. If you switch the server address to an IP and the protocol requires TLS, keep the correct server name for TLS as required. The address and the identity used for verification aren’t interchangeable.
udp: true enables the proxy’s corresponding UDP forwarding capability, but doesn’t guarantee that every part of the route supports every UDP app. Check the protocol implementation, server settings, network restrictions, and how the app connects. If web browsing works but voice calls fail, first confirm which transport is being used; website load times alone can’t tell you whether UDP is working.
TLS Identity and Transport Settings
In a TLS connection, the server address and sni can serve different purposes: the former determines where to connect, while the latter supplies the target name during the handshake. Certificate validation also checks the name, expiration date, and trust chain. If you see a certificate error, check the device clock, node hostname, server certificate, and whether the connection is being redirected. Disabling verification shouldn’t be a routine fix.
skip-cert-verify: false keeps normal certificate verification enabled. Setting it to true changes the connection’s trust boundary; it won’t fix an unreachable server, incorrect password, or protocol mismatch. If the service uses a transport such as WebSocket or gRPC, its path, hostname, or service name must also match the server. You can’t infer those settings from a server address and port alone.
Supported proxy fields depend on the core’s capabilities. The original Clash, its various forks, and mihomo don’t all support the same protocols and extended fields. If an import reports an unsupported proxy type, check which core the client actually uses instead of repeatedly changing indentation. For more on how cores and clients fit together, see the Clash, mihomo, and client ecosystem map.
Manage External Proxies with proxy-providers
Proxy providers load an external proxy list as a separate resource, making it easy for groups to use a continuously updated set of proxies. They aren’t universal importers for any subscription format: the remote content must use a proxy-provider format supported by the core, commonly with proxies: as its root key. A full subscription that a client can import may not work directly as a provider URL.
proxy-providers:
remote-nodes:
type: http
url: "https://config.example.com/proxies.yaml"
path: ./providers/remote-nodes.yaml
interval: 3600
health-check:
enable: true
url: "https://www.example.com/"
interval: 600
proxy-groups:
- name: "Subscription Selection"
type: select
use:
- remote-nodes
proxies:
- DIRECT
Here, interval controls how often the resource is updated, in seconds. The same field name in a health check controls test frequency; they’re not the same setting. path sets the local cache location. When running the core manually, check the working directory and write permissions, and give each provider a separate cache path. If an update fails, distinguish between a first load with no cache and an update where an existing cache is still available.
Provider URLs usually contain credentials; don’t include them in screenshots or public repositories. For troubleshooting, keep the hostname but remove sensitive parts of the path, then check the update logs, returned format, and changes in proxy count. If the proxies updated but the group doesn’t show their new names, check use, filter conditions, and the active configuration instead of repeatedly clicking the update button.
4. Proxy Group Types, Testing, and Selection State
Proxy groups provide a stable interface between rules and proxies. Rules can always point to a “business egress” group while you replace the proxies inside it as needed. This is easier to maintain than hard-coding a proxy name into every rule. A proxy group doesn’t provide remote access itself; it still needs to select a proxy, another group, or a built-in outbound. Use group names to describe their purpose and proxy names to describe the connection, making connection logs easier to read.
Manual Selection and Automatic Testing
select leaves the decision to you and works well when choosing manually by use case, network, or region. The order of proxies affects how they appear in the list, but selection persistence means the first item isn’t necessarily selected at startup. url-test selects a candidate based on results from a specified test URL and is useful for automatically choosing among similar proxies. The result depends on the test target; it doesn’t predict performance on every website.
proxy-groups:
- name: "Business Egress"
type: select
proxies:
- "Automatic Selection"
- "Example SS"
- DIRECT
- name: "Automatic Selection"
type: url-test
proxies:
- "Example SS"
- "Example Trojan"
url: "https://www.example.com/"
interval: 300
tolerance: 80
lazy: true
This configuration depends on the proxies with the same names from the previous chapter; copying it alone leaves references unresolved. interval sets the test interval, while tolerance sets the threshold for switching. They’re different settings and aren’t interchangeable. A suitable tolerance can reduce frequent switches caused by small fluctuations, but it can’t guarantee session continuity or keep a failed proxy working. Choose values based on the number of proxies, network stability, and testing overhead.
lazy: true lets a group reduce unnecessary checks while it isn’t in use, so missing test results right after startup aren’t necessarily a problem. If a manual test still fails, check that the test URL is reachable, its certificate is valid, and the proxy can complete the request. Test traffic also passes through the proxy, so very short intervals can burden your device, network, and server.
Failover and Load Balancing
fallback prioritizes availability in a defined order: it usually selects the first available candidate, making it suitable for a primary and backup proxy. That’s different from url-test, which aims for lower test latency. If the first candidate stays available, the others may rarely carry traffic. That’s expected for a priority-based strategy and doesn’t mean they were added to the group incorrectly.
load-balance distributes connections across candidates; it doesn’t simply combine their bandwidth for a single download. Different distribution strategies can affect how consistently connections to the same destination use one egress. If a service requires a fixed login region or a persistent server session, consider the impact of changing egress first. More proxies in a group don’t automatically make load balancing a better choice than manual selection.
Existing connections usually don’t move to a new proxy when you switch groups. Browser keep-alive connections, streaming sessions, and background traffic may continue through the old egress; new connections will use the new selection. To verify a group change, close the relevant connection or start a new request, bearing in mind this may interrupt an active transfer. Mistaking an unchanged existing connection for a configuration that didn’t load is a common troubleshooting pitfall.
Provider References, Filters, and Circular Dependencies
Within a group, proxies references explicitly named proxies, while use references proxy providers. Combine them as needed to keep direct access or specific proxies alongside a dynamic set. Filters typically use fields such as filter to match candidate names. Quote regular expressions, and check that at least one expected candidate remains after filtering.
proxy-groups:
- name: "Remote Egress"
type: select
use:
- remote-nodes
filter: "(?i)office|backup"
proxies:
- DIRECT
Changes to provider proxy names can affect filter results. If the list suddenly shrinks, check the updated names and regular expression before investigating the network. Don’t hide naming problems by broadening the filter to match every proxy; that may add egress options unsuitable for the use case. Before changing a complex filter, test with a small configuration without filters to confirm that the provider loaded successfully.
Proxy groups can reference other groups, but they can’t form a cycle, such as one group referencing a second group that references the first. Each layer should lead to an actual proxy or built-in outbound. Nesting automatic groups also makes behavior harder to understand: if a connection fails, check the selection at each level, not just the outer group name. For more on the gap between test results and real-world performance, see How Clash latency tests work.
5. Rule Syntax, Match Order, and Rule Providers
Rule mode checks rules from top to bottom; the first match determines which outbound handles the connection. Later rules usually don’t affect the target for that connection. So a rule’s position matters as much as whether it exists. Put specific exceptions first, broader rules later, and finish with MATCH as an explicit fallback. This makes the routing decision in connection logs easier to follow.
Domain and IP Rules
DOMAIN matches an exact domain, DOMAIN-SUFFIX matches a domain and its subdomains, and DOMAIN-KEYWORD matches by keyword. Keyword rules have a wider scope and can catch unrelated domains. For business-specific routing, prefer exact domains or suffixes. Domain rules match hostnames, not full URLs; don’t include the scheme, path, or query parameters.
rules:
- DOMAIN,api.example.com,Business Egress
- DOMAIN-SUFFIX,example.net,Business Egress
- IP-CIDR,192.168.0.0/16,DIRECT,no-resolve
- IP-CIDR,10.0.0.0/8,DIRECT,no-resolve
- IP-CIDR,172.16.0.0/12,DIRECT,no-resolve
- MATCH,Business Egress
IP rules match destination address ranges, unlike domain rules. no-resolve prevents an IP rule from triggering a DNS lookup just to evaluate a domain request; the rule can still match if the request already has a destination IP. It doesn’t skip the entire rule or disable all DNS requests. Interpret matches in the context of whether the app gives the core a hostname or an IP address.
A single service may use different domains for sign-in, APIs, static assets, and updates. Routing only its homepage domain may let the page load while sign-in fails—a common sign of incomplete rule coverage. Use connection logs to identify missing dependencies instead of immediately switching to broad keyword rules or global mode. If you know the hostname but see only an IP, check whether the app resolves it itself and whether its DNS traffic passes through the core.
Rule Targets and Data Dependencies
The target at the end of a rule can be an existing proxy, a proxy group, or a built-in outbound such as DIRECT or REJECT. A target that names a nonexistent group may prevent the configuration from loading. For most business-specific rules, point to a group with a stable purpose so renaming a proxy doesn’t require changes throughout the file. Keep reject rules as specific as possible; blocking authentication or certificate-related requests can cause hard-to-diagnose failures elsewhere.
Rules such as GEOIP rely on geographic data, while rule providers rely on external resources. Even with valid configuration syntax, missing data files, failed downloads, or unwritable cache paths can prevent initialization. In the logs, distinguish an invalid rule string from an unavailable resource; each requires a different fix.
Process-based rules are limited by the operating system, permissions, and traffic entry method. Don’t assume a process rule that works on a desktop will identify the original app when routing traffic on a router. For traffic from other devices on the local network, the core sees forwarded connections and can’t inherently identify the remote process. In these cases, build policies around domains, destination addresses, or identifiable source conditions.
Move Large Rule Lists into rule-providers
Rule providers keep long rule lists out of the main configuration, leaving it to define priority and outbound targets. behavior declares the rules’ semantics, while format declares the file format; both must match the remote content. The example uses a YAML classical ruleset. It contains matching conditions without an outbound target on each line; the main configuration sets the target with RULE-SET.
rule-providers:
work-domains:
type: http
behavior: classical
format: yaml
url: "https://config.example.com/work-rules.yaml"
path: ./rules/work-rules.yaml
interval: 86400
rules:
- RULE-SET,work-domains,Business Egress
- MATCH,DIRECT
The remote file should use a structure like the one below. If the provider declares a domain ruleset, don’t put full classic rule strings in it as if they were plain domains. Mixing formats can cause parsing errors or prevent rules from matching as expected. Check the content format before increasing the update interval.
payload:
- DOMAIN,api.example.com
- DOMAIN-SUFFIX,example.net
Rulesets also have trust and maintenance implications. Updates can expand or shrink the match scope, changing the egress used by a service. Choose rulesets with a clear purpose and inspectable content. Before making changes, keep the cache and a known-good configuration. To verify a change, test one destination that should match and one that shouldn’t, then check both the matched rule and final outbound. One successful test isn’t enough to confirm that all routing is correct.
6. DNS Resolution, fake-ip, and Bootstrap Routing
DNS plays a role in resolving destination domains, proxy server hostnames, and some rule matches. If a website won’t load, the problem could be anywhere in that chain: the app may not get an address, the core may fail to resolve a proxy hostname, or the resolution path may not match the connection’s egress. First identify which hostname is being resolved, by which component, and over which path. Then choose the relevant field to change; replacing every resolver at once makes the original problem harder to reproduce.
DNS Entry Points and Enhanced Modes
dns.enable enables the core’s DNS feature, and listen sets the address that accepts DNS requests. Neither automatically changes the operating system’s DNS settings. Explicit system proxies, apps’ built-in encrypted DNS, and TUN handle DNS differently. DNS policies can apply only when requests actually reach the core; the presence of these fields alone doesn’t mean the core has taken over resolution.
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:
- 1.1.1.1
nameserver:
- "https://cloudflare-dns.com/dns-query"
fake-ip uses a virtual address to link a domain to subsequent connections, which the core then handles based on the domain and rules. The returned address isn’t the website’s real server address, so a test from this range doesn’t automatically mean DNS has been tampered with. Apps that need real addresses for LAN discovery or specific checks may need to be excluded, but don’t exclude every domain just to avoid investigating compatibility issues.
redir-host generally returns the actual resolved address and can suit different compatibility needs. The main difference from fake-ip is how addresses are associated with domains, not simply speed. Choose based on the traffic entry point, app behavior, and rule requirements. After switching, account for the system DNS cache, app cache, and existing connections; don’t judge by the first request alone.
Bootstrap DNS and Proxy Hostname Resolution
default-nameserver handles bootstrap lookups, such as resolving the hostnames of DNS servers. It isn’t an additional regular resolver with the same priority for all traffic. When an encrypted DNS address uses a hostname, the client first needs to resolve that hostname. If bootstrap resolution depends on a proxy that in turn depends on the same DNS service, startup can get stuck in a dependency loop.
proxy-server-nameserver lets you set a separate resolution path for proxy server hostnames, reducing dependencies with regular DNS lookups. Use a resolver that’s actually reachable in your current network. An address working on another network doesn’t guarantee it will work here; firewalls, ISP networks, and device DNS policies can all affect connectivity.
respect-rules makes DNS connections follow routing rules. This can help when you need a consistent egress policy, but it also adds dependencies on rules and proxy hostname resolution. Before enabling it, plan a separate resolution path for proxy hostnames to avoid DNS requests waiting on a proxy that’s itself waiting on DNS. For initial troubleshooting, start with a simpler resolution path and add routing policies once basic connectivity works.
Choose Resolvers by Domain and Troubleshoot Caches
nameserver-policy lets you assign a resolver to specific domains, such as sending internal domains to an office DNS server. Internal resolver addresses are environment-specific and may not be reachable outside that network, so update the domain policy when the environment changes. Sending internal hostnames to a public resolver usually won’t return the expected address and may expose internal lookup requests unnecessarily.
dns:
enable: true
enhanced-mode: fake-ip
default-nameserver:
- 1.1.1.1
proxy-server-nameserver:
- 1.1.1.1
nameserver:
- "https://cloudflare-dns.com/dns-query"
nameserver-policy:
"+.corp.example.com":
- 192.168.1.53
Merge this snippet into your existing DNS configuration; don’t add a second dns key to the same file. Replace the sample corporate network address with a resolver that works in your environment. If you also use fallback resolvers and filters, understand how primary and fallback results are selected. Adding a fallback resolver doesn’t automatically route requests by domain or guarantee that every request uses the same resolver.
After making changes, retest with the same domain, app, and connection method. A command-line lookup tool may use the system resolver directly, unlike a browser using encrypted DNS; different results don’t always indicate a problem with the core. Once you confirm the resolution path, clear the relevant caches and reconnect. If you use fake-ip mappings, also check persisted state so old addresses don’t get mixed with the new configuration and cause intermittent failures.
7. TUN Routing, System Routes, and Platform Differences
TUN creates a virtual network interface in the operating system, allowing traffic that doesn’t use an explicit proxy to enter the core. It handles traffic interception, not proxy groups, proxies, or rules. After TUN is enabled, the core still has to decide whether each connection goes direct, gets rejected, or uses a proxy. If the proxy is already unavailable, TUN just exposes more apps to the same failure; it won’t fix the remote connection.
Minimum Configuration and Permissions
Desktop clients usually need to install a service or obtain appropriate permissions to create an interface and change routes. If you run the core manually, you’ll need to handle system authorization yourself. If the configuration says enable: true but the logs show that interface creation failed, fix the permissions, driver, or service status first rather than changing rules. A toggle in the GUI doesn’t replace checking the actual interface and routes.
tun:
enable: true
stack: mixed
auto-route: true
auto-detect-interface: true
dns-hijack:
- "any:53"
auto-route asks the core to configure the routes needed for traffic interception. auto-detect-interface helps select an outbound interface. On devices with Ethernet, Wi-Fi, virtual machine, or other tunnel interfaces, automatic selection can be affected by changes to the default route. If the problem occurs only after switching networks, check the current default route and actual outbound interface before suspecting the subscription.
stack determines which network stack handles virtual interface traffic. Available options and their behavior depend on the core you’re using. Don’t copy a supposed universal “best” setup: UDP, LAN access, and high-volume transfers can behave differently in the same app. When switching stacks, keep other settings unchanged and retest the same use cases to see whether this field made a difference.
DNS Hijacking and Traffic Scope
dns-hijack sends matching standard DNS traffic to the core’s DNS resolver. It can’t automatically decrypt HTTPS-based DNS built into an app, nor can it intercept requests outside the TUN route. If an app keeps using its own DNS path, the core may see different destination information than expected. Check the app’s DNS settings and actual connections before deciding whether to change the configuration.
Route interception also needs to account for LAN printers, network storage, development environments, and virtual machine subnets. A direct rule determines where traffic goes after it enters the core; a system route exclusion determines whether traffic enters the core at all. If a local service becomes unreachable, first check whether its requests reach the core, then check the destination rules. Avoid adding exceptions at the wrong layer.
Strict routing can help prevent traffic from bypassing the tunnel, but may also affect system services, other tunnels, and LAN connections. Enable it only after you understand how it works on your platform. On remotely managed devices or servers in particular, route changes can cut off your current management connection. Keep local access or a rollback option available; don’t apply a desktop configuration without a recovery path.
Check the Actual State on Each Platform
| Platform | Check first | Common limitations |
|---|---|---|
| Windows | Service permissions, virtual interface, firewall | System proxy behavior may differ for UWP apps |
| macOS | System authorization, routes, and client implementation | Network extensions or other tunnels may affect traffic interception |
| Linux | TUN device, permissions, policy routing, and firewall | Check desktop and server environments separately |
| Android / iOS | Client VPN authorization and system status | Desktop TUN fields aren’t a universal configuration entry point |
On Windows, first confirm that the explicit proxy works. Then turn off the system proxy and test TUN on its own to distinguish the two traffic paths. UWP app loopback restrictions in explicit proxy mode are a separate issue from TUN interception; not every connection failure is a firewall problem. For setup and service troubleshooting, see Clash Verge Rev on Windows: installation and TUN troubleshooting.
On Linux, also check whether forwarding, firewall rules, and policy routing are managed by another service; reloading the network manager may change existing settings. On macOS and mobile platforms, use the authorization flow provided by the current client rather than forcing a desktop configuration onto the device. Clash Plus, Clash Verge Rev, and FlClash have different interfaces; follow the client instructions for your platform.
If all connectivity stops, disable TUN first and check whether the original network connection returns. Then inspect core startup logs, DNS, and proxies. If the problem persists with TUN off, check for leftover system proxy settings and routes instead of repeatedly switching proxies. After testing, keep only the traffic entry method you need. Running several proxies or tunnels at once makes it hard to identify the traffic path.
8. Client Overrides, Configuration Merges, and Validation
The final active configuration is often determined by a combination of the original subscription, local extensions, client settings, and runtime state. Subscription updates refresh the source content, overrides preserve local changes, and the controller interface may change the current selection. When all three are involved, the file in your editor can’t explain everything. The goal of troubleshooting is to find out what the core actually loaded and where the connection actually went—not just whether a field was saved.
Understand Merge Rules and Where Settings Persist
Mappings, lists, and scalar values may be merged differently. Mappings may be recursively merged, while lists may be replaced or appended by the client’s extension mechanism. YAML itself doesn’t define one universal merge strategy. Pay particular attention to rules: even if all rules are preserved, their order changes priority, so check the merged sequence.
For example, if a subscription already ends with MATCH and the client appends your custom domain rule after it, that rule won’t get a chance to match. Use the client’s prepend-rules feature, if available, or insert the custom exception before the fallback during generation. Don’t stop when the UI says “added successfully”; send a matching request and check which rule actually applied.
# Expected rule order after merging
rules:
- DOMAIN,api.example.com,Business Egress
- IP-CIDR,192.168.0.0/16,DIRECT,no-resolve
- MATCH,Business Egress
Clients often manage ports, the controller interface, and TUN themselves, so overrides to those fields may be replaced again. First determine which layer owns a field, then make changes in one place to avoid conflicts between UI settings and file overrides. Clash Plus is a good starting point when choosing a client. With other clients, use their own local extension mechanism; don’t treat one client’s script syntax as a core-wide configuration field.
If local changes disappear after a subscription update, you may have edited the wrong file or the override may not be linked to the active configuration. Compare the original text before and after the update, then check that the extension is enabled and applies to this configuration. Some extensions affect only one configuration; others affect all of them. Choosing the wrong scope can make a test configuration affect everyday use. For long-term maintenance, document each local change, what it does, and when it can be removed.
Validate the Configuration Before Running It
When running mihomo manually, test the file you intend to enable first. The command below checks whether the current core accepts the specified file; it doesn’t verify remote proxies, external rule resources, or end-to-end connectivity for every app. GUI clients may use their own core and working directory, so a successful test with another mihomo in a terminal doesn’t prove the client is using the same file.
mihomo -t -f ./config.yaml
During static validation, address four types of issues in order: indentation or type errors, missing name references, fields unsupported by the current core, and external resource loading failures. Focus on the first meaningful error; later errors may be knock-on effects. Retest after each change. Don’t delete multiple fields without understanding them: the configuration might start, but its original routing behavior could be lost.
At runtime, confirm that listeners start successfully, then that test requests enter the core. Next check the mode, rules, proxy group, and final proxy. Only then verify the egress address and actual app behavior. Test one request that should go direct and one that should use a proxy, and reconnect if needed. For a complete first-connection walkthrough, see Choose a proxy, test latency, and verify the connection.
Troubleshoot at the Right Layer
Configuration test passes, but why can’t my app connect?
Check whether the request appears in the connection logs. If it doesn’t, check the system proxy, app proxy, or TUN entry point. If it appears but fails, check DNS, the proxy handshake, and the rule target. Successful resolution and configuration validation are only intermediate steps; they don’t replace testing an actual connection.
Why doesn’t the egress change immediately after switching proxy groups?
Check whether you’re in rule or global mode, then confirm which group the request actually uses. Existing long-lived connections may keep using the old egress, so start a new connection. If new requests still use the old route, check persisted group selections, nested groups, and whether the client loaded the expected configuration.
Why didn’t my local rules take effect after a subscription update?
Check whether the override is still linked to the active config and whether lists are replaced, prepended, or appended. Inspect the final file for duplicate top-level keys, rules placed after MATCH, and renamed groups. Fix the generation process instead of repeatedly editing the runtime cache.
Make rollback steps as clear as the changes themselves: restore a known-good configuration, disable the most recently added override or TUN setting, and check the system proxy and DNS state. Don’t delete the entire client directory to fix a single field; it may contain proxy selections, caches, and local extensions. If you need to reinstall, choose the right installer from the platform download page and back up any local configuration you need.