Before You Start: Scope of This Manual and Config Basics
First, a note on how this page relates to the tutorial. Subscription Setup Basics is the main walkthrough: import a subscription, pick a mode, connect, and verify it works — follow it once and your client is up and running. This page does not repeat that path. Instead, it breaks advanced topics into nine chapters so you can jump straight to whatever you need to adjust; it is meant to live in your bookmarks as a reference. If you have not completed your first connection yet, walk through the tutorial first, or read the blog post Clash First Connection Guide: Picking a Node, Testing Latency, and Verifying the Proxy Works, then come back here to look up topics as needed.
The configuration target of this page is the mihomo kernel (formerly Clash Meta). Every actively maintained client listed on the client download page — Clash Plus, Clash Verge Rev, FlClash, Clash Meta for Android, and others — is built on this kernel, so the configuration syntax is identical. All YAML snippets on this page are mihomo configurations: graphical clients on desktop and mobile usually provide a config editor or override entry point, while users running the bare kernel edit config.yaml directly in the config directory.
Before changing anything, locate the config that is actually in effect. In Clash Verge Rev, right-click a profile on the Profiles page to edit the file or write a global extension; in Clash Plus, open the active subscription in profile management; in FlClash, edit the override section in the profile details; the bare kernel reads config.yaml from its config directory by default. When multiple subscriptions coexist, editing the wrong file is the number one reason changes fail to take effect.
All examples in this manual follow four YAML rules: indent with spaces only — never tabs — and use two spaces consistently; put exactly one space after the colon following a key; wrap the whole value in quotes when it contains a colon, #, or wildcards; keep keys at the same level strictly aligned. Chapter 9 includes a table of common syntax errors.
# Minimal config.yaml skeleton (mihomo)
mixed-port: 7890 # Mixed HTTP and SOCKS port
allow-lan: false # Whether to allow LAN devices to connect
mode: rule # rule / global / direct
log-level: info
dns: {} # Covered in Chapter 4
proxies: [] # Nodes, usually from subscriptions or proxy-providers
proxy-groups: [] # Covered in Chapter 2
rules: [] # Covered in Chapter 3
Proxy Group Types in Practice
Proxy groups are the hub of traffic routing. Each rule in rules ends not with a specific node but with a proxy group name; once traffic matches a rule, it is handed to the group, which picks the actual outbound according to its own type logic. Separating "node selection" from the rules is the core idea of mihomo configuration: rules only classify, groups only select. Besides real nodes, a group can also contain two built-in policies: DIRECT for direct connections and REJECT for outright refusal (commonly used for ad and tracking domains).
The Five Types, One by One
select: manual selection. The first entry in the proxies list is the default outbound; you switch it manually in the dashboard later. This is the most common type and usually serves as the top-level outbound group.
url-test: automatic latency-based selection. It sends HTTP requests to the url through each node in the group, runs a round every interval seconds, and picks the node with the lowest latency; tolerance sets a millisecond margin — if the gap between the current node and the new best is within it, no switch happens, which avoids flapping; when lazy is true, the group skips testing while it has no traffic, saving both requests and battery.
fallback: failover. It takes the first node in the proxies order that passes the health check. Ideal for primary/backup setups — when the primary node fails, traffic automatically lands on the backup, and switches back once the primary recovers.
load-balance: load balancing. With strategy set to consistent-hashing, the same destination domain is always hashed to the same node, so site sessions do not break from outbound IP changes; with round-robin, requests rotate one by one, which suits multi-threaded download workloads.
relay: chained proxying. The proxies are strung together in order — traffic passes through the entry node first, then on to the landing node. It enables special "in through one node, out through another" paths, but every extra hop adds latency and another point of failure, so everyday routing rarely needs it.
Nesting Groups and the use Reference
A proxies list can contain node names as well as other group names — put "Auto Select" and the regional groups inside "Node Select" and users switch everything in one place. A group can also use the use field to reference an entire subscription node set pulled in by proxy-providers (see Chapter 7); when the subscription updates, the group's membership follows automatically, with no manual edits to the group list.
A Practical Combination
proxy-groups:
- name: "Node Select"
type: select
proxies: ["Auto Select", "Hong Kong", "Japan", "United States", "DIRECT"]
- name: "Auto Select"
type: url-test
use: ["airport-a"] # References the subscription set from Chapter 7
url: "http://www.gstatic.com/generate_204"
interval: 300
tolerance: 80
lazy: true
- name: "Streaming"
type: select
proxies: ["Hong Kong", "Japan", "Node Select"]
- name: "Download"
type: load-balance
use: ["airport-a"]
strategy: consistent-hashing
url: "http://www.gstatic.com/generate_204"
interval: 600
| Type | Selection Logic | Typical Use | Key Parameters |
|---|---|---|---|
| select | Picked manually in the dashboard | Top-level outbound; services that need a fixed node | — |
| url-test | Periodic latency tests, lowest wins | Automatic outbound for everyday browsing | url / interval / tolerance / lazy |
| fallback | First healthy node in order | Primary/backup failover | url / interval |
| load-balance | Spreads requests by strategy | Multi-threaded downloads, heavy traffic tasks | strategy / interval |
| relay | Nodes chained in order | Special chained paths | — |
Keep Test Intervals at 300 Seconds or Above
Every test round runs a proxy handshake through every node in the group; testing every few dozen seconds is a visible battery drain on mobile. For the full mobile battery troubleshooting sequence, see the blog post Troubleshooting Abnormal Clash Battery Drain on Mobile and Optimizing Background Behavior.
Managing Rule Sets as Subscriptions
Rules decide "which traffic goes out through which exit". Hardcoding dozens or hundreds of rules into config.yaml creates two problems: rules go stale because domain lists change daily, and a subscription update overwrites your hand edits in full. Rule providers (rule-providers) split rules into standalone remote files that the kernel re-fetches on an interval schedule, leaving only a single reference line in the main config.
Field-by-Field Reference
type: http fetches from a URL, local reads a local file. behavior: the shape of the rule set's contents — domain is a plain list of domain suffixes, ipcidr is a plain list of IP ranges, and classical is classic rules (each line in the file is a complete typed rule such as DOMAIN-SUFFIX,example.com). format: the file format — yaml, text, and mrs are supported (mrs is a binary format with the smallest footprint; prefer it for very large lists). url / path: the remote address and the local cache path; if a fetch fails, the cache at path serves as fallback. interval: the update interval in seconds.
How to Reference Them, and Why Order Matters
In rules, reference a set with RULE-SET,set-name,policy-group. Matching runs top to bottom and stops at the first hit, so order is priority: ad-blocking reject sets go first, LAN and mainland-China direct rules sit in the middle, and the FINAL catch-all goes last. Misplace a RULE-SET and every rule below it will never be reached. Note that GEOSITE rules (such as GEOSITE,cn) come from the kernel's built-in geosite database — a different source from RULE-SET's external files — and the two can be mixed freely.
rule-providers:
reject:
type: http
behavior: domain
format: yaml
url: "https://example.com/rules/reject.yaml"
path: ./ruleset/reject.yaml
interval: 86400
lan:
type: http
behavior: classical
format: text
url: "https://example.com/rules/lan.txt"
path: ./ruleset/lan.txt
interval: 86400
rules:
- RULE-SET,reject,REJECT
- RULE-SET,lan,DIRECT
- GEOIP,CN,DIRECT
- MATCH,Node Select
| behavior | File Contents | Entry Format | Typical Use |
|---|---|---|---|
| domain | Domain suffixes | example.com (one per line) | Ad domains, service domain lists |
| ipcidr | IP ranges | 1.0.0.0/24 | ISP or organization IP ranges |
| classical | Classic rules | DOMAIN-SUFFIX,example.com | Mixed-type rule collections |
Update Frequency and Failure Fallback
An interval of 86400 (one fetch per day) is recommended; anything more frequent adds little. Rule-set update failures share the same root causes as subscription update failures — network conditions, dead links, client settings — each covered in the blog post Common Causes of Clash Subscription Update Failures and How to Enable Auto-Updates.
DNS Configuration Optimization
DNS is the dividing line between good and bad routing. Three typical problems: the ISP's DNS returns poisoned results; the system or browser bypasses the proxy and asks the ISP directly, causing DNS leaks; and domains outside China get resolved through DNS servers inside China, returning suboptimal CDN nodes. The dns section in mihomo exists to take over all three.
Recommended Structure
Turn on the kernel DNS (enable plus listen) and set enhanced-mode to fake-ip (explained in Chapter 5); fill nameserver with public DNS servers inside mainland China as the baseline; use nameserver-policy with geosite to pin Chinese domains to Chinese DNS; put DoH servers in fallback for domains outside China; and filter with fallback-filter via geoip — fallback answers are only trusted when the result lies outside the CN ranges, so poisoned results never get in.
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"
- "*.msftconnecttest.com"
- "localhost.ptlogin2.qq.com"
nameserver:
- 223.5.5.5
- 119.29.29.29
fallback:
- "https://1.1.1.1/dns-query"
- "https://dns.google/dns-query"
fallback-filter:
geoip: true
geoip-code: CN
nameserver-policy:
"geosite:cn":
- 223.5.5.5
- 119.29.29.29
What Is a DNS Leak?
A leak means an application skips the kernel and queries the ISP's DNS directly, leaving your browsing history fully visible to the ISP. The fix is to have the kernel handle all DNS queries: in TUN mode, dns-hijack intercepts port 53 for the whole system (Chapter 5); without TUN, point the system DNS to 127.0.0.1 and make sure listen is bound to port 53. For leak symptoms and how to verify, see the DNS entries on the troubleshooting Q&A page.
| Name | Address | Type | Notes |
|---|---|---|---|
| Alibaba DNS | 223.5.5.5 | UDP | Public DNS in mainland China, low latency |
| Tencent DNS | 119.29.29.29 | UDP | Public DNS in mainland China |
| Cloudflare | https://1.1.1.1/dns-query | DoH | Outside China, encrypted transport |
| https://dns.google/dns-query | DoH | Outside China, encrypted transport |
Don't Put Only Overseas DoH in nameserver
If every Chinese domain is resolved overseas, CDNs will route you to nodes outside China and video and downloads will slow down noticeably. The correct split: Chinese domains stay on UDP DNS inside China, domains outside China go through DoH — each handles its own share.
TUN Mode and Fake-IP
The system proxy only governs "well-behaved" apps — browsers and software that respect the system proxy settings. Games, some command-line tools, and UWP apps ignore the system proxy, and UDP and ICMP traffic is out of reach entirely. TUN mode creates a virtual network adapter that takes over all traffic on the machine — the broadest form of takeover available. The system proxy is enough for everyday browsing; enable TUN when you run into apps that refuse to follow proxy settings.
Key Fields
stack: the protocol stack implementation — differences in the table below. dns-hijack: DNS hijacking; any:53 intercepts all port-53 queries on the machine and hands them to the kernel DNS, working together with the Chapter 4 setup. auto-route: automatically configures the routing table to steer traffic into the virtual adapter. auto-detect-interface: automatically identifies the physical outbound interface to prevent traffic loops. mtu: the default is fine; a few campus networks or leased-line environments need it lowered.
tun:
enable: true
stack: mixed
dns-hijack:
- any:53
auto-route: true
auto-detect-interface: true
| stack | Implementation | Characteristics |
|---|---|---|
| system | System network stack | Broadest compatibility, good TCP performance |
| gvisor | Userspace network stack | Stable UDP behavior, slightly higher resource usage |
| mixed | TCP via system, UDP via gVisor | A middle ground; the safe choice for most environments |
Fake-IP vs. redir-host
In redir-host mode, the application resolves the domain for real, gets an IP, and then connects; the kernel reverse-maps the IP back to a domain to match rules — an extra real lookup to wait for, and the IP it gets may be poisoned. In fake-ip mode, the DNS stage immediately returns a fake address from the 198.18.0.1/16 range; the app connects to that fake address, and the kernel maps it back to the domain and matches rules — no real lookup needed, rule matching gets the domain directly, routing is more accurate, and first connections are faster.
The trade-off: some services must not receive fake addresses — LAN domains, NTP time sync, and certain direct-connect services in mainland China. List them in fake-ip-filter and the kernel returns real resolutions for those domains. The Chapter 4 example includes a baseline list; add to it as needed.
Platform Notes
On Windows, TUN requires installing the service mode or running as administrator. UWP apps have an additional loopback restriction — beyond TUN, you must also grant a loopback exemption; see the blog post Windows UWP Apps Can't Use the Proxy: How to Lift the Loopback Restriction. TUN conflicts with other VPN and game-booster software — running both at once means fighting over the routing table.
Domain Sniffing
Ideally, the kernel learns the target domain straight from the connection. But many apps do their own DNS — built-in DoH, hardcoded IPs — so by the time traffic reaches the kernel, only an IP address remains. Every domain rule misses, IP rules become the only safety net, and routing precision drops noticeably. Domain sniffing (sniffer) exists to recover that lost domain.
How It Works
The kernel inspects the first handshake packet of each connection: the TLS ClientHello carries an SNI field, and HTTP requests carry a Host header — both state the target domain. Sniffing extracts that domain, replaces the connection target with it, and re-runs rule matching — traffic that could only match IP rules now matches domain rules, and far fewer connections show up in the dashboard as bare IPs.
Configuration Example
sniffer:
enable: true
sniff:
TLS:
ports: [443, 8443]
HTTP:
ports: [80, 8080]
override-destination: true
skip-domain:
- "Mijia Cloud"
- "+.push.apple.com"
override-destination replaces the connection target with the sniffed domain; enable it for fake-ip and TUN setups. Pure redir-host configs whose rules are mostly IP ranges can leave it off. skip-domain lists services that break under sniffing: Apple Push and Mijia use strict certificate pinning, and sniffing rewrites cause their handshakes to fail — skipping them outright is the simplest fix.
Recommended to Keep On
Sniffing only reads the first packet of each connection, so the overhead is negligible. Rule hit rates go up once it is on, and the improvement is most visible when combined with fake-ip.
Local Overrides and Multi-Subscription Merging
Subscriptions are delivered as whole files: when the provider updates its config and the client re-fetches, every hand-edited group and rule is overwritten. The right approach is to keep subscriptions and local changes separate, merging them at load time instead of writing both into the same file.
Clash Verge Rev: Merge Overrides
Open the global extend config from the Profiles page and choose the Merge method, then describe your changes with fixed keys: prepend-rules and append-rules insert rules at the head or tail; prepend-proxies and append-proxies add nodes; prepend-proxy-groups and append-proxy-groups add groups. Processing happens after the subscription loads, so no matter how the subscription updates, your overrides are reapplied on top. Users comfortable with JavaScript can choose the Script method and write a handler function for more freedom, but Merge keys cover most needs.
# Clash Verge Rev global extend config (Merge)
prepend-rules:
- "DOMAIN-SUFFIX,internal.example.com,DIRECT"
append-proxy-groups:
- name: "Download"
type: select
proxies: ["Node Select", "DIRECT"]
Entry Points in Other Clients
Clash Plus applies overrides to the active subscription in profile management; FlClash edits the override section in profile details. Bare-kernel users have no override layer — keep the main config in your own hands and let subscriptions supply nodes only through proxy-providers.
Merging Multiple Subscriptions: proxy-providers
With two or more providers on hand, there is no need to copy nodes into the main config. proxy-providers pulls each subscription into its own node set, each updating on its own schedule with its own health checks; a proxy group references several sets at once via use, their union joins the group, and you switch in one place.
proxy-providers:
airport-a:
type: http
url: "https://example.com/sub-a.yaml"
path: ./providers/airport-a.yaml
interval: 86400
health-check:
enable: true
url: "http://www.gstatic.com/generate_204"
interval: 300
airport-b:
type: http
url: "https://example.com/sub-b.yaml"
path: ./providers/airport-b.yaml
interval: 86400
health-check:
enable: true
url: "http://www.gstatic.com/generate_204"
interval: 300
proxy-groups:
- name: "Auto Select"
type: url-test
use: ["airport-a", "airport-b"]
url: "http://www.gstatic.com/generate_204"
interval: 300
How to Confirm Overrides Took Effect
Once overrides and merging are in place, subscriptions can update freely without losing your local changes. When troubleshooting "my edit did nothing", first check the client's runtime config (the dashboard usually offers a viewer), confirm the overrides were actually applied, and only then go back to checking syntax.
External Dashboards
mihomo ships with a built-in RESTful API, and every desktop client's interface is essentially a frontend for it. Expose the API and you can attach a dashboard in the browser — when running the bare kernel on a server or router with no GUI, this is the standard way to manage it.
Enabling the API
external-controller: 127.0.0.1:9090
secret: "your-password"
external-ui: ui
external-controller is the listen address; secret is the access key; external-ui points to the dashboard's static file directory — once set, visiting http://127.0.0.1:9090/ui opens the dashboard directly. If you would rather not host dashboard files yourself, use an online one — metacubexd, zashboard, and yacd-meta are all actively maintained; open the page, enter the API address and secret, and you are in.
Common Endpoints
# List all proxy groups and nodes
curl -H "Authorization: Bearer your-password" http://127.0.0.1:9090/proxies
# Switch "Node Select" to a specific node
curl -X PUT -H "Authorization: Bearer your-password" \
-d '{"name":"Hong Kong 01"}' \
http://127.0.0.1:9090/proxies/Node%20Select
Other frequently used endpoints include /connections (the live connection list, closable one by one), /traffic (real-time rates), and /logs (the log stream). Scripts that switch nodes in bulk or run health inspections all build on these endpoints.
Never Expose the API Bare to the Network
Do not bind external-controller to 0.0.0.0 and expose it to the LAN or the public internet without a secret — anyone who scans the port can change your proxy outbound. If you must expose it, use a strong random secret and only allow trusted network ranges.
Config Validation and Troubleshooting
Validate the config before restarting. Graphical clients report the exact line number of YAML errors in their log page; for the bare kernel, run a full load test from the command line:
mihomo -t -d /etc/mihomo
-t means test only, do not run; -d specifies the config directory. Syntax errors, references to nonexistent proxy groups, and missing rule-set paths all surface at this step — nothing goes live broken.
Frequent YAML Errors at a Glance
| Symptom | Cause | Fix |
|---|---|---|
| Error: mapping values are not allowed | Value contains a colon without quotes | Wrap the whole value in double quotes |
| Error: did not find expected key | Tabs mixed into indentation, or misaligned levels | Use space indentation; align same-level keys |
| Loads fine but rules don't apply | Misspelled key; the kernel silently ignores unknown keys | Check each key against this manual's field names |
| New group missing from the dashboard | You edited a config that is not in effect | Confirm the active subscription and override entry |
Troubleshooting Order
Work through "my edit did nothing" in four steps: one, confirm you edited the config currently in effect — the most common pitfall when multiple subscriptions coexist; two, after a subscription update, confirm your overrides are still in place; three, inspect the runtime config to confirm the merged result matches expectations; four, clear the rule-set and node caches and restart the core. When a rule fails to match, check the actual rule chain the connection hit in the dashboard or logs — it is usually an ordering problem. More specific error Q&A lives on the troubleshooting Q&A page, and Windows installation pitfalls are covered in the blog post Clash on Windows: Full Installation and Setup Walkthrough with Common Pitfalls.
If the problem lies in the client or the kernel itself, first confirm you are on the latest version. The client download page collects clients and kernel packages for five platforms, with Clash Plus as the top pick across all of them; for subscription-level update failures, see the blog post Common Causes of Clash Subscription Update Failures and How to Enable Auto-Updates.