How to Read This Page
Everything a Clash-family client does is driven by a single YAML config file: which port it listens on, how DNS resolves, what proxies exist, and which rules route traffic. The switches and dropdowns in a GUI client are essentially a visual editor for this file — understanding the fields themselves is what lets you get unstuck when the UI options run out, or when a subscription needs manual tweaking.
This site divides config-related content deliberately: the usage guide covers the shortest path from download to a working connection without dwelling on field details; FAQ collects standalone Q&As searchable by symptom; this page is a systematic field reference, organized in the top-to-bottom order fields appear in the config file — good for a full read-through to build a mental model, and equally good for jumping straight to a section via the table of contents above.
Field semantics here are based on the mihomo kernel. Since the original Clash kernel was archived, mainstream GUI clients — including our top pick Clash Plus, plus Clash Verge Rev, FlClash, and Clash Nyanpasu — have all switched to mihomo, and the fields described here apply across them; a handful of mihomo-only extensions won't work on very old kernels, and those are flagged inline. Platform coverage and download links are on the downloads page; for how these projects relate to each other, see "How Clash, mihomo, and Verge Rev Relate to Each Other".
If you use a subscription-based service: the subscription link itself returns a complete config.yaml, with proxies and rules maintained by your provider. Reading this page helps you understand what's in it, but editing the subscription file directly gets overwritten on the next update. See Section 8, "Override & Merge," for the correct way to make persistent changes.
YAML Structure Overview
Top-Level Structure: A Minimal Working Skeleton
config.yaml is a mapping made up of several top-level keys. Stripped of every optional field, a minimal runnable config needs just five parts: runtime settings, DNS, the proxy list, the proxy group list, and the rule list. Here's the skeleton, with each part expanded in the sections below:
mixed-port: 7890
allow-lan: false
mode: rule
log-level: info
external-controller: 127.0.0.1:9090
dns:
enable: true
enhanced-mode: fake-ip
nameserver:
- https://223.5.5.5/dns-query
proxies:
- name: "HK-01"
type: ss
server: hk01.example.com
port: 8388
cipher: aes-256-gcm
password: "your-password"
proxy-groups:
- name: "PROXY"
type: select
proxies:
- HK-01
- DIRECT
rules:
- DOMAIN-SUFFIX,example.com,PROXY
- GEOIP,CN,DIRECT
- MATCH,PROXY
Top-level keys have no required loading order — the kernel just reads whichever keys exist — but convention places them as "runtime settings → dns → proxies → proxy-groups → rules," matching the logical order traffic is processed in, which makes reading and troubleshooting easier. The table below lists common top-level keys and their types:
| Top-level key | Type | Purpose | Required |
|---|---|---|---|
mixed-port and other port keys | Integer | Local listening port | At least one |
mode | Enum string | Routing mode (rule/global/direct) | Recommended to declare explicitly |
dns | Mapping | Built-in DNS module | Required for TUN/transparent proxy |
proxies | Array | Proxy definitions | Yes |
proxy-groups | Array | Proxy group definitions | Yes |
rules | String array | Routing rules, matched top-down | Required in rule mode |
proxy-providers | Mapping | External proxy sets (split subscriptions) | No |
rule-providers | Mapping | External rule sets | No |
tun | Mapping | Virtual network adapter parameters | No |
Formatting Rules: Indentation, Quoting, and Common Typos
YAML is format-sensitive, and a large share of config errors trace back to formatting, not incorrect fields. Three hard rules: first, indentation must use spaces only — two spaces consistently; tabs cause an outright parse failure, and the reported line number is often far from the actual mistake, making it hard to locate; second, a colon must be followed by a space — port:7890 is invalid; third, array items start with "dash + space," and the dash's indentation level determines which key it belongs to.
Strings don't need quotes by default, but three situations require them: the value contains a colon, hash, or other special character; the value is purely numeric but should be treated as a string; or it's a sensitive value like a password or UUID that might start with a special character — quoting everything with double quotes is the low-effort habit to adopt. Also watch for a YAML 1.1 legacy trap: unquoted yes, no, on, and off get parsed as booleans by some parsers — quote them if a proxy name happens to match one of these words.
Copying a config snippet from a webpage can leave rich-text editors replacing regular spaces with non-breaking spaces (U+00A0), which are invisible to the eye but cause a "found character that cannot start any token" parse error. Paste into a plain-text editor, or select-all and re-indent after pasting.
General Fields
The Port Family: mixed-port, port, and socks-port
Port fields determine how local apps hand traffic to the kernel. mixed-port is the current recommended setting: a single port accepts both HTTP and SOCKS5 inbound traffic, so the system proxy only needs to point at this one port — this is what most clients generate by default, conventionally set to 7890. The legacy port (HTTP only) and socks-port (SOCKS5 only) fields still work and can coexist with mixed-port, listening on separate ports — useful for debugging scenarios where you need to distinguish protocol entry points.
There are also redir-port and tproxy-port, aimed at Linux transparent proxying (paired with iptables/nftables forwarding); everyday desktop use doesn't need these. All ports share one constraint: they can't conflict with another process on the machine. A startup error reading bind: address already in use means the port is taken — for the full diagnosis and fix, see "What to Do When Clash Reports Port In Use".
allow-lan and bind-address
allow-lan controls whether the client accepts inbound connections from other devices on the local network; it defaults to false (listening only on 127.0.0.1). Set it to true and phones or streaming boxes on the same Wi-Fi can point their proxy settings at this machine's LAN IP and port, effectively sharing one connection across the household. The companion bind-address field restricts which network interface is used for listening: the default "*" means all interfaces, and on multi-NIC setups you can supply a specific interface address to narrow the entry point. Turning on allow-lan means any device on the local network can route traffic through your exit node — keep it off on public networks like offices or hotels.
mode: Three Routing Modes
mode accepts three values. rule is rule mode, where every connection is checked against the rules list one by one — the everyday standard; global is global mode, skipping rules and sending all traffic to a single global exit (typically shown in the UI as picking one proxy directly); direct is direct mode, where all traffic bypasses the proxy entirely — effectively pausing routing while keeping the kernel running. When trying to figure out "which rule is this site actually hitting," toggling between global and rule mode is a quick way to tell whether the problem lies in the rules or in the proxy itself.
Logging and the External Controller
log-level controls how verbose kernel logs are, from quietest to loudest: silent, error, warning, info, debug. Info is fine for daily use; switch to debug when troubleshooting to see exactly which rule and DNS resolution path each connection hits, then switch back once things are stable to avoid bloating log files. external-controller declares the listen address for the RESTful control API (conventionally 127.0.0.1:9090), which GUI clients and web dashboards use to read state and switch proxies; the companion secret field sets an access token for that API — required whenever the controller listens on 0.0.0.0 or is exposed to the LAN, e.g. secret: "your-secret".
Other Common Switches
ipv6 defaults to false; enabling it lets the kernel resolve and connect via AAAA records, which can actually cause timeouts on some sites if IPv6 isn't properly supported locally or on the proxy side — confirm your path supports it before turning this on. unified-delay (a mihomo extension) subtracts handshake overhead from latency tests, making latency numbers more comparable across different protocols. Under the profile block, store-selected: true remembers each group's last-selected proxy across restarts or subscription updates, so you don't have to re-pick — nearly every client template enables this by default.
DNS Fields
Why Built-In DNS Matters
Routing depends on knowing where a connection is headed, and domain resolution is exactly the step most prone to tampering or leakage: if resolution is handed off to an untrusted upstream, the returned IP might be wrong; if every lookup goes through your local ISP, your browsing history is fully exposed. The dns module lets the kernel take over resolution, working with the rule system to decide which domains use which DNS servers and how results get handed back to applications. Under TUN mode or transparent proxying, the dns module is required; with system proxy mode alone it's optional, but enabling it noticeably improves routing accuracy.
dns:
enable: true
listen: 0.0.0.0:1053
enhanced-mode: fake-ip
fake-ip-range: 198.18.0.1/16
fake-ip-filter:
- "*.lan"
- "+.local"
- "time.*.com"
default-nameserver:
- 223.5.5.5
- 119.29.29.29
nameserver:
- https://223.5.5.5/dns-query
- https://doh.pub/dns-query
fallback:
- https://1.1.1.1/dns-query
fallback-filter:
geoip: true
geoip-code: CN
enhanced-mode: fake-ip vs. redir-host
This is the highest-impact field in the dns block. In fake-ip mode, the kernel returns a fake address from a reserved range (defined by fake-ip-range, defaulting to 198.18.0.0/16) for each domain; the application connects using that fake IP while the kernel maintains the mapping between fake IP and real domain. The upside: applications skip real resolution entirely, rules can match directly on domain names, connections establish quickly, and there's no resolution leakage; the downside is that a handful of programs relying on real IPs — LAN discovery, some game matchmaking, NTP sync — will misbehave, and those domains need to go into the fake-ip-filter allowlist, which falls back to real resolution for matches. redir-host mode always returns real IPs, giving the best compatibility, but domain information gets lost on some paths, so both rule hit rate and resolution speed lag behind fake-ip. Desktop client templates generally default to fake-ip and add filter entries only when a specific app misbehaves.
| Comparison | fake-ip | redir-host |
|---|---|---|
| IP seen by the app | Fake address from reserved range | Real resolved result |
| Connection setup speed | Fast (skips waiting for resolution) | Affected by upstream resolution time |
| Domain rule hit rate | Stable | Some cases degrade to IP-based matching |
| Compatibility risk | Programs needing real IPs must be allowlisted | Essentially none |
Three Upstream Groups: default-nameserver, nameserver, and fallback
These three lists have distinct jobs. default-nameserver does exactly one thing: resolve the domain names of the DoH/DoT servers listed in the other two groups, so it must be plain IPs — otherwise you get a deadlock where "the resolver's own domain has nothing to resolve it." nameserver is the primary upstream, handling everyday resolution; it's best set to encrypted-protocol addresses (DoH starting with https:// or DoT starting with tls://), avoiding plaintext port 53, which can be tampered with in transit. fallback is the backup upstream, working alongside fallback-filter: when nameserver's result matches the filter condition (typically geoip: true with geoip-code: CN, meaning "the result isn't a mainland China IP"), the fallback result is used instead — this counters DNS tampering, giving overseas domains a clean resolution. If you don't need this mechanism, keeping just nameserver is simpler.
After changing the dns block, flush your system DNS cache before testing (ipconfig /flushdns on Windows, sudo killall -HUP mDNSResponder on macOS) — otherwise a stale cache can make the new config look like it "didn't take effect."
Proxy Fields
Four Fields Shared by Every Protocol
Each element in the proxies array defines one proxy. Regardless of protocol, four fields are required: name (must be unique across the whole file — proxy groups and rules reference proxies by name, and duplicates cause a load failure), type (protocol type), server (server hostname or IP), and port (server port). The optional udp field declares whether the proxy forwards UDP traffic — worth enabling for gaming and real-time calls, provided the server supports it. mihomo supports a broad set of protocols: ss, vmess, trojan, vless, hysteria2, tuic, wireguard, socks5, http, and more can all be used as the type value. Below are the three most common, expanded.
Shadowsocks (type: ss)
proxies:
- name: "HK-01"
type: ss
server: hk01.example.com
port: 8388
cipher: aes-256-gcm
password: "your-password"
udp: true
The core is the encryption parameters: cipher and password must exactly match the server. Common ciphers include aes-256-gcm, chacha20-ietf-poly1305, and the newer 2022-blake3-aes-256-gcm (which requires a Base64-encoded fixed-length key as the password, not an arbitrary string). Mismatched encryption parameters typically show up as an immediate disconnect with a decryption error in the logs. If the server has a plugin enabled (such as obfs), you'll also need the plugin and plugin-opts fields.
VMess (type: vmess)
- name: "JP-01"
type: vmess
server: jp01.example.com
port: 443
uuid: 23ad6b10-8d1a-40f7-8ad0-e3e35cd38297
alterId: 0
cipher: auto
tls: true
network: ws
ws-opts:
path: /ray
headers:
Host: jp01.example.com
The credential here is uuid, issued by the server and copied verbatim. alterId is fixed at 0 for modern deployments (enabling AEAD). network determines the transport layer: tcp connects directly, ws uses WebSocket (often paired with a CDN in front), and grpc uses gRPC; choosing ws means supplying path and Host header in ws-opts — any mismatch with the server on these three values causes the handshake to fail. When tls: true, servername can set the SNI independently (useful when server is an IP but the certificate is issued for a domain).
Trojan (type: trojan)
- name: "SG-01"
type: trojan
server: sg01.example.com
port: 443
password: "your-password"
sni: sg01.example.com
skip-cert-verify: false
udp: true
Trojan runs natively over TLS, disguising itself as ordinary HTTPS traffic. sni should match the server certificate's domain; skip-cert-verify controls whether certificate validation is skipped, and leaving it at false is a hard baseline — setting it to true means giving up on verifying the server's identity, letting any man-in-the-middle impersonate the proxy. Only a self-signed test environment is ever a legitimate reason to flip this temporarily. For the causes and diagnosis of certificate errors behind a proxy, see "Common Causes of HTTPS Certificate Errors Behind a Proxy".
Some subscriptions bulk-set skip-cert-verify: true on every proxy to "reduce errors." Once you understand this section, consider flipping it back to false in your override layer; if a proxy genuinely fails to connect because of this, question that proxy's certificate setup rather than disabling verification.
Proxy Group Fields
What a Proxy Group Is
proxy-groups adds a layer of abstraction between proxies and rules: rules don't point directly at a specific proxy but at a group, and the group decides the actual exit based on its policy (manual selection, auto latency testing, failover, and so on). The benefit is obvious — switching proxies only means switching within the group, leaving hundreds of rules untouched; when a subscription update renames proxies, only the group's member list is affected. A group's proxies members can be a proxy name, the built-in DIRECT (direct connection) or REJECT (block) policies, or the name of another group, letting you build a hierarchy like "region groups → main exit group"; the only restriction is no circular references.
proxy-groups:
- name: "PROXY"
type: select
proxies:
- AUTO
- HK-01
- JP-01
- DIRECT
- name: "AUTO"
type: url-test
url: https://www.gstatic.com/generate_204
interval: 300
tolerance: 50
lazy: true
proxies:
- HK-01
- JP-01
- SG-01
Five Group Types, Explained
| type | Behavior | Typical use |
|---|---|---|
select | Manual selection, holds the choice until the user changes it | Main exit; scenarios needing manual control |
url-test | Periodic latency test, auto-picks the lowest-latency member | Auto-optimizing among multiple proxies in the same region |
fallback | Takes the first available member in list order | Primary/backup failover, switches back once primary recovers |
load-balance | Spreads connections across multiple members | Avoiding a single proxy's rate limit, boosting concurrent throughput |
relay | Chains members in sequence to form multi-hop forwarding | Multi-hop exits (each hop adds latency) |
Auto-type groups (url-test/fallback/load-balance) rely on three test parameters: url is the probe address, typically a lightweight endpoint that returns an empty 204 response; interval is the probe period in seconds — 300 is a common balance, and setting it too low generates constant test traffic; tolerance is used only by url-test, meaning "only switch if the latency gap between the current best and a new candidate exceeds this many milliseconds," which suppresses flapping between two proxies with similar latency. lazy: true (a mihomo extension) pauses probing when a group isn't in use, noticeably cutting background requests when there are many proxies. load-balance also has a strategy field: consistent-hashing tries to keep the same destination site routed through the same proxy (friendly to logged-in sessions), while round-robin strictly alternates.
Organizing Your Groups
In practice, a three-layer structure works well: a single top-level select group as the main exit (which most rules point to); a middle layer of one url-test auto group per region; and the proxies themselves at the bottom. For services with region requirements — streaming, AI services — set up a dedicated select group whose members reference the region groups. This way, day-to-day operation needs zero intervention (auto latency testing takes over), specific services can be manually pinned to a region, and the impact of subscription updates stays contained within the region groups.
↑ Back to topRule Syntax
Basic Format and Matching Order
Each element in the rules array is a single string in the form TYPE,VALUE,POLICY, comma-separated; the policy segment can be a group name, a proxy name, or the built-in DIRECT/REJECT. For every new connection, the kernel checks rules top-to-bottom and stops at the first match — this single fact governs everything: specific rules (exact domains) must come before broad rules (suffixes, keywords, GEOIP), or they'll never get a chance to match; the list ends with a MATCH catch-all that absorbs anything unmatched. The number of rules barely affects performance (domain rules use a prefix-tree index), but ordering mistakes cause routing errors that are hard to spot — verify hit behavior against the log after each change.
rules:
- DOMAIN,api.example.com,DIRECT
- DOMAIN-SUFFIX,example.com,PROXY
- DOMAIN-KEYWORD,tracker,REJECT
- IP-CIDR,192.168.0.0/16,DIRECT,no-resolve
- GEOSITE,category-ads-all,REJECT
- GEOIP,CN,DIRECT
- MATCH,PROXY
Rule Type Reference
| Type | Matches on | Example |
|---|---|---|
DOMAIN | Exact full domain match | DOMAIN,api.example.com,DIRECT |
DOMAIN-SUFFIX | A domain and all its subdomains | DOMAIN-SUFFIX,example.com,PROXY |
DOMAIN-KEYWORD | Domain containing a given substring | DOMAIN-KEYWORD,google,PROXY |
IP-CIDR / IP-CIDR6 | Destination IP falls within a range | IP-CIDR,10.0.0.0/8,DIRECT,no-resolve |
GEOIP | Geographic origin of destination IP | GEOIP,CN,DIRECT |
GEOSITE | Community-maintained domain sets | GEOSITE,github,PROXY |
PROCESS-NAME | Name of the process making the connection (desktop only) | PROCESS-NAME,steam.exe,DIRECT |
RULE-SET | References a rule-providers set | RULE-SET,streaming,PROXY |
MATCH | Unconditional catch-all | MATCH,PROXY |
no-resolve and the Interaction Between Domain and IP Rules
IP-based rules (IP-CIDR/GEOIP) have an easily overlooked side effect: when the connection target is still a domain, matching against an IP requires resolving it first — that resolution can leak through local DNS and also slows matching down. Appending a fourth field, no-resolve, to an IP rule means "only match if the target is already an IP; skip this rule if it's a domain" — LAN range rules should always carry this. The overall ordering rule of thumb: domain rules first, IP rules next, MATCH last; ad-blocking (REJECT) entries go at the very front of their category. For the full topic of writing rules — including whether custom rules should be inserted before or after subscription rules — see "How to Write Custom Clash Rules".
rule-providers: Externalizing Rules
rule-providers:
streaming:
type: http
behavior: classical
format: yaml
url: https://example.com/rules/streaming.yaml
path: ./rules/streaming.yaml
interval: 86400
rules:
- RULE-SET,streaming,PROXY
Cramming thousands of rules into the main file is hard to maintain and hard to share, so rule-providers lets you load rule sets from an external URL or local file, refreshed on an interval of seconds. behavior declares the set's content shape: domain (plain domain list), ipcidr (plain range list), or classical (full three-field entries like rules). A mismatch between behavior and the file's actual content causes the set to silently fail — the number one reason an externalized rule set "looks loaded but doesn't work."
Override & Merge
Why You Shouldn't Edit a Subscription File Directly
Subscription users face a structural conflict: the config file is generated by the provider, and every time the subscription updates, the entire file gets re-downloaded and overwritten — any rule you hand-added or port you changed only survives until the next update. The correct approach is to keep "the provider's base config" and "your own changes" stored separately, merged into a final config by the client at load time. Different clients call this mechanism by different names — Override, Merge, extended config — the principle is the same.
Basic Merge Semantics
The merge/override config is itself a YAML file that only contains what you want to change. Semantics fall into two categories: scalar and mapping fields get replaced outright — writing mixed-port: 7891 in the override makes 7891 the final port, and writing a full dns: block replaces the entire base dns block; array fields support prepending and appending — most clients use a prepend- prefix to insert at the start of a base array and an append- prefix to add at the end, which matters a great deal for rules: prepended rules take priority over subscription rules, while appended rules can only act as a fallback. A typical override:
mixed-port: 7891
log-level: warning
prepend-rules:
- DOMAIN-SUFFIX,internal.example.com,DIRECT
- PROCESS-NAME,steam.exe,DIRECT
append-proxies:
- name: "SELF-01"
type: ss
server: my.example.com
port: 8388
cipher: aes-256-gcm
password: "your-password"
The exact key names and supported scope for prepend/append vary slightly by client implementation: Clash Verge Rev offers a "global extended config" plus a two-tier Merge/Script override per subscription, while Clash Plus provides an override editor within subscription details. Follow your specific client's documentation for exact syntax — what's given here is the general semantics.
Script Overrides and proxy-providers
When declarative merging isn't enough — say, batch-filtering proxies by name, or inserting a member into every group at once — some clients (like Verge Rev) support JavaScript script overrides: the script receives the parsed config object and returns a modified one, offering maximum flexibility at the cost of the whole config failing to load if the script errors, so change it in small steps and verify each time. Another approach flips the model: instead of using the provider's entire config, reference only the subscription's proxy list via proxy-providers, while building proxy groups and rules entirely yourself. This way subscription updates only affect the proxy pool, and the main config stays yours — a good fit once you've worked through this manual and are willing to maintain your own rule set:
proxy-providers:
airport:
type: http
url: https://example.com/subscribe?token=xxxx
path: ./providers/airport.yaml
interval: 43200
health-check:
enable: true
url: https://www.gstatic.com/generate_204
interval: 600
proxy-groups:
- name: "PROXY"
type: select
use:
- airport
Note that a group references a provider via the use field (not proxies) — the two can be combined; health-check lets proxies inside a provider also be probed for availability.
↑ Back to topValidation & Troubleshooting
Before Loading: Static Validation
Don't just restart and hope after editing a config. If you have the mihomo kernel binary available, one command runs a pure syntax and semantics check without starting any listener:
mihomo -d . -t -f config.yaml
-t is test mode, -d specifies the working directory (where resources like GeoIP data are found), and -f specifies the file to check. Output reading configuration file ... test is successful means it passed; error messages give a line number and cause, with common ones being: indentation errors (yaml: line N), duplicate proxy names (proxy N: name duplicated), a group referencing a nonexistent member (proxy group ... proxy not found), and typos in a rule's policy field. GUI clients run the same validation on import, and the error dialog's message matches the command-line output — just trace back to that line number in the file.
After Loading: Verifying Routing Behaves as Expected
A config loading successfully doesn't mean it behaves correctly. A three-step verification approach: first, check the logs — temporarily set log-level to debug, visit the target site, and the log will print which rule this connection hit and its final exit, the only authoritative way to know "which rule actually matched"; second, check the connections panel — a GUI client's connections page lists active connections in real time along with their target, matched rule, and exit group, good for observing overall traffic distribution; third, do an external check — visit an IP-detection service both directly and through the proxy, confirming the exit address actually changes with policy. If routing looks correct but a specific app isn't going through the proxy, suspect first that the app is bypassing the system proxy setting — the Windows Store app loopback restriction is a classic example; see "Why Windows Store Apps Don't Use the Proxy" for the fix.
Common Issues, Quick Reference
| Symptom | Likely cause | Relevant section |
|---|---|---|
| Startup fails with bind: address already in use | Port is occupied by another process | Section 3 · Port family |
| Load fails with yaml: line N | Indentation/tabs/missing space after a colon | Section 2 · Formatting rules |
| All proxies time out but the subscription updates fine | Proxy credentials or transport parameters don't match the server | Section 5 · Proxy fields |
| Domain rules aren't taking effect | Domain lost under redir-host, or a broad rule matched first | Sections 4/7 |
| A change disappears after the subscription updates | You edited the subscription file directly | Section 8 · Override & merge |
| LAN devices can't connect to the shared proxy | allow-lan is off, or a firewall is blocking inbound connections | Section 3 · allow-lan |
More symptom-organized Q&As are on the FAQ page; if you suspect the client itself rather than the config is at fault, try a different client from the downloads page for cross-checking — Clash Plus is our top pick across all platforms, and comparing the same config's behavior across clients often quickly narrows down where the problem sits.
↑ Back to top