MANUAL / ADV · Reference Guide · Works with Clash-family Cores

Advanced Clash Configuration Guide

This page lays out the full toolbench: proxy groups, rule providers, DNS, TUN, sniffing, overrides, and the controller panel—seven advanced components broken down spec by spec, plus a troubleshooting quick-reference table. If this is your first setup, start with the main flow in Getting Started—import a subscription, pick a node, verify the connection, done in about ten minutes. Once that's working and you want smoother routing or easier subscription management, come back here and read chapter by chapter. Haven't installed a client yet? Grab one from the client download page.

Compatible with Clash-family core syntax Ready-to-use YAML examples Jump to any of the eight chapters

Reading tip: jump to whatever chapter you need—no need to read start to finish. Back up your current config before making changes.

CH-01Proxy Groups: The Gear-Shifting Mechanism of Your Config

Start by separating two jobs. Rules decide who handles a given piece of traffic. Proxy groups decide which node actually carries it once a rule has claimed it. In a config built to last, almost every rule points to a proxy group rather than a single node—nodes come and go, but a group's name stays put. Set the groups up properly, and swapping providers or nodes later is just swapping magazines, not rebuilding the gun.

Five Types, One Spec Table

TypeHow it picks a nodeTypical use
selectManual pick, stays fixed until changedMaster switch, region toggles, anything needing a human decision
url-testTests latency on a schedule, auto-picks the fastestEveryday auto mode, the default destination for most traffic
fallbackTries the list in order, moves down only when the current one failsScenarios with a clear primary/backup priority
load-balanceSpreads connections across multiple nodesSpreads load when a single node is rate-limited; some services are sensitive to IP changes
relayChains nodes in order, forwarding traffic hop by hopChained proxying—high overhead and latency, use only for special needs

The picking rule is simple: use url-test when you're happy to let the machine decide, use select when a human should call the shot, and treat the other three as special-purpose parts for specific scenarios. Get comfortable with just the first two and you've already covered ninety percent of what you'll need.

url-test's Three Knobs

url is the test target—by convention a lightweight endpoint that returns a 204 status, such as https://www.gstatic.com/generate_204. It sends back an empty response, so what you're measuring is pure link latency, not page-load time. interval is the test cycle in seconds; 300 means one round every five minutes. Set it too low and you flood the network with test requests; set it too high and a dead node can sit undetected for ages. tolerance is the allowed difference in milliseconds: if the gap between the current fastest node and a new candidate stays under this value, the group won't switch. Skip it, and two nodes bouncing between 80ms and 85ms will make the group flap back and forth, breaking long-lived connections along the way. One more knob worth knowing: lazy: true skips testing altogether while the group is idle, saving a batch of pointless background requests.

A Layering Structure That Holds Up

In practice, three layers work well: a select master switch at the top, auto-test and per-region groups in the middle, and individual nodes at the bottom. Rules always point to a group name, never to a specific node. To lock onto a region manually, switch it in the master switch; to go fully automatic, switch back to the auto-test group. Here's the layout:

proxy-groups:
  - name: Manual-Switch
    type: select
    proxies: [Auto-Speed, HK-Nodes, JP-Nodes, DIRECT]

  - name: Auto-Speed
    type: url-test
    url: https://www.gstatic.com/generate_204
    interval: 300
    tolerance: 50
    lazy: true
    proxies: [HK-01, HK-02, JP-01, JP-02]

  - name: HK-Nodes
    type: url-test
    url: https://www.gstatic.com/generate_204
    interval: 300
    proxies: [HK-01, HK-02]

  - name: JP-Nodes
    type: select
    proxies: [JP-01, JP-02]
TipA group name is basically an interface identifier in your config. Rename a group and update every rule that references it—pointing at a group that doesn't exist makes the core fail outright at startup, and nothing gets through.

CH-02Rule Providers: Turning Rules into Swappable Magazines

Writing hundreds of rules directly into your main config is like welding every part to the chassis—swapping out even one means tearing down the whole machine. rule-providers takes a different approach: rules live in pluggable magazines. Your main config keeps just a one-line RULE-SET reference, while the actual magazine sits at an external URL and updates itself on schedule. The maintainer edits the magazine; you don't touch anything.

Field Reference

Each provider has five key fields. type is either http (fetched from a URL) or file (read from a local file); behavior declares what kind of ammunition the magazine holds—more on this below; format is the file format, either yaml or text; path is the local cache path after download; interval is the auto-update cycle in seconds, with 86400 meaning once a day. The cache matters because if you restart offline, the core reads the local copy directly instead of refusing to start just because it couldn't fetch a rule set.

Choosing Among the Three behavior Options

