clashsg.com › manual

Clash Config File Reference

A field-by-field lookup for config.yaml: structure overview, general fields, DNS, proxies, proxy groups, providers, rule syntax, TUN, and override merging, with runnable YAML examples throughout. This page is a reference, not a getting-started guide — for a first install and subscription import, start with the User Guide and follow the three-step flow; come back here whenever a specific field needs clarifying. Grab the client installer from the Downloads page; Clash Plus is our top pick.

1. YAML Structure Overview: What Makes Up a Config File

A Clash config file is a standard YAML document, named config.yaml by default. YAML has three hard rules worth memorizing before you go further: first, hierarchy is expressed purely through indentation — two spaces per level by convention, and tabs are forbidden; second, a colon must be followed by a space, so port:7890 is invalid while port: 7890 is correct; third, values containing colons, hashes, curly braces, or other special characters need to be quoted, especially passwords and URLs. Anything after a # to the end of the line is a comment and is ignored when the core loads the file.

A complete config file breaks into roughly four layers, top to bottom: inbound and runtime settings (ports, mode, logging), the DNS section, outbound resources (the proxies node list, proxy-groups, and the two kinds of providers), and finally the rules list. The core processes every connection in the order "rule match → proxy group decision → node egress," and the layout of the config file mirrors that data path. Here's a quick-reference table of the common top-level fields:

Top-Level FieldTypePurpose
mixed-portIntegerCombined HTTP + SOCKS5 inbound port; the modern default for most clients
port / socks-portIntegerSeparate HTTP and SOCKS5 inbound ports; use either these or mixed-port, not both
allow-lanBooleanWhether other devices on the LAN can connect to the local proxy port
modeEnumThree run modes: rule / global / direct
log-levelEnumLog verbosity — switch to debug when troubleshooting, use info or warning day to day
external-controllerStringAddress the RESTful control API listens on; dashboards and client UIs depend on it
dnsMappingThe built-in DNS module — fake-ip and split resolution are both configured here
proxiesArrayThe proxy node list, one mapping object per node
proxy-groupsArrayThe proxy group list, deciding who picks a node and how it's picked
proxy-providers / rule-providersMappingExternal subscription node sets and external rule sets
rulesArrayRouting rules, matched top to bottom — the first match applies
tunMappingVirtual adapter that takes over system-wide traffic, catching apps that bypass the system proxy

Below is a minimal config that actually runs, with all five parts present. Every later chapter builds on this skeleton:

config.yaml · Minimal Runnable Example
mixed-port: 7890
allow-lan: false
mode: rule
log-level: info

proxies:
  - name: "NodeA"
    type: ss
    server: example.com
    port: 8388
    cipher: aes-128-gcm
    password: "your-password"

proxy-groups:
  - name: "PROXY"
    type: select
    proxies:
      - NodeA
      - DIRECT

rules:
  - DOMAIN-SUFFIX,example.com,PROXY
  - GEOIP,CN,DIRECT
  - MATCH,PROXY

The number one cause of load failures is indentation: one extra or missing space on a single line breaks parsing for the whole file, and the client usually reports nothing more than a single "yaml: line N" error. Set your editor to show whitespace and convert tabs to spaces before editing — that alone avoids the vast majority of syntax problems.

2. General Fields: Ports, Modes, and the Control API

Inbound Ports: mixed-port Does the Job of Three

Older configs used to split port: 7890 (HTTP) and socks-port: 7891 (SOCKS5) into two separate settings. These days, use mixed-port: 7890 instead — the same port auto-detects both protocols, so system proxy settings and every app's proxy field can just point at it, with no need to track which protocol is which. 7890 is only a community convention, not a reserved value; if the startup log shows "address already in use," some other process has claimed that port, so pick an unused one instead.

allow-lan and bind-address

