Config File Structure and Reading Order
Clash's config.yaml is a YAML text file whose top-level fields the client reads in order at startup. The best way to understand it is to split the file into four sections from top to bottom: general settings, DNS configuration, proxy definitions, and proxy groups with rules.
YAML syntax rules are straightforward: use spaces for indentation (never tabs), keep indentation consistent within the same level, and separate keys from values with a colon followed by a space. Field order does not affect parsing, but proxies, proxy-groups, and rules reference each other: any node name used in a proxy group must be defined in proxies first, and any group name used in a rule must be defined in proxy-groups first. Writing the file in the order "general settings → DNS → proxies → proxy-groups → rules" matches how you read it and avoids errors from referencing undefined names.
Here is the skeleton of a minimal working config:
port: 7890
socks-port: 7891
allow-lan: false
mode: rule
log-level: info
dns:
enable: true
nameserver:
- 223.5.5.5
proxies:
- name: "Example Node"
type: ss
server: 203.0.113.10
port: 443
cipher: aes-256-gcm
password: "example-password"
proxy-groups:
- name: PROXY
type: select
proxies:
- "Example Node"
- DIRECT
rules:
- DOMAIN-SUFFIX,github.com,PROXY
- GEOIP,CN,DIRECT
- MATCH,PROXY
This skeleton omits optional fields like ipv6 and external-controller, but it already covers the full flow: start the client, route traffic through the proxy, and split traffic by rules.
General Fields: Port, Mode, and Logging
The top-level general fields control the client's basic behavior. The table below lists the most commonly used ones:
| Field | Example Value | Purpose |
|---|---|---|
| port | 7890 | HTTP proxy listening port |
| socks-port | 7891 | SOCKS5 proxy listening port |
| allow-lan | false | Allow connections from LAN devices |
| mode | rule | Running mode: rule / global / direct |
| log-level | info | Log level: silent / error / warning / info / debug |
| ipv6 | false | Enable IPv6 traffic |
| external-controller | 127.0.0.1:9090 | External controller API listen address |
mode is the field people most often confuse because it has three possible values. In rule mode, traffic is matched against the rules section one by one and forwarded on the first hit — this is the mode you use every day. In global mode, all traffic goes through the first proxy group in proxy-groups, bypassing rule matching entirely. In direct mode, all traffic connects directly, effectively bypassing the proxy for everything — useful when troubleshooting node failures.
port and socks-port can be enabled at the same time, or you can enable only one of them. If port 7890 is already taken by another program on your machine, the startup log will show bind: address already in use — just change the port to an unused value like 7897.
external-controller provides a RESTful API. Clash Verge's dashboard, Yacd, and Metacubexd all read and modify the configuration through this address. By default it binds to 127.0.0.1, allowing access only from the local machine. If you set the secret field, you need to include the corresponding request header when calling the API.
For log-level, info is recommended for daily use; switch to debug temporarily when troubleshooting. debug prints rule-match details for every connection:
[TCP] dial Match(DomainSuffix): github.com → PROXY
[UDP] dial Match(GEOIP): 8.8.8.8 → DIRECT
This kind of log tells you exactly which rule picked up a connection, making it the most effective way to debug traffic splitting.
DNS Section: How nameserver and fallback split the work
The dns section decides which path domain resolution takes and is the second most important part of the configuration. The core fields are:
| Field | Example Value | Purpose |
|---|---|---|
| enable | true | Take over the system DNS |
| listen | 0.0.0.0:53 | DNS server listen address |
| nameserver | 223.5.5.5, 119.29.29.29 | Default resolver servers |
| fallback | 8.8.8.8, 1.1.1.1 | Fallback resolver servers |
| fallback-filter | geoip: true | Conditions that trigger fallback |
| default-nameserver | 223.5.5.5 | Servers used to resolve proxy node domains |
nameserver handles most domains. In mainland China, use public DNS servers such as 223.5.5.5 (Alibaba) and 119.29.29.29 (Tencent) — they respond quickly and are not polluted.
fallback is used for polluted domains: when an IP returned by nameserver is classified as a reserved address by geoip, the client re-resolves using the servers in fallback. In simple setups, setting fallback to 8.8.8.8 and 1.1.1.1 is enough.
There is a common pitfall here: if the nodes in proxies use domain names instead of IPs, the client also goes through the dns section when resolving node domains. If a node domain is polluted, every node will fail to connect. So default-nameserver must be set to a reliable DNS server in mainland China, and you should not hand node domain resolution over to the fallback flow. In TUN mode, enable under the dns section must be true; otherwise system traffic cannot resolve domains correctly once TUN is enabled.
proxies Section: How to Define Each Protocol
proxies is an array where each element defines a proxy node. All protocols share four base fields — name, type, server, and port — while protocol-specific fields vary.
An SS node has the fewest fields:
- name: "SS-Tokyo"
type: ss
server: 203.0.113.10
port: 443
cipher: aes-256-gcm
password: "your-password"
Common cipher values include aes-256-gcm, chacha20-ietf-poly1305, and 2022-blake3-aes-256-gcm. The password must match the one provided by your subscription provider, or the handshake will fail immediately.
A VMess node adds uuid, alterId, and transport-layer fields:
- name: "VMess-Singapore"
type: vmess
server: 203.0.113.20
port: 443
uuid: "550e8400-e29b-41d4-a716-446655440000"
alterId: 0
cipher: auto
network: ws
ws-opts:
path: "/path"
headers:
Host: "example.com"
alterId is usually 0 in newer server implementations. When network is ws, you must also configure path and Host under ws-opts; both must match your subscription provider's settings exactly, or the tunnel will fail to establish after the TLS handshake.
A Trojan node has a structure similar to VMess but without uuid:
- name: "Trojan-Los Angeles"
type: trojan
server: 203.0.113.30
port: 443
password: "your-password"
sni: "example.com"
skip-cert-verify: false
sni is used for the TLS SNI extension and must match the certificate domain. skip-cert-verify defaults to false and is not recommended to change to true unless the node's certificate is invalid and you accept the risk.
A Hysteria2 node additionally requires up and down bandwidth parameters:
- name: "Hysteria2-Hong Kong"
type: hysteria2
server: 203.0.113.40
port: 443
password: "your-password"
up: "50 Mbps"
down: "200 Mbps"
sni: "example.com"
up/down declare the client's available bandwidth. Values that are too low will limit transfer speed; values that are too high may cause congestion.
proxy-groups: How Selection Logic Nests
proxy-groups defines policy groups. The proxies list inside a group can reference nodes or other groups. Four types are commonly used:
| Type | Behavior | Use Case |
|---|---|---|
| select | Manually select a node | Daily driver, manual switching |
| url-test | Tests latency on a schedule and automatically picks the lowest-latency node | For automatic best-node selection |
| fallback | Uses nodes in list order, switches to the next on failure | When a fixed priority is needed |
| load-balance | Distributes traffic evenly across multiple nodes | Aggregating bandwidth across multiple nodes |
The select type is the most straightforward:
- name: PROXY
type: select
proxies:
- "SS-Tokyo"
- "VMess-Singapore"
- "Trojan-Los Angeles"
- DIRECT
- REJECT
url-test requires additional settings for the test URL and interval:
- name: Auto
type: url-test
url: "https://www.gstatic.com/generate_204"
interval: 300
tolerance: 50
proxies:
- "SS-Tokyo"
- "VMess-Singapore"
interval is in seconds; 300 means a speed test every 5 minutes. tolerance means no switch when the latency difference is less than 50ms, preventing frequent flapping.
Proxy groups can be nested — this is an advanced pattern:
proxy-groups:
- name: PROXY
type: select
proxies:
- Auto
- "SS-Tokyo"
- DIRECT
- name: Auto
type: url-test
proxies:
- "SS-Tokyo"
- "VMess-Singapore"
Here the first option in the PROXY group is the Auto group. When Auto is selected, actual traffic is decided automatically by url-test. There is no hard limit on nesting depth, but it is recommended to keep it to two levels or fewer; otherwise, figuring out which node is currently in use becomes difficult.
rules Section: Match Order Determines Traffic Direction
rules is the last section of the config and the core of traffic splitting. Clash matches rules one by one in order and stops at the first hit — it does not keep going. This makes rule order extremely important.
Common rule types:
| Rule Prefix | Matches | Example |
|---|---|---|
| DOMAIN | Full domain | DOMAIN,www.google.com,PROXY |
| DOMAIN-SUFFIX | Domain suffix | DOMAIN-SUFFIX,google.com,PROXY |
| DOMAIN-KEYWORD | Domain keyword | DOMAIN-KEYWORD,github,PROXY |
| IP-CIDR | IPv4 CIDR | IP-CIDR,192.168.0.0/16,DIRECT |
| GEOIP | Country or region | GEOIP,CN,DIRECT |
| PROCESS-NAME | Process name | PROCESS-NAME,wechat.exe,DIRECT |
| MATCH | Fallback | MATCH,PROXY |
A production-ready rules example:
rules:
- DOMAIN-SUFFIX,local,DIRECT
- IP-CIDR,127.0.0.0/8,DIRECT
- IP-CIDR,192.168.0.0/16,DIRECT
- IP-CIDR,10.0.0.0/8,DIRECT
- DOMAIN-SUFFIX,cn,DIRECT
- GEOIP,CN,DIRECT
- DOMAIN-SUFFIX,google.com,PROXY
- DOMAIN-SUFFIX,youtube.com,PROXY
- DOMAIN-KEYWORD,github,PROXY
- MATCH,PROXY
Notice the order above: LAN and mainland China traffic is picked up by DIRECT first, then foreign domains are matched to PROXY, and finally MATCH catches everything else. If you put DOMAIN-SUFFIX,google.com,PROXY before GEOIP,CN,DIRECT, and google.com resolves to a mainland China IP (for example, because a CDN scheduled it to a node in mainland China), it would incorrectly connect directly.
The rule target (the last field) must be a group name defined in proxy-groups, or one of the built-in targets DIRECT and REJECT. Referencing a group name that does not exist will cause config validation to fail immediately.
Rule order is priority. Clash matches rules one by one starting from the first line of rules and stops at the first hit. If you put broad rules (such as GEOIP,CN,DIRECT) before specific rules (such as DOMAIN-SUFFIX,google.com,PROXY), the latter will never get a chance to match.
Validating Your Config and Common Errors
After writing config.yaml, validate the syntax from the command line before starting the client — this saves a lot of debugging time. The mihomo core validation command:
./mihomo -t -f config.yaml
Output showing configuration file ... test is successful means it passed. Clash Verge's "Settings" → "Parameters" also provides a config check entry, which essentially calls the same validation logic.
Common errors, ordered by how often they occur:
- Indentation uses tabs. YAML only accepts spaces — enable "Convert tabs to spaces" in your editor to avoid this.
- Missing space after the colon. port:7890 is parsed as a string key instead of the port field.
- Special characters in passwords are not quoted. If a password contains characters like #, :, or *, it must be wrapped in double quotes; otherwise YAML treats them as comments or structural symbols.
- Referencing an undefined node name or group name. Check that the name values in proxies exactly match the proxies lists in proxy-groups.
- Defining duplicate nodes with the same name. The later definition overrides the earlier one, which often creates the illusion that "you changed the config but nothing changed."
Compare the validation command output against the checklist above, and most config issues can be located within 5 minutes.