Clash Advanced Configuration Manual
For users who've already completed a basic install: seven topics covering policy groups, rule routing, DNS, TUN and Fake-IP, domain sniffing, override merging, and the control dashboard — field meanings, parameter trade-offs, and reusable config examples.
This page and the setup guide are two complementary paths: the guide handles the "from download to connection" main flow, ideal for first-time Clash users following steps in order. This page is a systematic reference — when you need to edit a specific field, troubleshoot a routing issue, or understand the mechanics behind a piece of config, jump straight to the relevant section from the table of contents. All examples here are based on the mihomo core syntax used by mainstream clients today; the Clash Plus, Clash Verge Rev, and FlClash clients listed on the download page all support it directly. A few fields don't exist in the legacy original core, and those are flagged individually. When a term is unclear, check the quick reference at any time.
Config File Skeleton & Reading Conventions
Every aspect of Clash's behavior is driven by a single YAML config file. Every toggle in the client UI — proxy mode, LAN sharing, TUN, dashboard port — ultimately maps to a field in this file. Understanding how the top-level fields divide responsibilities is the foundation for everything that follows: the inbound section decides where traffic enters (ports, the TUN adapter), the outbound section decides where traffic exits (nodes, policy groups), the rules section decides "which traffic goes through which exit," and DNS/sniffing decide how the core identifies a connection's real target.
Top-Level Field Overview
| Field | Scope | One-line description |
|---|---|---|
mixed-port | Inbound | Combined HTTP + SOCKS5 listening port; the system proxy points here |
allow-lan | Inbound | Whether other devices on the LAN can connect to this port |
tun | Inbound | Virtual adapter configuration, catching traffic the system proxy misses |
mode | Dispatch | Three run modes: rule / global / direct |
proxies | Outbound | The node list, usually provided by a subscription |
proxy-groups | Outbound | Policy groups organizing nodes into selectable, testable sets |
proxy-providers | Outbound | Node providers, managing multiple subscriptions externally by URL |
rules | Dispatch | Routing rules, matched top-down, deciding the traffic exit |
rule-providers | Dispatch | Rule providers, externalizing large rule sets that can be auto-updated |
dns | Resolution | The built-in DNS module, including Fake-IP, split resolution, and leak prevention |
sniffer | Resolution | Domain sniffing, recovering the real domain from traffic |
external-controller | Management | The RESTful API listen address that external dashboards rely on |
log-level | Management | Log verbosity; set to debug temporarily when troubleshooting |
Reading Conventions
All YAML examples that follow use three conventions: indentation is always two spaces, never tabs; boolean values are always lowercase true / false; every snippet can be pasted directly into the client's override area (see the override section) to test the effect without touching the original subscription. The full field list with line-by-line explanations lives in the config file field categories in the quick reference; this page only expands on the parts you actually need to edit in advanced use.
The original Clash core repository is archived. The sniffer field, parts of nameserver-policy syntax, and capabilities like format: mrs covered on this page are only supported by the mihomo core. For a full comparison between the two generations of cores, see the blog post "mihomo Core vs. Original Clash Core: The Complete Difference Rundown."
Policy Group Types & Practice
Policy groups (proxy-groups) sit between nodes and rules as the dispatch layer. A rule doesn't point directly to a specific node — it points to a policy group, and the group then decides which node is actually in use at any given moment. This indirection brings two benefits: rules don't need editing when subscription nodes are added, removed, or renamed, and you can manage traffic with different strategies — "auto speed test," "failover," "manual selection" — for different purposes.
Four Common Types
| Type | Selection logic | Typical use |
|---|---|---|
select | Fully manual selection by the user, no auto-switching | Top-level master group, business groups that need a fixed exit |
url-test | Periodically tests latency and auto-selects the fastest node | General latency-sensitive traffic like everyday browsing |
fallback | Checks availability in list order; the first alive node takes effect | Primary/backup structure: use a primary node normally, fall back automatically on failure |
load-balance | Spreads connections across multiple nodes by hash or round-robin | Heavy concurrent downloads, avoiding rate limits on a single node |
There's also a relay (chained relay) type, which mihomo no longer recommends; for chaining needs, use the node-level dialer-proxy field instead, which is more predictable.
Key Parameter Trade-offs
The behavior of auto-type policy groups is governed by four parameters. url is the health-check target — pick something lightweight and stable, commonly https://www.gstatic.com/generate_204; interval is the check period in seconds — too short wastes traffic on frequent probes, too long makes failover sluggish, and 300 seconds is a balanced default for everyday use; tolerance only applies to url-test and means the latency difference between the current and a new node must exceed this many milliseconds before switching — setting it to 50–100 avoids flapping between two nodes with similar latency; when lazy is true, groups that aren't actively in use skip health checks, significantly reducing background probing.
proxy-groups:
- name: Node Selection
type: select
proxies:
- Auto Speed Test
- Failover
- HK Nodes
- JP Nodes
- DIRECT
- name: Auto Speed Test
type: url-test
url: https://www.gstatic.com/generate_204
interval: 300
tolerance: 60
lazy: true
proxies:
- HK Nodes
- JP Nodes
- name: HK Nodes
type: url-test
url: https://www.gstatic.com/generate_204
interval: 300
include-all: true
filter: "(?i)hk|hong|港"
Nested Grouping in Practice
A common approach at scale is a two-layer structure: at the bottom, build a url-test group per region (one each for Hong Kong, Japan, Singapore, using filter regex to pick nodes out of the full pool); at the top, build select groups by purpose (streaming, dev tools, a master exit — with regional groups, not individual nodes, as candidates). This way, when the subscription's node list changes, the regional groups re-absorb them automatically, and day-to-day switching happens only between the top-level groups — the panel list won't get buried under hundreds of node names. include-all: true paired with filter is a mihomo filtering syntax; since the regex is case-sensitive, prefix it with (?i) for case-insensitive matching. After writing your groups, make sure the exit names in rules match the group names exactly — wrapping group names in quotes is safer when they contain spaces or special characters.
Rule Routing Basics & Matching Order
Rules (rules) are Clash's core dispatch table, and they only take effect under mode: rule. Each rule consists of three parts: type, match value, and exit. The core compares every new connection against the list from top to bottom, and stops at the first match — subsequent rules never get evaluated for that connection. This "first match wins" principle means the order of rules is itself the priority: if the same domain could be matched by both rule 10 and rule 200, rule 10 always wins. When troubleshooting "why is this site going through the wrong exit," the first step is always checking which rule it matched in the logs or the connections view of the dashboard.
Common Rule Types
DOMAIN: Exact full-domain match — only matches an identical hostname.DOMAIN-SUFFIX: Suffix match —example.commatches itself and all subdomains; the most commonly used type.DOMAIN-KEYWORD: Keyword match — matches if the keyword appears anywhere in the domain; high false-positive rate, use with caution.IP-CIDR/IP-CIDR6: Matches by destination IP range, commonly used for LAN direct connections and specific service IP ranges.GEOIP: Matches against an IP geolocation database;GEOIP,CN,DIRECTis the standard rule for direct-connecting IPs inside mainland China.GEOSITE: Matches against a built-in domain category database (a mihomo feature) — one rule covers an entire category of sites.PROCESS-NAME: Matches by the name of the process initiating the connection — good for routing an entire app direct or through the proxy; works on desktop and in TUN mode.RULE-SET: References an external rule set, covered in the next chapter.MATCH: The catch-all rule matching everything remaining — must be the last rule, and only the last.
no-resolve & Ordering Recommendations
IP-based rules (IP-CIDR, GEOIP) have a subtle side effect: when the connection target is a domain, the core must resolve it to an IP first to compare against the rule, and that resolution may go through the local DNS, slowing down matching and potentially triggering an unnecessary lookup. Appending no-resolve to an IP rule makes it skip pure-domain connections and only match targets that are already IPs. In practice, the recommended order is: process rules and exact domain rules first, domain-suffix rules and rule sets in the middle, IP rules like GEOIP,CN after all domain rules, and MATCH last. This way, the vast majority of connections are routed at the domain stage without triggering a resolution at all.
rules:
- PROCESS-NAME,Telegram,Node Selection
- DOMAIN,dl.google.com,Node Selection
- DOMAIN-SUFFIX,openai.com,Node Selection
- RULE-SET,ads-block,REJECT
- RULE-SET,cn-sites,DIRECT
- GEOSITE,category-games,DIRECT
- IP-CIDR,192.168.0.0/16,DIRECT,no-resolve
- GEOIP,CN,DIRECT
- MATCH,Node Selection
Any rule placed after MATCH is dead code; omitting MATCH leaves unmatched traffic's fate up to the core's default behavior, which is confusing when troubleshooting. Always confirm the last rule is MATCH and that its exit points to a policy group that actually exists. For more ideas on organizing custom rules, see the mode selection section on the setup guide page.
Managing Rule Sets as Subscriptions
Writing thousands of domain rules directly into the main config has two long-term costs: the config file becomes bloated and hard to read, and updating the rule library requires replacing the whole config each time. rule-providers externalizes rules into standalone files that the core downloads and updates on a schedule, leaving just one RULE-SET reference in the main config — maintaining rule content is decoupled from the main config. This is the standard way to consume the large community-maintained rule libraries.
Field Structure
rule-providers:
cn-sites:
type: http
behavior: domain
format: yaml
url: "https://example.com/rules/cn-domains.yaml"
path: ./rules/cn-sites.yaml
interval: 86400
ads-block:
type: http
behavior: domain
format: mrs
url: "https://example.com/rules/reject.mrs"
path: ./rules/ads-block.mrs
interval: 86400
type: http means remote download (there's also file for referencing a local file); path is the local cache path — if a download fails, the core keeps using the cache, so a single network hiccup doesn't drop the whole rule set; interval is the auto-update period in seconds — rule libraries are usually updated once a day, so 86400 is plenty. format supports yaml, text, and mihomo's own binary format mrs, which is smaller and loads faster — prefer it for large rule sets.
The Three behavior Values
| Value | File content | Matching semantics |
|---|---|---|
domain | Plain domain list; +.example.com includes subdomains | The whole set matches by domain suffix, best performance |
ipcidr | Plain IP range list | The whole set matches by CIDR; no-resolve can be added when referencing it |
classical | Full rule lines, mixed types | Equivalent to inlining the rules one by one — flexible but with the highest overhead |
behavior must match the file's actual content — getting it wrong means the loaded rule set never matches anything, which is the most common reason a rule set "doesn't work," followed by a mismatch between the RULE-SET reference name and the provider's key name. Also note: downloading the rule set itself is its own piece of traffic — if the rule library's source requires a proxy to reach, add proxy: Node Selection in the provider to specify the download exit, avoiding a deadlock on first launch where "no rules loaded because the rules couldn't download."
DNS Configuration Tuning & Leak Prevention
DNS is the highest-ROI, most-overlooked area of advanced configuration. By default, the operating system's DNS queries don't go through the proxy at all: even though the traffic itself is already encrypted and forwarded, domain resolution requests may still be sent in plain text to the local ISP's DNS server — this is where "Clash DNS leaks" come from, and the resolution log fully exposes which sites were visited. Enabling the core's dns module and configuring it in proper layers is what brings resolution under control too.
Three Layers of Resolvers
mihomo's DNS config has three layers. default-nameserver does exactly one thing: resolve the domains of the DoH/DoT servers used later, so it must be filled with plain IP addresses; nameserver is the main resolver group handling most queries — fill it with encrypted-transport (DoH) addresses; fallback is an optional secondary group used together with fallback-filter — when the main group's result matches the filter condition (e.g., the resolved IP falls outside GEOIP CN), the fallback group's result is used instead. The point of this mechanism is to let a fast domestic resolver handle domains inside mainland China and an uncensored encrypted resolver handle everything else, balancing speed and accuracy.
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
- 119.29.29.29
nameserver:
- https://doh.pub/dns-query
- https://dns.alidns.com/dns-query
fallback:
- https://1.1.1.1/dns-query
- https://8.8.8.8/dns-query
fallback-filter:
geoip: true
geoip-code: CN
ipcidr:
- 240.0.0.0/4
Fine-Grained Routing with nameserver-policy
If fallback's "pick one of two results" isn't fine-grained enough, nameserver-policy lets you assign a resolver directly by queried domain: the key can be a domain suffix, a geosite: category, or even a rule-set: reference, and the value is a list of resolvers. A typical use is routing geosite:cn through a domestic DoH and everything else through an overseas DoH — splitting traffic right at the query entry point instead of patching things up after the fact.
dns:
nameserver-policy:
"geosite:cn":
- https://doh.pub/dns-query
"+.internal.corp":
- 10.0.0.53
After configuring, actually verify it: visit any DNS leak test site in your browser and check whether the listed resolvers still include your local ISP's address. If using system proxy mode, note that the browser's own secure DNS (DoH) setting may bypass Clash's direct resolvers — disable the browser's built-in DoH before testing. In TUN mode, pairing with dns-hijack can force-intercept all plaintext resolution — see the next chapter.
TUN Mode & the Fake-IP Mechanism
The system proxy essentially works by "announcing": the OS writes the proxy address into the environment config, and cooperative applications (browsers, most desktop software) voluntarily hand their traffic to Clash. But command-line tools, some game clients, and nearly all UDP traffic ignore this arrangement entirely — their connections bypass the proxy directly. TUN mode takes a different approach — it creates a virtual network adapter in the system and takes over the default route, intercepting all traffic at the network layer where applications have no way around it. For a full comparison of the two mechanisms and when to use each, see the blog post "What's the Difference Between Clash TUN Mode and System Proxy."
Configuring the tun Field
tun:
enable: true
stack: mixed
auto-route: true
auto-detect-interface: true
dns-hijack:
- any:53
- tcp://any:53
stack determines the protocol stack that processes TUN packets: system reuses the OS network stack directly — good performance but sensitive to system environment; gvisor is a userspace implementation with the best compatibility; mixed combines the strengths of both (TCP through system, UDP through gvisor) and is the recommended value for most scenarios today. auto-route automatically writes routing table entries directing traffic to the virtual adapter, and auto-detect-interface auto-detects the physical outbound interface to prevent loopback; both are generally left on, unless you need manual routing control on a complex network topology like a router. dns-hijack force-intercepts plaintext DNS queries headed to port 53 on any address and hands them to the core for resolution — this is the key piece for sealing DNS leaks in TUN mode. Enabling TUN on desktop requires admin privileges or an installed system service; both Clash Plus and Clash Verge Rev walk you through granting permission the first time you enable it.
Fake-IP: Why the Resolved Address Starts With 198.18
enhanced-mode: fake-ip pairs best with TUN. Here's the mechanism: when an app queries a domain, the core doesn't do a real resolution — it immediately hands back a "fake IP" allocated from the reserved range 198.18.0.0/16 and remembers the mapping between that IP and the domain; when the app then connects using the fake IP, the core looks up the mapping to recover the domain and routes by the domain rules. Two benefits: skipping the wait for a real resolution means faster connection setup, and the domain information survives all the way to the rule-matching stage, so routing accuracy isn't affected by resolution. The cost is that a handful of apps relying on real IPs may misbehave — LAN discovery, NTP time sync, some game launchers — and those domains should go into the fake-ip-filter allowlist so they get real resolution:
dns:
fake-ip-filter:
- "*.lan"
- "*.local"
- "time.*.com"
- "ntp.*.com"
- "+.stun.*.*"
If your use case is mostly browser-based and you often need to hand resolved results off to third-party programs, you can fall back to enhanced-mode: redir-host (real IP mode), at the cost of waiting for a real resolution on every connection. You can switch between the two modes at any time; after switching, it's worth flushing the DNS cache once before observing the result.
Domain Sniffing: Recovering Domains From IPs
Rule routing works best with domains as input, but two kinds of traffic arrive at the core with only an IP left: first, when an app's own DNS (like a browser's built-in DoH) bypasses the core's resolution and the connection target is already a real IP; second, when a program still has a cached resolution result for something outside the Fake-IP allowlist. Once the target is down to just an IP, every domain rule becomes useless, and traffic can only fall back to GEOIP or MATCH — routing accuracy takes a real hit. Domain sniffing (sniffer) is the fix: the core inspects the plaintext features in a connection's header (the Host header in an HTTP request, the SNI field in a TLS handshake, a QUIC initial packet), "sniffs out" the real domain, and re-runs rule matching with it.
Configuration Example
sniffer:
enable: true
sniff:
HTTP:
ports: [80, 8080-8880]
override-destination: true
TLS:
ports: [443, 8443]
QUIC:
ports: [443, 8443]
force-domain:
- "+.v2ex.com"
skip-domain:
- "+.push.apple.com"
- "Mijia Cloud"
The three protocol blocks each declare which ports to attempt sniffing on; when override-destination is true, the sniffed domain overrides the connection's target address, so subsequent rules and logs both display the domain. Domains listed in force-domain get the sniffed value forced even if a resolution result already exists — useful for sites where the CDN domain and resolution result don't line up; skip-domain is the opposite — targets listed there skip sniffing entirely, which matters for things like Apple push and some vendors' long-lived connections that are sensitive to header inspection and can drop unexpectedly if scrutinized. Sniffing only reads fields that are already plaintext during the protocol handshake — it never decrypts any encrypted payload; its overhead is concentrated on the first packet of each new connection and is barely noticeable in daily use. Running TUN + Fake-IP + sniffing together is currently the highest-accuracy routing combination available.
Local Overrides & Multi-Subscription Merging
A subscription file is generated server-side, and every update overwrites the local content wholesale — editing DNS settings or adding rules directly in the subscription file means losing all of it on the next update. The right approach is to put personal customizations in an "override" layer: the client automatically merges your override content into the final config every time the subscription loads, keeping the subscription and your customizations independent of each other. This is the single most valuable habit to build for long-term Clash use.
Override Capabilities by Client
Clash Plus, the top pick on the download page, has a built-in override editor supporting appending or replacing fields as YAML snippets, well-suited to putting every example on this page into practice; Clash Verge Rev offers two global extension modes — Merge (declarative merging) and Script (programmatic modification in JavaScript) — where Merge directly replaces whole matching fields like dns and tun from the subscription, with a prefix syntax for appending to lists, and Script for anything more complex; FlClash also supports config overrides, preserving custom rules on mobile too. For the full path to migrating override configs away from a discontinued client, see the blog post "What to Use After Clash for Windows Stopped Updating." A typical Merge override looks like this:
dns:
enable: true
enhanced-mode: fake-ip
prepend-rules:
- DOMAIN-SUFFIX,corp.example.com,DIRECT
append-rules:
- GEOIP,CN,DIRECT
Merging Multiple Subscriptions With proxy-providers
If you have subscriptions from multiple providers, there's no need to switch configs back and forth in the client. proxy-providers lets you declare each subscription as a node provider, then reference them with use inside policy groups — nodes from multiple subscriptions flow into one unified grouping system for shared speed testing and routing:
proxy-providers:
provider-a:
type: http
url: "https://example.com/sub-a?token=xxxx"
path: ./providers/a.yaml
interval: 3600
health-check:
enable: true
url: https://www.gstatic.com/generate_204
interval: 300
provider-b:
type: http
url: "https://example.net/sub-b?token=xxxx"
path: ./providers/b.yaml
interval: 3600
proxy-groups:
- name: All Nodes
type: url-test
use:
- provider-a
- provider-b
url: https://www.gstatic.com/generate_204
interval: 300
Provider-level health-check gives nodes a health status before any group even uses them; the filter and exclude-filter fields can pre-filter out non-node entries like expiration notices and traffic announcements at the provider level. Note that two subscriptions sharing a node name causes a load conflict; use override.additional-prefix to prefix each provider's nodes consistently to distinguish their source. For a LAN setup sharing this merged config across multiple devices, see the blog post "Clash Mixed Port & Allow LAN Connections Setup."
Connecting an External Control Dashboard
The core exposes a RESTful API while running — switching policy groups, testing latency, viewing connections, and reloading config can all be done through it, and the desktop client's graphical UI is, at its core, a wrapper around this same API. When Clash runs on a router, a NAS, or a headless server (see deployment ideas in the blog post "An Overview of Running the Clash Core Directly on a Router"), an external dashboard is the only convenient way to manage it.
Enabling the API & Dashboard
external-controller: 127.0.0.1:9090
secret: "your-strong-secret"
external-ui: ./ui
external-ui-url: "https://example.com/dashboard/dist.zip"
external-controller sets the API's listen address — keep it at 127.0.0.1 for local-only management, or change it to 0.0.0.0 if other devices on the LAN need access; secret is the API access token, sent by the dashboard as an Authorization: Bearer header. external-ui points to a local static dashboard directory, and paired with external-ui-url, the core can auto-download the dashboard release — after that, open http://127.0.0.1:9090/ui in a browser. Popular community dashboards include metacubexd, zashboard, and the yacd family, all plain static pages — pick whichever one you like the look of; desktop users usually don't need to deploy one separately, since Clash Plus and Clash Verge Rev already have an embedded dashboard view.
Calling the API Directly
# Check the core version
curl -H "Authorization: Bearer your-strong-secret" \
http://127.0.0.1:9090/version
# Switch the "Node Selection" group to "HK Nodes"
curl -X PUT -H "Authorization: Bearer your-strong-secret" \
-d '{"name":"HK Nodes"}' \
http://127.0.0.1:9090/proxies/Node Selection
# Trigger a latency test on a given group
curl -H "Authorization: Bearer your-strong-secret" \
"http://127.0.0.1:9090/group/Auto Speed Test/delay?url=https://www.gstatic.com/generate_204&timeout=3000"
The API has complete control over the core. Exposing external-controller on 0.0.0.0 without a secret hands proxy control to any device on the LAN — and if that device happens to have a public address, the risk multiplies further. The rule of thumb: use 127.0.0.1 whenever possible instead of listening on the whole network; if you must listen network-wide, set a strong token and make sure your firewall doesn't expose port 9090 to the public internet.
That covers all seven advanced configuration topics. A good practical path: first confirm basic connectivity works on the setup guide page, then work through the DNS and policy group chapters, applying examples block by block and verifying each change in the dashboard's connections view; check field meanings anytime in the quick reference, see client options and downloads on the download page, and read the site's purpose and maintenance principles on the about page.