allow-lan: true exposes the inbound port to other devices on the same LAN — commonly used to let a smart TV box or game console borrow the Clash instance running on a computer. Pair it with bind-address to restrict the listening address: the default "*" listens on every network interface, while setting a specific local IP limits exposure to a single subnet. Confirm the network is trustworthy before enabling this — turning on allow-lan at an office or on public Wi-Fi effectively hands your outbound channel to anyone else on the same segment.

mode: What Each Run Mode Actually Does

mode: rule is the everyday setting, checking every connection against the rule table one entry at a time; global skips the rule table entirely and sends all traffic to whatever node the global proxy group has selected — handy for quickly telling whether a problem is a rule issue or a node issue; direct bypasses proxy logic altogether and connects everything directly. All three modes can be switched instantly from the client UI without touching the file, but whatever value is written in the config determines the core's starting mode on launch.

log-level and external-controller

log-level runs from least to most verbose: silent / error / warning / info / debug. Switch to debug when investigating why a particular rule isn't firing — the log will print the exact rule each connection matches and its final egress; switch back to info once you've confirmed the fix, since debug-level logging isn't kind to a system running long-term. external-controller: 127.0.0.1:9090 opens the RESTful control API, which is how the client's dashboard handles node switching, latency tests, and the connection list; the secret field puts a password on that API — always write a placeholder value like secret: "xxxx" in examples; external-ui points at a local directory that can host a web dashboard. If the control API is bound to 0.0.0.0, a secret is mandatory, or anyone on the LAN can alter your proxy state.

config.yaml · General Fields Section
mixed-port: 7890
allow-lan: true
bind-address: "*"
mode: rule
log-level: info
ipv6: false
external-controller: 127.0.0.1:9090
secret: "xxxx"

ipv6: false tells the core not to resolve or use IPv6 addresses. When the network's IPv6 connectivity is poor, turning this off avoids a class of timeouts where a v6 address resolves but never actually connects; turn it back on once both the network and the nodes support v6. This switch interacts with resolution behavior in the DNS section, so it's best to restart the core rather than hot-reload after changing it.

3. DNS Configuration: fake-ip, Split Resolution, and Anti-Pollution

The DNS section decides how domain names become IP addresses, which directly affects routing accuracy and whether connections succeed at all. enable: true turns on the built-in DNS module; listen sets the module's own listening address, used alongside dns-hijack in TUN mode. The part that actually needs understanding is the two possible values of enhanced-mode.

fake-ip vs. redir-host

Under fake-ip, the core first hands every domain a fake address from fake-ip-range (default 198.18.0.1/16); the app connects using that fake address, and the core reconstructs the real domain from its fake-IP-to-domain map before running rule matching. The upside is that domain rules match with total reliability and skip a local resolution round trip; the cost is that a small number of programs relying on real IPs — LAN discovery, some game multiplayer — get confused by the fake address, so those domains need to go on the fake-ip-filter allowlist to get real resolution results instead. redir-host resolves first and matches second, which is straightforward but can let domain-based rules slip past because the IP was already known before matching happened. Use fake-ip for everyday desktop and mobile use; fall back to redir-host in environments with more compatibility issues.

nameserver, fallback, and fallback-filter

nameserver is the primary resolver group — fill it with reachable, low-latency resolvers, written as udp, tls:// (DoT), or https:// (DoH); fallback is the backup group, typically filled with trustworthy DoH resolvers outside mainland China. Both groups are queried in parallel, and fallback-filter decides which answer to trust: with geoip: true and geoip-code: CN, an answer from the primary group is trusted if it resolves to a mainland China IP, and the fallback answer is used otherwise — a way of sidestepping polluted responses. The mihomo core additionally supports nameserver-policy, which routes specific domain suffixes to specific resolvers for finer-grained control.

config.yaml · dns Section
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"
  nameserver:
    - https://223.5.5.5/dns-query
    - 119.29.29.29
  fallback:
    - https://1.1.1.1/dns-query
  fallback-filter:
    geoip: true
    geoip-code: CN

