Configuration Reference / mihomo
Clash Configuration Reference
YAML structure, field meanings, and override order
For your first connection, start with the four-step setup guide to import a subscription, choose a mode, connect, and verify. This page is a reference: find the section you need to change, check how its fields depend on one another, then verify the config actually in use. Menu names may vary across clients such as Clash Plus. The guidance below focuses on mihomo YAML semantics and distinguishes client interface settings from core fields.
How to read this guide: Except for the minimal config in the final section, each code block is a partial snippet—not a replacement for your entire subscription. Example domains, usernames, and passwords are fictional. Export a working config before making changes, and keep your current client settings.
Structure / 01
YAML structure and config sources
Understand mappings, lists, and references
A config file isn't a sequence of commands executed in display order. It's structured data read by the core. The text before a colon is a field name; the text after it is its value. If no value follows, the indented lines below usually define child fields. At the top level, dns is a mapping, rules is a list, and proxies is a list of node mappings. Identify the data type before editing; it's more reliable than guessing from where a setting appears in the interface.
Use spaces for indentation and never mix in tabs. Two spaces are a common convention for readability, but they aren't mandatory everywhere; what matters is consistent alignment at the same level and deeper indentation for child fields. Put a space after list-item hyphens and field colons. Full-width colons, ideographic spaces, and curly quotes copied from chat apps may look similar to the expected characters but can break parsing.
mixed-port: 7890
mode: rule
dns:
enable: true
enhanced-mode: fake-ip
proxy-groups:
- name: "Manual Select"
type: select
proxies:
- DIRECT
rules:
- MATCH,Manual Select
In the example, name, type, and proxies belong to the same proxy group. The group's proxies field contains a list of names, not node connection settings. In the final rule, “Manual Select” must exactly match the group name, including spaces, capitalization, and punctuation. A successful YAML parse only confirms that the file structure is valid; the core can still reject a config if a referenced name doesn't exist.
Strings and duplicate fields
Quote names that contain special punctuation. Passwords with a hash sign, colon, or leading or trailing spaces should also be explicitly quoted as strings. Double quotes interpret backslash escapes; single quotes are usually better for preserving text literally, but a single quote inside the value must be escaped by doubling it. Write port numbers as integers and Boolean values as true or false. Don't quote every value just for consistency.
Do not define the same field more than once in a mapping. For example, if the file already has a dns section at the top and you add another dns section at the bottom, some parsers may reject the file while others may keep only the later entry. Adding new settings to the existing mapping is safer than relying on duplicate-key behavior. Comments begin with a hash mark and explain why a change was made. They are not runtime settings, and commenting out an invalid line should not be used to conceal a broken reference.
Make sure you know which file you're editing
Clients often keep the original subscription, a local editable copy, and the final runtime config. The subscription is the source for remote updates; the local copy may be used only for editing; the runtime config is generated after the client adds ports, a control interface, and overrides. Find the currently active profile in the client, then check the runtime config or core logs for the loaded path. Don't assume a config.yaml you found in File Explorer is the file currently in use.
Before editing, make a copy of the original and give each file a clear name. Save your changes, then reload the config in the client; simply closing the editor won't make the core read it again. If the interface reports a successful save but behavior hasn't changed, check the active profile, generated file path, and reload result before blaming the rules. Subscription updates can recreate the source file, so put long-term changes in an override mechanism supported by the client.
This page is organized by field semantics and doesn't assume every client exposes every option. Confirm that the core is mihomo, then check which fields the installed version supports. Legacy Clash clients aren't interchangeable with current mihomo implementations.
If you encounter an unknown field, narrow it down to one config section and determine whether it belongs to the core, the client, or an external tool. Client-specific fields placed in a core config may be ignored or cause unexpected behavior. To review the basics, start with the quick glossary, then return to the relevant section. You don't need to learn every field at once.
Runtime / 02
Ports, modes, and the control interface
Local listeners and the system proxy are separate
mixed-port accepts both HTTP proxy and SOCKS5 requests on one listening port, which makes it convenient to configure in desktop apps. port provides an HTTP-only proxy listener, while socks-port provides a SOCKS-only listener. Most users only need one mixed port, not every listener enabled at once. A port number identifies a local listening endpoint; it says nothing about the server address, node quality, or a specific protocol capability.
A listening core doesn't mean the operating system is sending traffic to it. The client usually manages the system proxy toggle by updating the OS proxy settings. Some programs use that setting, some have their own proxy options, and others connect directly. When troubleshooting, confirm the listener is running first, then check whether the app uses it. “System proxy enabled” does not mean “all apps use the proxy.”
mixed-port: 7890
allow-lan: false
bind-address: "127.0.0.1"
mode: rule
log-level: info
ipv6: false
external-controller: "127.0.0.1:9090"
Rule, global, and direct modes
mode: rule routes connections according to rule order and is the starting point for understanding split routing. Global mode sends ordinary connections through the selected global policy, but you still need a working proxy; global mode won't fix a dead node. Direct mode is useful as a temporary comparison that bypasses the proxy. After switching modes, existing long-lived connections may keep using their original route. Start a new request to test the change instead of refreshing a page that may reuse its connection.
| Field | Purpose | What to check |
|---|---|---|
mixed-port | Combined HTTP and SOCKS5 listener | Does the app use the same port? |
allow-lan | Allow access from the local network? | Check the bind address and firewall |
mode | Connection routing mode | In rule mode, check rule order |
log-level | Log verbosity | Restore the usual level after debugging |
external-controller | Control interface bind address | Keep it separate from the proxy port |
log-level: info is suitable for everyday checks of startup and connection status. You can temporarily increase verbosity when debugging, but logs will be busier and may include sensitive details such as destinations. Restore the original level when done, and remove subscription URLs and credentials before sharing logs. ipv6: false does not disable IPv6 across the operating system or guarantee that apps can't use IPv6 through another route. Check DNS and traffic interception settings too.
Configure LAN sharing and the control interface separately
allow-lan controls access from the local network, while bind-address determines the listening address. The example binds to loopback and is suitable only for local connections. Turning on LAN access alone doesn't mean other devices can connect; check that the bind address allows it. If you want to share the proxy, explicitly bind to the relevant local interface or listening range, and restrict firewall access to the right networks, such as home or work.
When LAN access is enabled, set the phone to the computer's LAN address and proxy port—not 127.0.0.1. The loopback address on a phone refers to the phone itself. A changing computer address, guest-network isolation, sleep, or firewall rules can interrupt sharing. See sharing a mixed port over your local network for setup steps. Check your network's usage policies, and don't expose the proxy on an untrusted network.
external-controller is an interface for a client or dashboard to manage the core. It isn't the proxy endpoint your browser uses to access the internet. A loopback listener is usually enough for a desktop client. If you genuinely need remote control, configure strong authentication separately and restrict network access. Don't expose the control interface just to share the proxy with a phone. If the client can't connect to the core, check the control port and proxy port separately so you don't change the wrong one.
If a port is already in use, first quit the other running proxy instance, then reload. If you must change the port, update the system proxy, app-specific proxy settings, and LAN devices as well. Changing only the YAML while leaving the old system proxy in place can leave the core running normally while the browser keeps failing to connect. Change one related set of fields at a time to make the cause easier to identify.
Name resolution / 03
DNS, fake-ip, and resolution paths
First find out what is making the DNS request
DNS translates domain names into addresses, but resolution can happen in the app, operating system, browser, proxy core, or remote proxy server. Enabling core DNS only means the core can resolve names; it doesn't mean every app's queries reach it. The system proxy usually doesn't intercept all system DNS, and TUN DNS hijacking has its own scope. Map the request path before changing settings. Otherwise, you might tune core DNS while testing a browser that bypasses it with built-in resolution.
dns.enable controls the core DNS module, and listen sets its listening address. The example uses loopback and a high port for clarity and to avoid common system ports. A listen address alone doesn't make the system use it. Desktop clients may route queries to the core through TUN or other integrations; check the actual client settings. Don't treat the example as a complete system-wide DNS interception setup.
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"
- "localhost"
default-nameserver:
- 223.5.5.5
- 1.1.1.1
nameserver:
- https://dns.alidns.com/dns-query
proxy-server-nameserver:
- 223.5.5.5
fake-ip and real-address modes
fake-ip assigns a virtual address to a requested domain. When the connection later reaches the core, it can use the mapping to recover the domain, making domain-based routing easier. The example address range is reserved for this mechanism; it isn't a public server address to put in a node's server field. If a lookup returns an address in that range, check whether fake-ip mode is active before assuming DNS has been tampered with.
redir-host generally returns the actual resolved address. It can suit cases that need real address results, but it isn't a universal fix for DNS problems. Switching modes changes address caching and connection behavior, so old cache entries may affect what you see. After changing the mode, reload the config and clear caches or restart affected apps as needed for your OS. Test one known target first, then expand the scope; don't change the mode, upstreams, and rules all at once.
fake-ip-filter lets specific domains bypass virtual address assignment. Add local services, device discovery domains, or apps that require real addresses one at a time. A broad filter reduces the convenience of domain mappings. Filter modes and matching syntax have specific semantics; don't assume every entry is a regular expression. Start with domains that actually cause problems, note why each was added, and avoid building up a filter list no one can explain later.
Upstream DNS and proxy server name resolution
nameserver lists upstreams for regular queries. default-nameserver can bootstrap resolution for the hostnames of upstream DNS servers. If every upstream is specified by hostname and there's no initial way to resolve it, startup can get stuck waiting on itself. proxy-server-nameserver resolves proxy server hostnames, so node addresses don't have to depend on a proxy connection that hasn't been established yet. Choose upstreams based on reachability from your network; more entries don't automatically mean greater reliability.
nameserver-policy selects upstreams for specified domains or matching ranges. fallback and related filters evaluate resolution results according to their conditions; they don't simply mean “try the second server if the first fails.” If you don't have a specific need, start with a single, easy-to-understand path. If routing rules should control connections to DNS upstreams, also check field support and proxy hostname resolution to avoid DNS and proxy connections waiting on each other.
Encrypted DNS changes how a particular leg of DNS traffic is carried; it doesn't automatically intercept apps or unify every network route. Evaluate test results alongside system DNS settings, browser settings, and current connection logs.
Troubleshoot in stages: test whether the domain resolves, whether the returned address accepts a connection, and finally which route the request takes. If only the domain lookup fails while the address is reachable, DNS is a more likely cause. Certificate errors and server rejections aren't automatically DNS problems. For more steps, see DNS leak testing and fake-ip configuration. Keep the original upstream settings so you can compare and roll back one change at a time.
Endpoints / 04
Proxy nodes and remote node collections
Node fields must match a real service
proxies stores node connection settings. Each node needs at least a name that can be referenced, plus the address, port, and authentication fields required by its protocol. A node is an actual exit; a proxy group is a way to choose one. They aren't interchangeable. If your subscription already provides nodes, you usually don't need to write them by hand. Edit the node section only to check fields, add your own service, or build a minimal test config—and keep a copy of the original first.
The example below shows the structure of an HTTP proxy. Its domain and credentials are fake, so it won't connect. server takes a hostname or address, not a https:// prefix or a web path. port is the remote service port, not your local mixed port. Use the authentication details provided by the service; changing the name or guessing another port won't produce a working config.
proxies:
- name: "Example Exit"
type: http
server: proxy.example.com
port: 443
username: "your-username"
password: "your-password"
tls: true
sni: proxy.example.com
skip-cert-verify: false
name identifies a node within the config. If you rename it, update any proxy groups and rules that reference it directly. Two nodes with the same name but different settings can also cause confusion when configs are merged. Use a distinctive prefix for locally added nodes, but don't put passwords, subscription identifiers, or personal contact details in a name. Node names may appear in the interface, logs, and screenshots, so treat them as information that could be shared.
Keep protocol and transport settings separate
Each protocol has its own set of fields. Shadowsocks commonly uses a cipher and password; Trojan has its own authentication and TLS settings; VMess, VLESS, and other protocols use their own identity and transport fields. Seeing uuid, network, or transport options in someone else's config doesn't mean they apply to your node. Confirm the protocol, check the server's transport settings, then verify what the core supports. Don't combine snippets for different protocols into one node.
TLS server names and certificate validation are a separate layer. When connecting by IP address, the server certificate may still be issued for a hostname, so set sni to the name actually configured on the service; don't make one up. Disabling certificate verification drops an important identity check and isn't a sound everyday troubleshooting shortcut. For certificate errors, check the system clock, server name, and certificate chain. See why HTTPS certificate errors occur and how to fix them.
UDP support in a node config needs to be checked at each layer. Core support for a transport doesn't mean the remote service, network, and target app all support the same path. The HTTP proxy example isn't a general-purpose UDP proxy. If web browsing works but voice, games, or another app doesn't, check the protocol actually in use and the remote service's capabilities—not just whether the client says it's connected. Confirm any unfamiliar protocol extensions in a subscription before removing them.
A provider is a node collection, not a routing policy
proxy-providers can load a set of nodes from a local file or remote resource. A proxy group uses use to reference that collection by name, then makes its nodes available as choices. The remote resource must return a node collection in a format the core can read; a full subscription file and a provider file aren't always interchangeable. A successful download only confirms that content was retrieved. Check that its format, fields, and node list are valid too.
proxy-providers:
local-nodes:
type: file
path: ./providers/local-nodes.yaml
proxy-groups:
- name: "Subscription Exit"
type: select
use:
- local-nodes
The example depends on a local file being present, with a matching proxies list inside it. Relative paths are usually resolved from the core's working directory or the config directory set by the client—not necessarily the folder currently open in your editor. Downloading, updating, and health checking are separate actions: an update retrieves node settings, a health check tests availability, and the selected policy determines which node is used. Success at one step doesn't guarantee success at another.
Real subscription URLs often contain personal access credentials. Never put them in a public config, issue screenshot, or code repository; replace them with clearly fake values when showing an example. If an update fails, first check network access, subscription status, and the response content type, then investigate YAML. If the server returns a login page or error message, it can't load as a valid node collection even if the file still has a YAML extension.
Routing choices / 05
Proxy group types and references
Start with a working manual selection path
proxy-groups organizes nodes and other policies into selectable routes. When building a config, start with a select group and manually choose a node you've confirmed works. This separates node connection failures from unexpected automatic selection behavior. Rules can target a group name, which then resolves to a node. That creates a clear reference chain without repeating a node name for every domain.
proxy-groups:
- name: "Manual Select"
type: select
proxies:
- Example Exit
- DIRECT
- name: "Web Traffic"
type: select
proxies:
- Manual Select
- DIRECT
rules:
- DOMAIN-SUFFIX,example.com,Web Traffic
- MATCH,Manual Select
This example depends on the “Example Exit” defined in the previous section. For the example domain, the rule first selects “Web Traffic.” If that group is set to “Manual Select,” it then uses the member currently selected there. Nesting groups can reduce repetitive management, but avoid circular references: if group A references B and B references A, neither has a clear exit. Trace the route from the rule target down to a node or built-in policy to check the config.
A health check doesn't guarantee a good user experience
url-test selects a member based on probe results, fallback uses the first available member in its candidate order, and load-balance distributes connections according to its policy. They solve different problems, so don't swap them just because they all automate something. A speed test measures the route to its probe target, not necessarily how a particular streaming service, work app, or account login will perform. For a specific service problem, test that service directly.
proxy-groups:
- name: "Auto Test"
type: url-test
proxies:
- Example Exit
url: https://www.gstatic.com/generate_204
interval: 300
tolerance: 50
lazy: true
This example includes only one candidate to show the structure; real comparisons need multiple working members. interval sets the probe interval, tolerance helps prevent switches over minor differences, and lazy affects probing when the group isn't in use. More frequent probes create more background requests and don't always improve the experience. On mobile devices, also consider background limits and battery use. Start with the client's sensible defaults and change them only to address a specific issue.
After an automatic group switches members, existing long-lived connections often keep using the original route; new requests use the new selection. Start a new connection when testing and check the actual route in the connection panel. Some services associate login state with the network route, and frequent switching may trigger additional verification. If a service needs a stable session, give it a separate manual group instead of making every connection share the same auto-switching behavior.
Collection references, filtering, and saved selections
A group's proxies field lists member names explicitly; use references a provider name. Filter expressions usually match names. They don't prove a node is in the location its name suggests or verify its network properties. If a subscription renames nodes, a filter may return an empty set. Check the final member list before reviewing the expression. A group still appearing in the interface doesn't mean it has any usable members.
The built-in DIRECT means a direct connection, and REJECT blocks the connection; neither is a remote node. Adding direct access to an automatic test group makes it one of the candidates. If you want direct access only as a manual fallback, put it in an outer manual group instead. Automatic failover and direct fallback aren't the same thing: allowing traffic to bypass the proxy is a routing decision that should match your use case.
Some clients save proxy group selections and try to restore them after reloading a config. This usually depends on group names, member names, and the client's persistence mechanism. If a group is renamed or a subscription removes a node, the previous selection may not return. The core's profile.store-selected can affect saved choices, but the client may manage state itself. Check the current value in the interface rather than inferring the selection from YAML order.
After adding a proxy group, check in order: its members exist, there are no circular references, rules target it correctly, and its current selection works. The group is part of the live traffic path only when all four checks pass.
Think of “rule,” “proxy group,” and “node” as three layers: match condition, selection point, and connection settings. A rule determines where traffic goes, a group determines which option to use, and a node determines how to connect to the remote service. Keeping the layers simple usually means you can replace a node later without rewriting all your routing rules.
Traffic matching / 06
Rule order, rule providers, and the catch-all
Rules are checked top to bottom; the first match sets the policy
In rule mode, regular routing rules are checked in order. The first matching rule applies its specified target. Put specific exceptions before broader rules, and use MATCH at the end to catch anything that hasn't matched. If the catch-all comes first, later rules won't be reached. A domain appearing in the list doesn't mean its rule takes effect; an earlier suffix rule, IP rule, or rule provider may already cover it.
rules:
- DOMAIN,login.example.com,Manual Select
- DOMAIN-SUFFIX,example.com,DIRECT
- DOMAIN-KEYWORD,example,Manual Select
- 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,Manual Select
The example sends one exact login domain through the proxy first, then sends the other example domains directly. If you swap the first two rules, the login domain will match the broader suffix rule first. The “Manual Select” target in the example must already exist. Commas in rule syntax separate fields; don't replace them with full-width commas or add explanatory text to the target name. After editing rules, test one known domain, check the matched rule type and policy, then test more sites.
| Type | What it matches | When to use it |
|---|---|---|
DOMAIN | Exact domain | Good for precise exceptions |
DOMAIN-SUFFIX | Domain and its subdomains | No scheme or path |
DOMAIN-KEYWORD | Keyword in a domain name | Broad match; watch for false positives |
IP-CIDR | IPv4 address range | Check whether it triggers DNS resolution |
RULE-SET | A declared rule set | Behavior must match the file format |
MATCH | Connections not matched above | Usually placed last |
Domain and IP information aren't always both available
Domain rules require the core to know the target domain. With an explicit HTTP proxy request or a SOCKS request that includes a domain, the information is usually clear. If an app resolves the name itself and then connects by IP, the core may see something different. fake-ip mappings and sniffing can help recover domains, but each has limitations and neither identifies all encrypted traffic. Check what the connection panel actually shows before trying to work out why a domain rule didn't match.
no-resolve applies to supported address-matching rules and prevents them from triggering a DNS lookup just to try that rule. A rule can still match if the connection already includes a matching address. This option doesn't disable all DNS or change queries made directly by an app. For address rules placed early in the list, whether a lookup is triggered can affect the route and response time. Consider their priority alongside domain rules instead of adding the same option to every rule by default.
Declare and use rule sets together
rule-providers defines reusable rule sets, and RULE-SET adds one to the rules list. A declaration alone doesn't route traffic. behavior determines whether the content is interpreted as domains, IP ranges, or classic rules; format specifies the file format. Both must match the actual file. Keep the remote update URL separate from the local cache path: a cache file existing doesn't mean the latest update succeeded.
rule-providers:
local-domains:
type: file
behavior: domain
format: yaml
path: ./rules/local-domains.yaml
rules:
- RULE-SET,local-domains,DIRECT
- MATCH,Manual Select
A local rule file for this setup could use the structure below. These entries form a domain set, not complete three-field routing rules, so they don't include a policy name. Putting classic rule lines into a domain set can cause parsing errors or unexpected matches. When using a third-party rule source, inspect its actual contents instead of guessing the format from its filename.
payload:
- "example.com"
- "+.example.org"
For routers, containers, or TUN traffic, confirm that the environment can identify processes before using process-based rules. A rule based on a desktop app's process name won't necessarily work on a router, which sees connections from remote devices. See the OpenWrt side-router deployment overview. Write rules for information the environment can actually observe; don't rely on fields it can't provide.
For each final check, record at least the target domain, matched rule, and actual exit. A page loading by itself doesn't tell you whether the proxy worked or a direct connection succeeded. If something behaves unexpectedly, test with one precise rule before broadening the match. Overly broad keywords or entire allowed IP ranges can affect other traffic and make the problem harder to reproduce.
Persistent changes / 07
Subscription overrides, merging, and update persistence
Understand how the final config is generated
A subscription update fetches new content from the remote source, so edits made directly to the downloaded file may be lost. Client features such as overrides, merges, extended config, and scripts usually process the original content further, but their order and merge behavior vary. Before choosing where to make a change, work out the order of the source subscription, client-wide settings, subscription-specific overrides, and final runtime config.
Ports, the system proxy, and TUN are often managed in the client interface, so subscription fields with the same names may be overridden. Rules and proxy groups may be added by subscription-specific extensions. Don't change the same field in multiple places, or it will be hard to tell which value wins after a reload. Choose one place to manage it, then check the actual runtime config. An interface may show the value you entered, not necessarily the value the core finally reads.
Treat mapping merges and list merges separately
Mappings may be merged recursively field by field or replaced as a whole. For example, an override containing only dns.enable may preserve the original upstreams in one mechanism but replace the entire DNS section in another. Lists need even more care: rules, nodes, and proxy groups may be appended, prepended, merged by name, or replaced. There's no universal guarantee that “merging YAML preserves the original lists.” Check the current client documentation and generated config.
# A snippet from the original config
dns:
enable: true
enhanced-mode: fake-ip
nameserver:
- https://dns.alidns.com/dns-query
# Override intent: change only this setting
# Enter this through the client's separate override feature; don't append it to the original file
# dns:
# enhanced-mode: redir-host
The example shows the override intent as comments to avoid creating a duplicate key if you paste it into an existing mapping. In practice, enter the corresponding field through the client's separate override feature, then check whether nameserver is still there. If it's gone, the current mechanism isn't merging recursively as expected, so you'll need to include the entire DNS section. Don't change upstreams at the same time; isolating the test makes the merge behavior clear.
Rule order, deduplication, and naming conflicts
Local exception rules usually need to go before subscription rules so they can match before broader rules or MATCH. If a tool only supports appending, an exception added at the end may never take effect. Check the generated order, confirm the catch-all is still last, then test the actual match. Don't reorder rules just to deduplicate them: order is part of routing behavior, and alphabetical sorting changes it.
When adding a proxy group, choose a name unlikely to conflict with the subscription. If an update introduces a group with the same name, merging by name could change its members, while whole-list replacement could remove the original selection. Also account for remote node renames when filtering by node name. Binding important traffic to a stable local policy entry, then letting that entry select a subscription group, is usually easier to maintain than making each custom rule depend directly on a remote node name.
Script-based overrides are more flexible and more likely to break the config structure. Before using one, find out whether the script receives an object or text, whether it must return a complete config, and whether the client keeps the previous config or stops loading if the script fails. Don't copy scripts from untrusted sources that fetch external resources. Configs may contain subscription URLs and credentials, so treat an extension script as code that can access them—not as an ordinary comment.
Test persistence with one subscription update
After setting up an override, reload the config and verify the feature, then manually run a subscription update. Compare the runtime config before and after, checking that custom rules, group names, DNS upstreams, and ports are as expected. A successful update doesn't prove the override worked, and an old config still running doesn't mean the new one loaded. Check the logs for generation and reload results, and make sure you're testing the active file—not an old instance left running after an update failed.
Keep three things: a working copy of the original config, the separate override content, and a note explaining why you changed it. To roll back, restore the original first, then enable the override separately. This keeps subscription changes distinct from local edits while troubleshooting.
Your notes can be brief, but include the field changed, expected result, and how you verified it. For example, “keep real DNS results for devices on the local network” is more useful than “optimize DNS.” If an issue comes back later, you can decide whether the change is still needed. Disable and test old overrides with no clear purpose one at a time instead of keeping them indefinitely and adding more patches on top.
When moving to another client, don't just copy the generated runtime file. Control interfaces, file paths, and dashboard settings injected by the old client may not work in the new environment. Start on the client downloads page and choose the right platform; Clash Plus is a good first choice for most users. Import the original subscription again, migrate only the local overrides you still need, then follow this page's validation steps.
Validation loop / 08
Config checks, connection tests, and recovery
Check syntax first, then verify traffic interception
After saving, use the client's built-in validation or reload feature and check for parse errors, unknown references, and invalid file paths. If you're running mihomo directly, run the test command below from the correct working directory. Adjust the executable name and path for your system. A successful command doesn't prove that every remote node can connect, and it doesn't replace checks for system proxy or TUN interception.
mihomo -t -d . -f ./config.yaml
If an error includes a line number, check indentation at that level and the quotes or colon on the preceding line. The parser may point to where it noticed the problem, while the actual cause is an unclosed string earlier in the file. If a policy can't be found, check names across the node, group, and rule layers. If a resource file is missing, check the working directory, relative path, and file permissions. Don't replace the whole config just because you see a red error message.
Isolate local issues with a minimal direct-connection config
The example below is a standalone minimal config for testing the core listener and basic rule path. It doesn't include a remote proxy and won't route traffic through a proxy automatically. Save and load it separately so you don't overwrite your subscription. If the original client is using the same port, stop that instance first, or choose a different test port and update the test command as well.
mixed-port: 7890
allow-lan: false
bind-address: "127.0.0.1"
mode: rule
log-level: info
ipv6: false
rules:
- MATCH,DIRECT
If even the minimal config won't start, check for a port conflict, permission issues, or a problem with the executable. Once it starts, send an explicit proxy request to an address that's already reachable on your current network. The curl command may be mapped differently on different operating systems; on Windows, use curl.exe explicitly. The command below doesn't change the system proxy; it tests only the specified local HTTP proxy endpoint.
curl --proxy http://127.0.0.1:7890 --head https://example.com
An HTTP response means the request completed the corresponding route, but check the status against what the target site should return. If the local port refuses the connection, check the listener and port. If the proxy connection succeeds but times out, investigate DNS, routing, and the exit. For certificate validation errors, check certificates and system time. Don't blame every failure on a bad node or hide the cause by disabling certificate validation.
Restore the real config one layer at a time
Once the minimal test works, restore the original config and first test one known-good node with one manual group. Then add DNS settings, rule sets, and overrides. Repeat the same test after each layer and note any log differences to pinpoint where the problem starts. If an explicit proxy request works but the usual browser doesn't, check the app's proxy settings or traffic interception scope before rewriting node settings.
TUN also involves a virtual network adapter, routes, permissions, and DNS interception. After enabling it, test domain requests and direct address connections separately, and check that local network resources remain reachable. Other VPNs, virtual machines, and security software can also change routes, so don't switch several networking tools at once. The system proxy and TUN serve different desktop use cases. Validate one clear route first, then decide whether to combine them for your apps.
To verify a rule, check the matched rule and final exit for a new connection—not just the global toggle. Browser caching, connection reuse, and background requests can make one page refresh include several different routes. If necessary, close the target app and try again. Before sharing screenshots, hide account details, subscription URLs, and node credentials. Keep only the fields, error text, and config section needed to diagnose the issue.
If there's no internet, restore service before troubleshooting further
For recovery, first turn off any traffic interception feature you just enabled, then restore the system proxy, and finally load the last known-good config. If access still fails after quitting the client, check whether the OS has a proxy setting pointing to a closed port. Restore routes left by TUN or other tools using their normal shutdown procedures. Don't install several clients in a row to try to fix the issue; that can create more port and system setting conflicts.
Once the network is working again, retry changes one at a time, starting with the most recent. Keep subscription updates, core switches, rule changes, and OS network changes separate where possible. If you keep seeing “connected, but websites won't load,” follow the eight-step no-internet troubleshooting checklist to check the subscription, exit, system proxy, and DNS. To retrace the main setup, return to the subscription import and connection guide.
A config change is complete when the file loads, the target request matches as expected, the change survives a subscription update, and you know how to roll it back. Confirm all four before moving on.