A domain magazine holds only domain names, and the core builds a dedicated index for it—fastest matching, lowest memory use. ipcidr holds only IP ranges and gets the same dedicated treatment. classical can hold anything (DOMAIN-SUFFIX, IP-CIDR, PROCESS-NAME all mixed together), which is flexible but matches rule by rule, so performance drops once the list grows. The rule of thumb: use domain for a pure domain list, ipcidr for a pure IP list, and reserve classical for genuinely mixed content. Loading a pure domain list into classical works, but it's like using a delivery truck to send a single envelope.

A Working Example and Update Cadence

rule-providers:
  streaming:
    type: http
    behavior: classical
    format: yaml
    url: "https://example.com/rulesets/streaming.yaml"
    path: ./rulesets/streaming.yaml
    interval: 86400
  cn-ip:
    type: http
    behavior: ipcidr
    format: text
    url: "https://example.com/rulesets/cn-ip.txt"
    path: ./rulesets/cn-ip.txt
    interval: 86400

rules:
  - RULE-SET,streaming,Manual-Switch
  - RULE-SET,cn-ip,DIRECT,no-resolve
  - MATCH,Manual-Switch
About no-resolveIP-based rules (IP-CIDR, or a RULE-SET pointing to an ipcidr magazine) trigger a DNS lookup by default just to check for a match. Adding no-resolve means matching only happens when the traffic already carries an IP—no proactive lookup. That saves a round of waiting and keeps the lookup from going through a channel it shouldn't. Place IP rules after domain rules, and attach this flag to as many of them as you can.

Set the update interval based on the magazine's nature: a once-a-day refresh is plenty for community-maintained routing lists, while lists you maintain and tweak often can drop to a few hours. Troubleshooting a failed rule-provider fetch follows the same logic as a failed subscription update—see the troubleshooting category in the Help Center.

CH-03DNS Tuning: Half of Your Problems Live in Resolution

"The proxy is on but the page won't load," "the rule is right there but never matches"—half the time, DNS is the culprit. The core ships with its own DNS resolver: get it configured well and resolution is fast and stable; get it wrong and you'll cycle through poisoning and leaks. This chapter walks through every slot in the resolution chain.

Understand the Resolution Chain First

The resolver has three layers. default-nameserver is the bootstrap layer: when your primary resolver is itself a domain name (like the DoH address https://doh.pub/dns-query), something has to resolve that domain to an IP first, and that's the bootstrap layer's job—so it can only hold plain IPs; putting a domain here creates a chicken-and-egg loop. nameserver is the primary layer that handles everyday lookups. fallback is the backup layer, there specifically for cases where the primary layer might be poisoned. Each layer has its own job—don't stuff a DoH address into the bootstrap layer, and don't leave the primary layer empty.

How nameserver and fallback Divide the Work

The two work concurrently: every query goes out to the primary and backup at the same time, and fallback-filter decides whose answer to trust. The most common decider is geoip: true combined with geoip-code: CN—if the primary layer's answer lands in mainland China's IP ranges, it's trusted as a legitimate domestic result; if it lands outside those ranges, the backup's answer is used instead (a domestic resolver returning a non-mainland IP looks suspiciously like poisoning). The cost is one extra query per lookup, and what you get in return is resistance to poisoning. If your primary layer already runs over an encrypted channel (DoH/DoT), the poisoning risk is already low, so fallback can be kept simple.

nameserver-policy: Assigning a Dedicated Resolver per Domain

For some domains you know exactly where they should be resolved: services within mainland China should use a mainland resolver to get the nearest CDN node, while speed-test and LAN domains each have their own destination. nameserver-policy is that routing table, and it supports wildcards and geosite categories. Full example:

dns:
  enable: true
  listen: 0.0.0.0:1053
  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
  nameserver-policy:
    "geosite:cn": https://doh.pub/dns-query
    "+.lan": 223.5.5.5
TipAfter changing the dns section, restart the core and reconnect your network before testing—both the system and individual apps keep resolution caches, and without a clean slate the new config can look like it isn't working. The difference between the two enhanced-mode values is covered in the next chapter.

CH-04TUN & Fake-IP: System-Level Fallback and the Ticket-Number Trick

TUN: Taking Over Traffic That Won't Play Along

System proxy settings work on the honor system: only apps that bother to check them actually use them. Command-line tools, some game clients, and certain desktop software ignore the setting entirely and send traffic straight out. TUN mode takes a different tack—it inserts a virtual network adapter and intercepts every bit of the device's outbound traffic at the network layer, handing it to the core. It doesn't matter whether an app cooperates; anything trying to sneak past still goes through security.

Worth clarifying for Android: the VPN channel the client requests from the system when you tap "Start" is, at its core, a TUN adapter. So Android users are already in TUN mode by default and don't need to configure any of this—it's desktop platforms that require explicitly turning TUN on. Client downloads for each platform are on the download page, with a side-by-side comparison in the comparison guide. Key fields on desktop: stack is the protocol stack implementation—system performs well but depends on OS support, gvisor is a user-space implementation with steady compatibility, and mixed takes the best of both; when unsure, pick mixed. auto-route takes over the routing table automatically. dns-hijack intercepts plaintext DNS requests headed to any port 53 and redirects them back to the core, preventing an app's built-in resolver from bypassing your routing rules.

Fake-IP: Hand Out a Ticket Number First, Handle Business Later

Think of it like a bank's ticket-number system. When an app asks "what's the IP for this domain," the core skips real resolution and instantly hands out a fake address from the reserved range 198.18.0.1/16—zero latency. The app connects using that fake address; once the connection reaches the core, the core looks up the ticket number, recovers the real domain, and routes by domain rule. If the traffic needs to go through a proxy, the actual resolution happens at the exit side instead. Two benefits follow: connections start faster because there's no local resolution wait, and domain-rule hit rates stay high because the core always knows which domain sits behind each connection.

The catch is that a ticket number is useless outside the branch. LAN device discovery, printers, some online games, and anything that needs to report a real IP back to a server all fall apart once they're handed a fake address. The fix is fake-ip-filter: domains on the list skip the ticket number entirely and get a genuine resolution instead.

tun:
  enable: true
  stack: mixed
  auto-route: true
  auto-detect-interface: true
  dns-hijack:
    - any:53

dns:
  enhanced-mode: fake-ip
  fake-ip-range: 198.18.0.1/16
  fake-ip-filter:
    - "*.lan"
    - "+.local"
    - "+.msftconnecttest.com"
    - "+.stun.*.*"

fake-ip vs. redir-host

Aspectfake-ipredir-host
Local resolution waitNone—returns a fake address immediatelyYes—waits for real resolution to finish
Domain-rule hit rateStable—the core always holds the domain mappingDepends on the resolution result; blind in some cases
LAN compatibilityNeeds fake-ip-filter to allow throughNaturally compatible
Best suited forMost everyday useHeavy LAN service use, special compatibility needs
TipAfter switching between the two enhanced-mode values, always disconnect and reconnect once: an app may still be holding an address issued under the old mode, and mixing old and new ticket numbers can cause a stretch of bizarre connection failures.

CH-05Domain Sniffing: A Barcode Reader at the Inbound Gate

Fake-IP patches one gap, but there's still a crack in the chain: some traffic arrives at the core carrying only an IP, no domain. The usual culprits are apps with their own encrypted resolution (built-in DoH that bypasses the core's DNS) or clients that connect directly to hardcoded IPs. Domain rules go blind for this traffic entirely, leaving only IP rules or MATCH as a fallback—and routing accuracy drops sharply.