"Connected but the page won't load" always has DNS on the checklist: leftover fake-ip cache entries, a fallback group that's timing out entirely, or the system DNS fighting the core's DNS can all produce the same symptom. A step-by-step verification walkthrough is in "Clash Connected But No Internet: A Step-by-Step Checklist From System Proxy to DNS."

4. Proxy Node Fields: proxies by Protocol

proxies is an array in which each element describes one outbound node. Four fields are shared across every protocol: name (the node's name, unique across the file, referenced by proxy groups), type (the protocol), server (the server address, domain or IP), and port. udp: true declares that the node forwards UDP traffic, which voice calls and games depend on. Quote names that contain non-ASCII characters, spaces, or special symbols to avoid parsing ambiguity.

Shadowsocks (ss)

The leanest protocol in terms of fields: cipher sets the encryption method (commonly aes-128-gcm or chacha20-ietf-poly1305), and password is the pre-shared key. The cipher must match the server exactly — getting it wrong doesn't produce an error, it produces a connection that "connects" but is garbage at the data level, with latency tests that time out forever.

VMess

The core fields are uuid (the user identity) and alterId (always 0 in modern deployments). cipher: auto lets both ends negotiate encryption; the transport is chosen via network, and beyond plain TCP the most common choice is ws (WebSocket), where ws-opts supplies the path and Host header, paired with tls: true to disguise the connection as HTTPS. If the ws path, Host, or TLS setting doesn't match the server on any one of those three, the handshake fails.

Trojan

Trojan is designed to look like ordinary HTTPS: password handles authentication, and sni sets the domain declared during the TLS handshake (it defaults to the server value if omitted). skip-cert-verify: true skips certificate validation — this should only be used temporarily when you know for certain the server uses a self-signed certificate; leaving it on long-term means giving up any defense against man-in-the-middle attacks.

mihomo's Extended Protocols

The mihomo (Clash Meta) core adds types beyond the classic set, including vless (with reality transport), hysteria2, and tuic, all following the same "common four fields plus protocol-specific section" structure. If a subscription includes these node types, it has to be loaded with a mihomo-based client (Clash Plus, Clash Verge Rev, and FlClash, all listed on the Downloads page, all qualify) — the classic core will refuse to load the entire config once it hits a type it doesn't recognize. A full comparison of the two cores is in "mihomo vs. Classic Clash Core: Feature Comparison."

config.yaml · proxies Section
proxies:
  - name: "SS-HongKong"
    type: ss
    server: hk.example.com
    port: 8388
    cipher: aes-128-gcm
    password: "your-password"
    udp: true

  - name: "VMess-Japan"
    type: vmess
    server: jp.example.com
    port: 443
    uuid: 00000000-0000-0000-0000-000000000000
    alterId: 0
    cipher: auto
    tls: true
    network: ws
    ws-opts:
      path: /ws
      headers:
        Host: jp.example.com

  - name: "Trojan-Singapore"
    type: trojan
    server: sg.example.com
    port: 443
    password: "your-password"
    sni: sg.example.com
    udp: true

skip-cert-verify: true isn't a "flip it on if things don't work" switch. It only fixes certificate validation failures specifically; turning it on for anything else does nothing useful and exposes all of that node's traffic to a potential man-in-the-middle.

5. Proxy Group Fields: The Five proxy-groups Types

Proxy groups sit above individual nodes as a scheduling layer: rules don't point directly at a node, they point at a group, and the group's type decides which node actually gets used. Group members go in the proxies array and can be node names, other group names, or one of two built-in targets — DIRECT (bypass the proxy) and REJECT (refuse the connection, commonly used to block a domain). Nesting groups inside groups is normal — for example, putting an "auto speedtest" group inside a "manual select" group gets you both manual override and automatic selection.

typeDecision BehaviorTypical Use
selectFully manual — whichever node the user clicks in the UIThe top-level master switch group
url-testPeriodically tests latency and auto-locks onto the fastest nodeEveryday automatic selection
fallbackUses the first available node in list order, failing over automaticallyPrimary/backup failover
load-balanceSpreads connections across multiple nodesAvoiding a single node's rate limits
relayChains nodes in sequence — traffic passes through each one in turnMulti-hop chains; latency stacks up, use sparingly

Auto-selecting group types rely on three parameters: url is the health-check endpoint, typically a lightweight one that returns 204; interval is the test period in seconds — 300 is a common value, and setting it too low generates a lot of probing traffic; tolerance (url-test only) is the switching margin in milliseconds — a new node only takes over once its latency beats the current one by more than this margin, which stops two similarly-fast nodes from flapping back and forth. lazy: true keeps a group from running tests until a rule actually routes traffic to it, which noticeably cuts background requests when there are a lot of nodes. load-balance picks a distribution strategy via strategy: consistent-hashing pins a given destination site to the same node every time, which plays nicely with login sessions, while round-robin rotates through nodes in turn.

config.yaml · proxy-groups Section
proxy-groups:
  - name: "Manual Select"
    type: select
    proxies:
      - Auto Speedtest
      - SS-HongKong
      - VMess-Japan
      - Trojan-Singapore
      - DIRECT

  - name: "Auto Speedtest"
    type: url-test
    url: http://www.gstatic.com/generate_204
    interval: 300
    tolerance: 50
    lazy: true
    proxies:
      - SS-HongKong
      - VMess-Japan
      - Trojan-Singapore

  - name: "Fallback"
    type: fallback
    url: http://www.gstatic.com/generate_204
    interval: 300
    proxies:
      - SS-HongKong
      - VMess-Japan

The "proxy not found" error almost always comes down to a naming mismatch: a group references a name that's off by a stray space, a quote mark, or a full-width character compared to the name in the proxies section, and loading fails outright. Whenever a node gets renamed, search the whole file for the old name and update every reference at once.

6. Providers: Subscription Nodes and External Rule Sets

proxy-providers: Externalizing Where Nodes Come From

Writing nodes directly under proxies has a downside: every subscription update overwrites the whole file, wiping out any local edits along with it. proxy-providers pulls the node source out into its own block: type: http pulls the subscription at url on a period set by interval (seconds) and caches it to path; type: file reads a local file instead. The health-check sub-section gives that batch of nodes its own liveness check. Proxy groups reference a provider through the use field, which injects every node in that provider into the group automatically, and this can coexist with a plain proxies array.

rule-providers: Three behavior Types for Rule Sets

rule-providers applies the same idea to rules, keeping the rules section short by handing large domain/IP lists off to an external set. The key field behavior takes three values that determine how the set's contents are interpreted: domain (a plain domain list), ipcidr (a plain CIDR list), and classical (each line is a full Clash rule). format declares the file format (yaml or text); when behavior doesn't match what's actually in the set, the whole group of rules fails silently — this is the number-one reason a "RULE-SET" that's clearly written just doesn't take effect.

config.yaml · providers Section
proxy-providers:
  main:
    type: http
    url: "https://example.com/sub.yaml"
    path: ./providers/main.yaml
    interval: 86400
    health-check:
      enable: true
      url: http://www.gstatic.com/generate_204
      interval: 600

rule-providers:
  cn-domains:
    type: http
    behavior: domain
    format: yaml
    url: "https://example.com/cn-domains.yaml"
    path: ./rules/cn-domains.yaml
    interval: 86400

proxy-groups:
  - name: "Subscription Nodes"
    type: select
    use:
      - main

Subscriptions come in several distinct formats — full YAML, Base64-encoded node lists, generic share links — and not all of them can be fed directly into a provider. Format differences and how to convert between them are covered in "Clash Subscription Formats Explained," and the steps for importing a subscription in the client are in the User Guide. When an update fails, check first whether the path cache file was actually written and whether its timestamp changed, then check the HTTP status code the provider fetch logged — that pinpoints the problem far faster than blind retries.

7. Rule Syntax: Top-Down, First Match Wins

Every rule is a comma-separated triple: type,match value,policy, where the policy can be a node name, a group name, DIRECT, or REJECT. The matching engine scans top to bottom, and the first match wins — nothing below it is even checked — so order is priority: precise rules (DOMAIN, PROCESS-NAME) go first, broad rules (GEOIP) go later, and MATCH must be the very last rule, with its match value left blank.

Rule TypeMatchesExampleNotes
DOMAINExact domain matchDOMAIN,ad.example.com,REJECTExcludes subdomains
DOMAIN-SUFFIXDomain suffixDOMAIN-SUFFIX,github.com,Manual SelectCovers the domain itself and every subdomain
DOMAIN-KEYWORDDomain contains a keywordDOMAIN-KEYWORD,google,Manual SelectBroad scope, prone to false positives — use sparingly
GEOIPThe target IP's regionGEOIP,CN,DIRECTDepends on the GeoIP database
IP-CIDR / IP-CIDR6Target IP rangeIP-CIDR,192.168.0.0/16,DIRECT,no-resolveSeparate types for v4 and v6
SRC-IP-CIDRSource IP rangeSRC-IP-CIDR,192.168.1.50/32,DIRECTPer-device routing when allow-lan is on
DST-PORTDestination portDST-PORT,22,DIRECTRough traffic classification by port
PROCESS-NAMEThe originating process namePROCESS-NAME,Telegram.exe,Manual SelectDesktop only — per-app routing
RULE-SETReferences a rule-providerRULE-SET,cn-domains,DIRECTName must match the provider key
MATCHUnconditional catch-allMATCH,Manual SelectMust be, and only be, the last rule

no-resolve is an optional fourth field on IP-based rules, meaning "if the target is still a domain and hasn't been resolved yet, skip this rule rather than triggering a DNS lookup just to test the match." Adding no-resolve consistently to rules covering LAN and reserved address ranges avoids a lot of pointless resolution requests. The mihomo core also offers a GEOSITE type that references a domain database maintained by category (such as GEOSITE,category-ads-all,REJECT), sitting between hand-written rules and a RULE-SET in granularity.

config.yaml · rules Section
rules:
  - PROCESS-NAME,Telegram.exe,Manual Select
  - DOMAIN,ad.example.com,REJECT
  - DOMAIN-SUFFIX,github.com,Manual Select
  - RULE-SET,cn-domains,DIRECT
  - IP-CIDR,192.168.0.0/16,DIRECT,no-resolve
  - IP-CIDR,10.0.0.0/8,DIRECT,no-resolve
  - GEOIP,CN,DIRECT
  - MATCH,Manual Select

To check exactly which rule a site is hitting, temporarily set log-level to debug and visit the site once — the log will print the matched rule and outbound node for every connection. That's far more reliable than guessing against the rule table. Look up unfamiliar terms (GeoIP, CIDR, proxy groups) anytime in the Glossary.

8. TUN Mode: A Virtual Adapter Takes Over System-Wide Traffic

The system proxy only affects applications that respect proxy settings — command-line tools, game clients, and some desktop software send packets directly, bypassing it entirely. TUN mode solves this by creating a virtual network adapter that intercepts the entire device's outbound traffic at the network layer and hands it to the core — apps are none the wiser, and rules apply as usual. The trade-off is needing elevated system permissions and taking over DNS more aggressively.

config.yaml · tun Section
tun:
  enable: true
  stack: system
  auto-route: true
  auto-detect-interface: true
  dns-hijack:
    - any:53

Field by field: stack selects the protocol stack implementation — system uses the OS's native stack for good throughput; gvisor is a userspace stack kept around for compatibility edge cases; mixed blends the two. auto-route writes routing table entries automatically, pointing the default route at the virtual adapter — turn it off and you have to configure routes by hand, so leave it true in general. auto-detect-interface automatically identifies the real physical uplink interface, preventing the core's own outbound traffic from looping back through the virtual adapter it just created — when TUN kills all connectivity outright, this is the step that broke nine times out of ten. dns-hijack intercepts queries sent to any address on port 53 and redirects them to the built-in DNS module, keeping the fake-ip system intact under TUN.

PlatformImplementationPermission Notes
Windowswintun virtual adapter driverThe client must run as administrator, or its service component must be authorized
macOSSystem Network Extension / utun deviceFirst-time use requires approving the network extension in System Settings
AndroidVpnService APIThe client just requests VPN permission — no root needed
Linux/dev/net/tun deviceRequires root or the CAP_NET_ADMIN capability

macOS has the trickiest authorization flow of the bunch: the network extension approval screen is buried deep in the settings, and mishandled Keychain prompts keep reappearing — step-by-step screenshots are in "Clash on macOS Blocked by Permissions? Full Steps for Network Extension and Keychain Authorization." The mihomo core also offers a sniffer domain-sniffing section that reconstructs the target domain from the TLS handshake under TUN, closing the gap where a rule can't match because only an IP, not a domain, is visible — advanced users can turn it on as needed.

TUN and the system proxy aren't an either-or choice: the system proxy is enough for everyday browsing, and TUN can be switched on only when command-line tools or game traffic need capturing. Most clients turn TUN into a single toggle, and the fields in this chapter are exactly what gets written underneath it.

9. Overrides and Merging: Making Edits Survive Subscription Updates

Editing the config.yaml a subscription generates is the most common maintenance trap: the next subscription refresh overwrites the whole file with the server-side version, and every hand-added rule or edited proxy group resets to zero. The right approach is to keep "the subscription as delivered" and "your local edits" as separate layers that the client merges at load time.

YAML Anchors: Deduplication Within a File

Start with YAML's own reuse syntax. &name defines an anchor, *name references it, and <<: is the merge key that expands an entire mapping in place. When several proxy groups share the same health-check settings, anchors can collapse the duplication into one place:

config.yaml · Anchor Reuse
check-common: &check
  url: http://www.gstatic.com/generate_204
  interval: 300

proxy-groups:
  - name: "Auto Speedtest"
    type: url-test
    <<: *check
    tolerance: 50
    proxies:
      - SS-HongKong
      - VMess-Japan

  - name: "Fallback"
    type: fallback
    <<: *check
    proxies:
      - SS-HongKong
      - VMess-Japan

Anchors only work within a single file — they solve duplication, not overwriting. Merging across files depends on the client's own override mechanism.

Client-Side Overrides: A Layer on Top of the Subscription

Most mainstream clients offer an override entry point built around the same idea: the subscription stays read-only, your edits live in a separate override layer, and every load pulls the subscription first and then applies the override on top, so refreshing the subscription never wipes out your changes. Clash Verge Rev, for example, offers Merge (declarative merging, typically using prepend-rules / append-rules semantics to insert custom rules before or after the subscription's own rules) and Script (arbitrary scripted transformation of the config object); Clash Plus and other clients have their own override editors, with the steps documented in the corresponding section of the User Guide. Declarative merging covers roughly nine out of ten needs and is hard to break, so reach for it first; save the scripting approach for more complex work, like bulk-filtering nodes by name or dynamically rewriting groups.

merge.yaml · Override Snippet Example
prepend-rules:
  - DOMAIN-SUFFIX,internal.example.com,DIRECT
  - PROCESS-NAME,ssh,DIRECT

append-rules:
  - GEOIP,CN,DIRECT

The maintenance structure worth settling into long-term has three layers: the subscription supplies nodes (via a proxy-provider or subscription link), rule sets are maintained remotely through rule-providers, and every personal tweak goes into the override layer. Updating any one layer leaves the other two untouched, and the config file turns from a one-off artifact into something maintainable over time. Get this far, and every field covered in the first eight chapters finally has a proper home.

Don't rush to reload after editing: most clients offer a config validation entry point, and the mihomo core also supports checking syntax from the command line with mihomo -t -f config.yaml. Validate first, then hot-reload — it saves you from the "one bad line, the whole household loses internet" scenario.

Where to Go Next

With the fields covered, get back to the practical side: install the client, import a subscription, verify routing.