Understand the configuration processing flow first
A Clash or mihomo configuration is more than a simple node list. When the core starts, it parses the YAML, creates local listening ports, initializes DNS, proxy nodes, and proxy groups, then matches connections from top to bottom using rules. Passing a syntax check does not guarantee that references between sections are valid: a rule may point to a missing policy group, while a policy group may reference a node that has been renamed.
To understand a configuration, divide it into five layers: basic runtime settings handle ports and operating mode; the DNS section handles name resolution; proxies defines individual outbound proxies; proxy-groups organizes them into selectable or automatically tested policies; and rules determines which policy handles each type of connection. Subscription configurations may also include proxy-providers and rule-providers to load nodes or rule sets from external files.
Three YAML fundamentals that cause the most errors
- Use spaces for indentation; never use tabs. Two spaces per indentation level is common.
- Leave a space after the colon in most cases, for example
mode: rule; list items start with a hyphen followed by a space. true,false, numbers, and strings have different meanings. Write a port as7890, while names or passwords containing special characters are safer in quotes.
In most desktop clients, open the YAML through “Profiles” → select the active profile → “Edit.” After saving, go to “Profiles” → “Reload” or “Apply.” Button names vary slightly by client, but the order is the same: save first, confirm that the core reports no parsing errors, then test the proxy connection. For troubleshooting, open “Logs” → “Log Level” → “debug,” then switch back to info after identifying the issue to avoid recording large amounts of debug output for too long.
General fields: ports, mode, and the control interface
A configuration usually starts with local listeners and core runtime settings. Here is an example suited to a desktop environment: the HTTP proxy listens on 7890, the SOCKS5 proxy listens on 7891, and the external control interface is bound only to the local loopback address:
port: 7890
socks-port: 7891
allow-lan: false
bind-address: "*"
mode: rule
log-level: info
ipv6: false
external-controller: 127.0.0.1:9090
secret: "change-this-token"
| Field | What it does | Typical check |
|---|---|---|
port |
Provides the HTTP proxy listening port | A common system proxy setting is 127.0.0.1:7890 |
socks-port |
Provides SOCKS4 and SOCKS5 proxy access | Apps that support SOCKS5 can connect to 127.0.0.1:7891 |
mixed-port |
Accepts both HTTP and SOCKS connections on one port | When enabled, a separate port for the same purpose is usually unnecessary |
allow-lan |
Determines whether devices on the local network can connect to the local proxy port | After enabling it, also check the bind address, system firewall, and access controls |
mode |
Choose rule, global, or direct |
rule is usually the right choice for everyday use |
external-controller |
Provides a control API for a graphical interface or panel | For local-only use, binding to 127.0.0.1 is more appropriate |
mode: rule sends each connection through rule matching; global sends all connections to the global policy; direct connects directly. The mode is not a proxy protocol and does not change the node’s own parameters. If a node passes latency tests but a specific site does not use the proxy, first check whether the mode was accidentally switched to direct, then see which policy the rules selected.
LAN sharing requires more than flipping one switch
After changing allow-lan to true, a phone or another computer must use the LAN address of the device running Clash, such as 192.168.1.20:7890, rather than its own 127.0.0.1. You must also allow TCP port 7890. If the computer receives its address through DHCP, it may change after reconnecting to the router, so consider creating a static lease for the device in the router.
DNS: resolution paths and fake-ip mode
DNS settings determine how domains are resolved to IP addresses and can affect whether domain rules match reliably. Common mihomo enhanced modes include fake-ip and redir-host. The former returns a mapped address from a reserved pool to the application, then restores the domain when the core receives the connection; the latter is closer to traditional real-address resolution. In TUN mode, fake-ip often preserves more domain information, but some LAN services, connectivity checks, and apps that require real addresses need to be added to the filter list.
dns:
enable: true
listen: 0.0.0.0:1053
ipv6: false
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://1.1.1.1/dns-query
proxy-server-nameserver:
- https://dns.alidns.com/dns-query
fake-ip-filter:
- "*.lan"
- "*.local"
- "time.*.com"
- "+.msftconnecttest.com"
default-nameserver mainly handles bootstrap resolution, so it should generally contain DNS servers written as plain IP addresses. If an encrypted DNS server is specified by hostname, the core must first know how to resolve that hostname. nameserver lists the upstream servers for regular queries. In mihomo, proxy-server-nameserver can resolve proxy server hostnames separately, avoiding the dependency loop where the DNS query waits for a proxy node whose address has not yet been resolved.
When DNS is valid but websites still do not open
- Check the logs for
dns resolve failed, timeouts, or certificate connection errors. - Confirm that system or TUN DNS requests actually reach the core’s listener instead of continuing to use an old LAN DNS server.
- Temporarily change
enhanced-modetoredir-hostand test again to determine whether the issue is related to fake-ip compatibility. - Check
fake-ip-filter. Add only domains with confirmed compatibility issues, rather than using an overly broad wildcard rule. - Test the domain and IP separately. If the IP works but the domain does not, investigate DNS first; if both fail, continue with the policy group and node connection.
The 198.18.0.1/16 range in the example is a common fake-ip pool. Seeing this address in an application does not mean the website is hosted there; the core maintains a temporary mapping. If packet captures repeatedly show 198.18.x.x, correlate them with the original domain in the Clash logs instead of treating the address as the remote server address.
proxies: writing individual nodes in YAML
proxies is a list of node objects. Each object needs at least a unique name, a protocol type, a server address server, and a port port; the remaining fields depend on the protocol. The examples below show the structure for Shadowsocks and SOCKS5; the sample domains and credentials are for format illustration only:
proxies:
- name: "Tokyo-SS"
type: ss
server: ss-node.example.com
port: 443
cipher: aes-128-gcm
password: "demo-password"
udp: true
- name: "Local-SOCKS"
type: socks5
server: 192.168.1.30
port: 1080
username: "proxy-user"
password: "demo-password"
udp: false
Field names must follow the protocol definition. Shadowsocks uses cipher and password; SOCKS5 may include username and password. Other protocols may require TLS, transport-layer, server-name, and related parameters. The fact that two nodes appear as one line in a graphical interface does not mean fields from one protocol can be copied directly into another.
A node name is really a reference key
name is not just a display label; policy groups reference it literally. If a node is named “Tokyo-SS” but the group contains “Tokyo SS,” the differing character makes the reference invalid. Avoid giving a node the same name as a policy group, or logs and rule targets become difficult to interpret quickly.
- Keep names unique within the same list, using a consistent format such as “region-protocol-number.”
- Quote names containing colons, hashes, asterisks, or leading or trailing spaces so YAML does not interpret those characters as syntax.
udp: trueonly means that UDP is enabled in the node configuration; actual support also depends on the protocol, server, and traffic-capture method.- When the server address is a domain, also check the DNS path used to resolve the node hostname.
proxy-groups: turning nodes into usable policies
Rules usually point to a policy group rather than a fixed node. This lets you choose outbound traffic independently for purposes such as work services, streaming, and the default proxy. Policy groups are also the main controls on a client’s proxy page: users typically switch the selected member of a select group, not the rules themselves.
proxy-groups:
- name: "Manual Select"
type: select
proxies:
- "Auto Test"
- "Tokyo-SS"
- "Local-SOCKS"
- DIRECT
- name: "Auto Test"
type: url-test
proxies:
- "Tokyo-SS"
- "Local-SOCKS"
url: "https://www.gstatic.com/generate_204"
interval: 300
tolerance: 50
lazy: true
- name: "Default Proxy"
type: select
proxies:
- "Manual Select"
- "Auto Test"
- DIRECT
| Group type | Selection logic | Best for |
|---|---|---|
select |
User manually selects a member | When you need to explicitly choose a region, route, or direct connection |
url-test |
Periodically tests a URL and selects the member with lower latency | When there are many nodes and you want automatic selection |
fallback |
Uses available members in order and switches when the current member fails | When fixed priority and failover matter |
load-balance |
Distributes different connections across multiple members according to a specified strategy | When you need to spread connections and the upstream conditions allow it |
In the example, interval: 300 runs a periodic test every 300 seconds, while tolerance: 50 avoids frequent switching when latency differences are small. Latency measures request time to the test URL, not download bandwidth. In one test, node A measured 86 ms and node B 112 ms; this only means A responded faster to that test address. Results may reverse when accessing a service in another region.
Check for circular policy-group references
A policy group can reference other policy groups, but they must not form a cycle. For example, if “Default Proxy” contains “Manual Select” and “Manual Select” contains “Default Proxy,” the core cannot determine the final outbound path. When reading a complex configuration, trace downward from each rule target: which group does the rule reference, what members does that group contain, and can those members ultimately resolve to a node or DIRECT/REJECT?
rules: determine the destination of each connection in order
rules is an ordered matching list. Each connection is checked from the first rule downward; once a rule matches, its specified policy is used immediately and later rules are ignored. Put specific rules before broad ones, and usually end with MATCH to catch traffic that matched nothing earlier.
rules:
- DOMAIN,api.example.com,Default Proxy
- DOMAIN-SUFFIX,example.net,Default Proxy
- DOMAIN-KEYWORD,video,Manual Select
- IP-CIDR,192.168.0.0/16,DIRECT,no-resolve
- IP-CIDR,10.0.0.0/8,DIRECT,no-resolve
- GEOIP,CN,DIRECT
- MATCH,Default Proxy
DOMAIN matches the complete domain; DOMAIN-SUFFIX matches the specified domain and its subdomains; DOMAIN-KEYWORD may match any domain containing the keyword, so it has broader coverage and a higher risk of false matches. IP-CIDR matches by address range, GEOIP relies on the geographic database loaded by the core, and MATCH is the fallback rule.
no-resolve is commonly used with IP rules to prevent extra domain resolution just to obtain an IP for matching. It is not a required suffix for every rule. Domain rules already match by domain, while scenarios that need an address result still require normal resolution.
Why a rule can look correct but never match
- A broader rule comes first: if
DOMAIN-SUFFIX,example.com,DIRECTappears first, the laterDOMAIN,api.example.com,Default Proxycan never run. - The app connects directly to an IP: the log shows only the destination address and no domain, so domain rules cannot participate. Use IP rules or adjust how DNS traffic is intercepted.
- The policy name is misspelled: the rule target must exactly match a name in
proxy-groups. - The configuration was not reloaded: you edited a local file, but the client is still running the old configuration. Save it, select “Profiles” → “Reload,” and check the logs.
- The connection was not re-established: an existing TCP or QUIC session may continue using the old path. Close the app’s connections, wait a few seconds, and test again for clearer results.
provider sections: move nodes and rules into external files
When nodes come from a subscription or a rule set is large, use providers. proxy-providers manages external node collections, while rule-providers manages rule sets. A provider does not decide traffic routing by itself: a node provider must still be referenced by a policy group, and a rule provider must be added to rules through RULE-SET.
proxy-providers:
airport:
type: http
url: "https://subscription.example.com/clash.yaml"
path: ./providers/airport.yaml
interval: 3600
health-check:
enable: true
url: "https://www.gstatic.com/generate_204"
interval: 600
rule-providers:
private-sites:
type: http
behavior: domain
format: yaml
path: ./rules/private-sites.yaml
url: "https://rules.example.com/private-sites.yaml"
interval: 86400
A node provider can be imported with use inside a policy group; a rule provider must be written as RULE-SET,private-sites,DIRECT. behavior must match the rule file’s contents: domain is for domain rules, ipcidr for address ranges, and classical for classic rule expressions. If the declared format does not match the file, the provider may download successfully but fail during parsing.
proxy-groups:
- name: "Subscription Auto Select"
type: url-test
use:
- airport
url: "https://www.gstatic.com/generate_204"
interval: 300
rules:
- RULE-SET,private-sites,DIRECT
- MATCH,Subscription Auto Select
interval: 3600 means the node provider attempts an update every 3,600 seconds; the 86400 in the rule example means 24 hours. Set update intervals according to how quickly the source changes: intervals that are too short create unnecessary network requests, while intervals that are too long delay updated nodes or rules.
How to connect the pieces into a readable minimal configuration
Combining the structure above produces a minimal configuration that is useful for learning. It is not a ready-made universal profile, because the node protocol, DNS upstreams, and routing targets still depend on your environment, but it clearly shows how the fields reference one another:
mixed-port: 7890
allow-lan: false
mode: rule
log-level: info
ipv6: false
external-controller: 127.0.0.1:9090
dns:
enable: true
listen: 0.0.0.0:1053
ipv6: false
enhanced-mode: fake-ip
fake-ip-range: 198.18.0.1/16
default-nameserver:
- 223.5.5.5
nameserver:
- https://dns.alidns.com/dns-query
fake-ip-filter:
- "*.lan"
- "*.local"
proxies:
- name: "Tokyo-SS"
type: ss
server: ss-node.example.com
port: 443
cipher: aes-128-gcm
password: "demo-password"
udp: true
proxy-groups:
- name: "Proxy"
type: select
proxies:
- "Tokyo-SS"
- DIRECT
rules:
- DOMAIN-SUFFIX,example.net,Proxy
- IP-CIDR,192.168.0.0/16,DIRECT,no-resolve
- GEOIP,CN,DIRECT
- MATCH,Proxy
When connecting to api.example.net, the core first resolves the domain through the DNS section, then DOMAIN-SUFFIX,example.net,Proxy matches and selects the “Proxy” group. If “Tokyo-SS” is currently selected in that group, the connection is sent through that node. If you switch the group to DIRECT on the proxy page, the same rule still matches, but the final connection is direct. This is why rules and policy selection should be understood separately.
Validation order after saving
- First check YAML indentation, list markers, and spaces after colons.
- Confirm that every rule target exists in a policy group and that every group member resolves to a node or built-in policy.
- Reload the configuration and check the startup log for field errors, failed references, or ports already in use.
- Confirm that ports such as
7890,9090, and1053are not occupied by other programs. - Test one direct destination and one proxied destination separately, then verify the actual matched rules in the logs.
- Enable the system proxy or TUN only at the end, so you do not change several variables at once and make troubleshooting harder.
If the core exits immediately after startup, the most effective approach is not to delete large sections at random, but to narrow the scope section by section: keep the general fields and one working node first, then add the policy group, DNS, and finally the rules and providers. Reload after each addition to pinpoint the faulty field. For a 200-line configuration, restoring five sections step by step usually takes only 5–8 reloads and is faster than guessing line by line.
Common mistakes and maintenance tips
Using a subscription link as a node address
A subscription URL returns a collection of nodes or a complete configuration, not the server value for one node. Give a complete subscription to the client’s subscription manager or proxy-providers; write an individual node under proxies using its protocol fields. These are different data layers and cannot be substituted for each other.
Looking only at latency, not at policy matches
A successful node latency test means the core can reach the test address through that node, but a real connection may be sent to DIRECT, another policy group, or another node by the rules. Troubleshoot by confirming the rule target in the logs first, then checking the group’s current selection, and finally inspecting the node connection result.
Blaming every problem on DNS
DNS handles only part of the resolution path. An occupied port, disabled system proxy, unmanaged TUN route, incorrect rule order, empty policy group, or mismatched node parameters can all appear as “the webpage will not open.” Layered checks are more reliable than repeatedly replacing DNS addresses.
Directly editing a configuration that updates automatically
After a subscription refresh, manually added rules, policy groups, or DNS fields may be replaced by regenerated content. For long-term maintenance, separate the layers clearly: subscriptions update nodes, local overrides hold fixed parameters, rule providers manage large rule sets, and a small number of personal rules stay in a controlled pre-rule section. This avoids merging the entire YAML again whenever nodes are updated.