sniffer acts as a barcode reader at the inbound gate: as traffic comes in, it reads the domain back out of the TLS handshake's SNI field or the HTTP request's Host header, tags the connection with it, and only then sends it on for rule matching. Once the domain is recovered, the rules work as intended.

Config Fields

Under sniff, you declare which ports to scan per protocol: TLS typically covers 443 and 8443, while HTTP covers 80 and common proxy port ranges. override-destination decides whether a sniffed domain replaces the original target address—recommended for HTTP. force-domain is a mandatory-sniff list: domains on it get re-sniffed even if a resolution already exists; skip-domain is an exemption list—for services with strict certificate checks or that are sensitive to SNI changes (Apple push notifications are a regular offender), sniffing can actually break the connection, so listing them lets traffic through untouched.

sniffer:
  enable: true
  sniff:
    TLS:
      ports: [443, 8443]
    HTTP:
      ports: [80, 8080-8880]
      override-destination: true
  force-domain:
    - "+.v2ex.com"
  skip-domain:
    - "+.push.apple.com"
NoteSniffing isn't free: every connection that hits a listed port gets an extra packet inspection. Declare port ranges based on actual need—don't take the shortcut of scanning 0-65535. If an app starts dropping connections frequently once sniffing is on, the first move is adding its domain to skip-domain.

CH-06Local Overrides & Multi-Subscription Merging: Changes That Survive Updates

Why Not Just Edit the Subscription File Directly

Editing rules or adding nodes straight into the file your subscription delivers feels convenient in the moment, but it ends badly: the next subscription update overwrites the entire file with the server's new version, wiping out every change you made. The right approach is overrides—treat the subscription's original file as read-only forever, keep your changes in a separate local layer, and have them automatically replayed onto the new file after every update. Your changes and the subscription stop interfering with each other.

Where to Find Overrides in Your Client

Mainstream clients all build in this mechanism—the name varies, but the idea is the same. Clash Verge Rev, for example, offers two tiers: Merge combines declarative snippets, good for structured changes like appending rules or proxy groups; Script runs actual code to rewrite the whole config programmatically, suited to more involved work like bulk-renaming nodes or conditional filtering. Don't reach for Script if Merge can do the job—a declarative snippet is readable at a glance, while a broken script is far harder to debug. The comparison guide has a dedicated section on how override support differs across clients.

Merging Multiple Subscriptions with proxy-providers

If you're running subscriptions from more than one provider, don't bother switching config files back and forth. proxy-providers works on the same idea as the rule-provider concept: each subscription becomes a node magazine, and a proxy group's use field can mount several magazines at once, blending nodes from every provider into a single auto-test group where the fastest one wins.

proxy-providers:
  airport-a:
    type: http
    url: "https://example.com/sub/a.yaml"
    path: ./providers/a.yaml
    interval: 43200
    health-check:
      enable: true
      url: https://www.gstatic.com/generate_204
      interval: 600
  airport-b:
    type: http
    url: "https://example.com/sub/b.yaml"
    path: ./providers/b.yaml
    interval: 43200
    health-check:
      enable: true
      url: https://www.gstatic.com/generate_204
      interval: 600

proxy-groups:
  - name: All-Nodes
    type: url-test
    use: [airport-a, airport-b]
    url: https://www.gstatic.com/generate_204
    interval: 300

health-check periodically checks the health of nodes inside a magazine, and the auto-test group skips any that go down; interval: 43200 means the subscription itself is re-fetched every twelve hours. For recognizing subscription link formats and where to import them, see this article first: Clash Subscription Link Import Formats.

Hard LineA subscription URL usually carries your identity token—it is the credential itself. Always redact the url field before posting screenshots or asking for help, and never upload a config file with a live subscription address to a public repository; that's the same as leaving your keys hanging on the door.

CH-07External Controller: Giving the Core a Remote Interface

Three Fields to Enable Remote Control

external-controller declares the listen address for the RESTful API, conventionally 127.0.0.1:9090; secret is the access password that every request must include; external-ui points to a local directory holding the web dashboard's static files, so pointing a browser at the listen address opens the graphical interface directly. Every dashboard out there (metacubexd, yacd, and their relatives) is essentially a different skin on the same API underneath.

external-controller: 127.0.0.1:9090
secret: "your-strong-password"
external-ui: ./ui

No Dashboard Needed: Three Handy Commands

The API is a usable tool on its own—call it directly from scripts or automation:

# List all proxy groups and node status
curl -H "Authorization: Bearer your-strong-password" \
  http://127.0.0.1:9090/proxies

# Switch the "Manual-Switch" group to HK-01
curl -X PUT -H "Authorization: Bearer your-strong-password" \
  -d '{"name":"HK-01"}' \
  http://127.0.0.1:9090/proxies/Manual-Switch

# Test latency for a single node
curl -H "Authorization: Bearer your-strong-password" \
  "http://127.0.0.1:9090/proxies/HK-01/delay?timeout=5000&url=https://www.gstatic.com/generate_204"

Three Panels Worth Using Regularly

Connections: watch in real time which rule and which exit each connection is using—questions like "why did this traffic go direct" get answered at a glance here. Logs: bump the level to debug temporarily to print the full rule-matching process line by line; remember to switch it back afterward, since debug logging is very verbose. Provider management: manually trigger an immediate update for a subscription or rule provider instead of waiting for interval to elapse.

Hard LineBinding external-controller to 0.0.0.0 without setting a secret is like leaving the remote hanging at the front door of the public internet: anyone who can reach that port can change your exit node and watch your connections. Lock it to 127.0.0.1 for local-only use; if you genuinely need LAN access, a strong password is mandatory.

CH-08Config Troubleshooting Quick Reference

Match your symptom, check the most common cause first, then follow the link to the relevant chapter or article. Most issues get resolved within the first two steps.

SymptomMost likely causeCheck here first
Changed a rule but nothing happenedEdited the raw subscription file; an update wiped it outCH-06 Overrides
Subscription update failsLink expired, or the update request itself never went throughHelp Center troubleshooting category
All node latencies time outTest URL unreachable, or system clock drift is too largeFirst Connection & Latency Testing
Some apps bypass the proxyThe app ignores system proxy settingsCH-04 TUN; for Windows Store apps, see UWP Loopback Restriction Fix
Latency looks fine but pages won't loadA DNS config issue, or stale cache after switching modesCH-03 DNS and CH-04 Fake-IP
LAN devices or printers become unreachableA local domain got issued a Fake-IP ticket numberfake-ip-filter in CH-04
An app drops connections frequently once sniffing is onThe service is sensitive to SNI changesskip-domain in CH-05

For anything not covered in the table, browse the Help Center under its four categories—Basics / Setup / Usage Tips / Troubleshooting. If you suspect the client itself is the problem, first check its capability boundaries in the comparison guide, and if needed, switch to the top-recommended Clash Plus from the download page. If you've forgotten a basic step, head back to Getting Started for a refresher.