CONFIG REFERENCE · MIHOMO CORE

Clash Config File Reference

This is the most detailed reference on the site: a section-by-section breakdown of the Clash (mihomo core) config file's YAML structure, general fields, proxy nodes, proxy groups, rule syntax, and override merging, each with a ready-to-use config.yaml example. If you just want to get a client running quickly, start with the three-step walkthrough on the setup guide; once it's working and you need to change ports, add rules, or split traffic, come back here to look up specific fields.

YAML Structure Overview

Where the Config File Lives

GUI clients usually shield you from the raw file: Clash Verge Rev manages configs on its "Profiles" page, Clash Plus keeps subscriptions and local configs inside the app, and both store the actual files in their app data folders, with an "Open config folder" option somewhere in the UI. The mihomo core itself (and legacy clients like Clash for Windows) reads a single YAML file, named config.yaml by default, with the -d flag pointing to the folder it lives in. Whichever path you take, the file that ends up running has the same structure: a block of top-level scalar fields (port, mode, log level), followed by four functional sections — dns, proxies, proxy-groups, rules — plus two external-resource sections, proxy-providers and rule-providers. Once you understand this skeleton, you understand the config logic shared by every Clash-family client.

Four Hard Rules of YAML Syntax

YAML has very few syntax rules, but each one can make or break parsing:

  1. Indentation must use spaces only, never tabs. This is the number one cause of parse errors for newcomers — set your editor to convert tabs to two spaces and you'll never think about it again.
  2. A colon between key and value needs exactly one space after it. Writing port:7890 or port : 7890 will either fail to parse or produce unexpected results.
  3. List items start with "- ", also followed by a single space. Lists can be written at the same indent level as the parent key, or indented one level further — both are valid, just stay consistent throughout the file.
  4. If a string contains a colon, #, curly braces, square brackets, or commas, wrap the whole thing in double quotes. Anything after a # is a comment and gets ignored during parsing.

YAML also supports anchors and references (& defines an anchor, * references it, << merges it in), which lets you avoid repeating shared parameters across multiple nodes — see the "Proxy Node Fields" section for a concrete example.

Top-Level Fields at a Glance

A complete config's top level roughly splits into three groups: network listeners (port, socks-port, mixed-port, etc.), runtime behavior (mode, log-level, ipv6, external-controller, etc.), and functional sections (dns, proxies, proxy-groups, rules). The table below gives the big picture; each section is expanded further down.

FieldTypePurpose
portIntegerHTTP proxy listening port
socks-portIntegerSOCKS5 proxy listening port
mixed-portIntegerCombined HTTP + SOCKS port
allow-lanBooleanWhether LAN devices can connect
modeEnumRuntime mode: rule / global / direct
log-levelEnumsilent / error / warning / info / debug
external-controllerStringRESTful control API listen address
dnsSectionBuilt-in DNS server and resolution behavior
proxiesListList of proxy nodes
proxy-groupsListList of proxy groups
rulesListTraffic rules, matched in order
rule-providersMapExternal rule set definitions
# config.yaml skeleton: scalars first, then sections
mixed-port: 7890
allow-lan: false
mode: rule
log-level: info
external-controller: 127.0.0.1:9090

dns:
  enable: true
  # ...see "DNS Fields" section for details

proxies:
  - name: "node-a"
    # ...see "Proxy Node Fields" section for details

proxy-groups:
  - name: "PROXY"
    type: select
    proxies: ["node-a", DIRECT]

rules:
  - DOMAIN-SUFFIX,local,DIRECT
  - MATCH,PROXY

Quick Note

When YAML parsing fails, the core refuses to start; GUI clients usually show "config load failed" or an empty proxy list. The line number in the error is generally trustworthy — check the indentation, quotes, and colons around that line first.

General Fields: Ports, Mode & Global Behavior

Port Fields

Clash exposes four kinds of inbound listeners. port is the classic HTTP proxy port; socks-port is a SOCKS5 port that supports UDP forwarding; mixed-port combines both protocols on one port, with the core auto-detecting the request type — most GUI clients today default to only opening the mixed port. redir-port and tproxy-port are for Linux transparent proxying and aren't relevant on desktop. If startup fails with "bind: address already in use," that port is taken — switch ports, or find and stop whatever process is holding it. For the mixed port's real-world use in LAN sharing, see the deep dive in this technical note.

FieldProtocolCommon ValuesNotes
portHTTP7890Legacy client default, being phased out by mixed port
socks-portSOCKS57891Supports UDP forwarding, common for gaming/voice apps
mixed-portHTTP + SOCKS57890 / 7897Current mainstream default, one port for both protocols
redir-portTransparent proxy (redirect)No defaultLinux gateway/router setups
tproxy-portTransparent proxy (tproxy)No defaultLinux gateway setups, supports UDP

Runtime Mode

mode controls how requests are routed, with three values: rule matches the rules list in order and is the default for everyday use; global sends all traffic through the GLOBAL proxy group (usually the node you've picked in the panel) — switching to it is the quickest way to check "is this a rule problem?"; direct sends everything unproxied, useful for confirming the proxy itself is working. The mode toggle on a GUI client's home screen changes exactly this field.

LAN & Remote Access

With allow-lan enabled, phones and tablets on the same network can point their proxy settings at this computer's IP and port and share the same outbound proxy; bind-address controls which network interface to listen on, defaulting to "*" for all interfaces — this only matters when paired with allow-lan. external-controller is the core's RESTful control API, which GUI panels use to read status, switch nodes, and pull logs; secret is the access token for that API. If you change this API's listen address to 0.0.0.0 to expose it externally, you must also set a secret — otherwise you're handing control of your proxy to the entire network. external-ui points to a static panel directory, letting you access a management UI directly from a browser.

Security Note

Only enable allow-lan on trusted networks. Opening a proxy port on public Wi-Fi means lending your bandwidth and exit node to strangers on the same network. For the full setup and verification steps for LAN sharing, see "Clash Mixed Port and LAN Proxy Sharing."

Logging, Latency & Other Behavior Fields

log-level has five tiers from silent to debug — bump it to debug temporarily when troubleshooting, and leave it at info the rest of the time. ipv6 controls whether AAAA records are handled; turn it off if your network has no IPv6 route. unified-delay makes latency tests include TCP/TLS handshake time, so numbers across nodes are actually comparable; tcp-concurrent lets candidate nodes connect in parallel for a faster first hop; find-process-mode controls whether local process names get resolved, which the PROCESS-NAME rule depends on; global-client-fingerprint sets a TLS client fingerprint to mimic (commonly chrome), relevant for protocols relying on uTLS; and store-selected under the profile section remembers your manual node picks in the panel so they survive a config reload. There's also a tun section for virtual-adapter-level traffic capture — an advanced topic that isn't needed for typical desktop use.

port: 7890
socks-port: 7891
mixed-port: 7897
allow-lan: false
bind-address: "*"
mode: rule
log-level: info
ipv6: false
external-controller: 127.0.0.1:9090
secret: ""
unified-delay: true
tcp-concurrent: true
find-process-mode: strict
global-client-fingerprint: chrome
profile:
  store-selected: true
  store-fake-ip: true

DNS Fields: Blocking Poisoning & Leaks

Why DNS Gets Its Own Section

The proxy only controls where traffic goes — DNS decides what a domain resolves to in the first place. By default, system DNS goes over plaintext UDP port 53 through your ISP: it can be hijacked, and it exposes what you're accessing to the local network, which is exactly what's meant by a DNS leak. Clash's dns section runs a built-in DNS server inside the core: it intercepts queries and routes different domains to different upstreams. Combined with fake-ip mode, it can even match rules by domain name first and decide whether to proxy afterward, sidestepping the chicken-and-egg problem of "the IP is already poisoned before the domain-based rule ever gets a chance to run."

Basic Fields

enable is the master switch; listen sets the built-in DNS server's listen address, which you can skip if the GUI client isn't doing system-level DNS takeover. enhanced-mode has two values: redir-host returns real resolution results, with the core tracking the domain-to-IP mapping itself; fake-ip returns a fake address in the 198.18.0.0/16 range immediately, then reverse-looks-up the domain once a connection actually comes in to match rules — it's faster and immune to poisoning, and is the mainstream choice today. fake-ip-filter lists domains that skip fake-ip — LAN device names, NTP time sync, and other services that need a real IP get resolved normally. default-nameserver only resolves the domain names of your upstream DNS servers themselves, so it must be plain-IP traditional UDP servers, or you end up needing DoH to resolve the DoH server's own domain — a circular dependency.

Upstream Servers

nameserver is your main upstream list; you can list several and the fastest response wins. fallback is a backup group used with fallback-filter based on GEOIP: if the resolved IP falls in a range outside your home region, it re-queries using fallback instead. mihomo recommends nameserver-policy instead: assign upstreams directly by domain-set, e.g. route geosite:cn through domestic DoH and everything else through a trusted DoH server outside — a more deterministic approach than fallback's query-then-check logic. proxy-server-nameserver is dedicated to resolving your proxy nodes' own domains, preventing a poisoned node domain from making the server unreachable. Turning on respect-rules makes DNS queries themselves follow your routing rules (e.g. querying a foreign DNS server through the proxy), at the cost of more configuration complexity. There's also a sniffer section that extracts domain names from TLS/HTTP handshakes, fixing rule matching for apps that connect directly by IP.

SyntaxProtocolNotes
223.5.5.5UDP 53Traditional plaintext queries, only suitable for default-nameserver
tls://dns.alidns.comDNS over TLSEncrypted transport over port 853
https://doh.pub/dns-queryDNS over HTTPSOver HTTPS, hardest to interfere with
quic://dns.exampleDNS over QUICSupported by mihomo, low latency
dhcp://en0DHCP-assignedUses the ISP DNS handed out by the network interface
dns:
  enable: true
  listen: 0.0.0.0:1053
  ipv6: false
  enhanced-mode: fake-ip
  fake-ip-range: 198.18.0.1/16
  fake-ip-filter:
    - "*.lan"
    - "*.local"
    - time.*.com
    - ntp.*.com
  default-nameserver:
    - 223.5.5.5
    - 119.29.29.29
  nameserver:
    - https://doh.pub/dns-query
    - https://dns.alidns.com/dns-query
  proxy-server-nameserver:
    - https://doh.pub/dns-query
  nameserver-policy:
    "geosite:geolocation-!cn":
      - https://dns.cloudflare.com/dns-query
      - https://dns.google/dns-query

Troubleshooting Path

The classic symptom of a DNS leak: your browser is behind the proxy, but a DNS leak test site still shows your local ISP. Check in this order: dns.enable is on, your system or browser's DNS points at the core's listen address, and nameserver doesn't contain any ISP UDP servers. More symptom-to-fix mappings are on the FAQ page.

Proxy Node Fields: the proxies List

Common Fields

proxies is a list, with each entry describing one node. Only five fields are shared across every protocol: name (the display name, referenced by rules and proxy groups — must be unique across the whole file), type (protocol), server, port, and udp (whether to forward UDP — turn it on if the node supports it). Every other field depends on the protocol. When maintaining multiple nodes of the same protocol by hand, you can pull shared settings into a YAML anchor and have later nodes reference it with a merge key, writing only the fields that differ:

proxies:
  - &ss-base
    name: "ss-a"
    type: ss
    server: 203.0.113.10
    port: 8388
    cipher: aes-128-gcm
    password: "your-password"
    udp: true
  - <<: *ss-base
    name: "ss-b"
    server: 203.0.113.11
    port: 8389

Examples per Protocol

The examples below cover the five protocols most commonly used in mihomo. All addresses use documentation-reserved ranges — swap in your real server details to use them:

proxies:
  - name: "ss-example"
    type: ss
    server: 203.0.113.10
    port: 8388
    cipher: aes-128-gcm
    password: "your-password"
    udp: true

  - name: "vmess-example"
    type: vmess
    server: 198.51.100.20
    port: 443
    uuid: 11111111-2222-3333-4444-555555555555
    alterId: 0
    cipher: auto
    tls: true
    servername: example.com
    network: ws
    ws-opts:
      path: /ray
      headers:
        Host: example.com

  - name: "trojan-example"
    type: trojan
    server: 192.0.2.30
    port: 443
    password: "your-password"
    sni: example.com
    skip-cert-verify: false
    udp: true

  - name: "hy2-example"
    type: hysteria2
    server: 203.0.113.40
    port: 443
    password: "your-password"
    sni: example.com
    skip-cert-verify: false

  - name: "vless-example"
    type: vless
    server: 198.51.100.50
    port: 443
    uuid: 11111111-2222-3333-4444-555555555555
    network: tcp
    tls: true
    udp: true
    flow: xtls-rprx-vision
    servername: www.microsoft.com
    reality-opts:
      public-key: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
      short-id: "0123456789abcdef"
    client-fingerprint: chrome

Only turn on skip-cert-verify temporarily if you have a specific, confirmed reason to bypass certificate chain validation — leaving it on long-term means giving up TLS identity verification entirely. sni / servername sets the domain declared during the TLS handshake; get it wrong and the connection simply won't go through.

Protocol Support Differences

Legacy Clash is no longer maintained, and new protocols only land in the mihomo core. That's also why every download recommendation on this site leads with mihomo-based clients:

ProtocolLegacy Clash (discontinued)mihomo
ShadowsocksSupportedSupported
VMessSupportedSupported
TrojanSupportedSupported
SnellSupportedSupported
Hysteria / Hysteria2Not supportedSupported
TUICNot supportedSupported
VLESS / RealityNot supportedSupported
WireGuardNot supportedSupported

Note for Subscription Users

After importing a subscription, the client auto-generates the proxies list, so you rarely need to write this section by hand. Editing nodes directly inside a subscription file gets wiped the next time it updates — if you want to keep custom nodes long-term, use the append method described in the "Override & Merge" section instead.

Proxy Group Fields: proxy-groups

Five Types

A proxy group is a container of nodes paired with a selection strategy. select is manual selection — click one in the panel and traffic follows it; almost every config has at least one main entry group like this. url-test tests latency across all nodes in the group at a fixed interval and auto-picks the lowest one. fallback also tests latency, but its logic is "only switch when the current node goes down" — list order is priority order. load-balance spreads connections across multiple nodes (strategy is either consistent-hashing or round-robin), good for stacking bandwidth across multiple lines. relay chains several nodes together so traffic passes through each in sequence, exiting from the last one. mihomo also lets you add an icon field to a group for a custom icon in the panel.

TypeSelection MethodTypical Use
selectManual selectionMain entry point, feature toggles (like an ad-block switch)
url-testAuto-pick lowest latencyBest node among several in the same region
fallbackFailoverPrimary/backup line, stability first
load-balanceLoad balancingStacking bandwidth across multiple lines
relayChained relayMulti-hop paths that separate entry from exit node

Latency Test Parameters

url is the test target, conventionally http://www.gstatic.com/generate_204 since a 204 no-content response costs almost nothing; interval is the test interval in seconds — too short just wastes bandwidth and battery, 300 is common; tolerance is the switch threshold in milliseconds — a new node has to beat the current one by at least this much before switching, which avoids flip-flopping between two similarly fast lines; setting lazy to true only tests the group while it's actually in use, so idle backup groups don't burn traffic for nothing; timeout is the timeout for a single latency test.

Nesting & References

A proxy group's proxies list can reference the names of other proxy groups — this is nesting. A top-level PROXY group might contain three region groups — "Hong Kong," "Japan," "US" — each of which is a url-test group; you manually pick the region, and each region group auto-picks the fastest node inside it, with each layer doing its own job. The use field pulls in a whole batch of nodes from a proxy-providers subscription source; filter uses a regex to keep only matching node names, e.g. filter: "HK|Hong Kong" keeps only nodes with Hong Kong in the name. DIRECT and REJECT are two built-in exits that can be dropped straight into any proxies list, and combined with a select group they make handy feature toggles like an ad-block switch.

proxy-groups:
  - name: "PROXY"
    type: select
    proxies: ["HK-AUTO", "JP-AUTO", "US-AUTO", DIRECT]

  - name: "HK-AUTO"
    type: url-test
    use: ["provider-main"]
    filter: "HK|Hong Kong"
    url: http://www.gstatic.com/generate_204
    interval: 300
    tolerance: 50
    lazy: true

  - name: "JP-AUTO"
    type: url-test
    use: ["provider-main"]
    filter: "JP|Japan"
    url: http://www.gstatic.com/generate_204
    interval: 300

  - name: "US-AUTO"
    type: fallback
    use: ["provider-main"]
    filter: "US|United States"
    url: http://www.gstatic.com/generate_204
    interval: 300

  - name: "AD-BLOCK"
    type: select
    proxies: [REJECT, DIRECT]

About relay

If any single hop in a relay chain drops, the whole chain stops working, and latency stacks up with every extra hop. It solves a specific need — keeping entry and exit separate — rather than being a general speed trick; the first four group types cover ordinary use cases just fine.

Rule Syntax: rules from Top to Bottom

Match Order

rules is an ordered list: the core checks entries top to bottom and stops at the first match, ignoring everything below it. The hard rule of thumb is: specific rules go first, broad rules go later, and MATCH always sits at the very bottom. Rules split into two categories: domain-based rules (DOMAIN / DOMAIN-SUFFIX / DOMAIN-KEYWORD / GEOSITE) can be evaluated before resolution happens; IP-based rules (IP-CIDR / GEOIP) need a resolved address first. Adding no-resolve to an IP-based rule means "if the request is already a plain domain, don't trigger a DNS lookup just to check this rule" — pure domain requests skip that rule and keep going, avoiding a lot of pointless lookups. GEOSITE and GEOIP rely on geo data files; GUI clients keep these updated automatically, but if you're running the core bare, make sure the data files are in the config directory or that auto-update is enabled.

Rule Type Quick Reference

SyntaxMatchesNotes
DOMAIN,example.com,outboundA single domainExact match, no subdomains
DOMAIN-SUFFIX,example.com,outboundDomain suffixMatches the domain and all subdomains
DOMAIN-KEYWORD,google,outboundDomain keywordMatches on substring — use with care
GEOSITE,cn,outboundDomain category listCommunity-maintained category data
IP-CIDR,192.168.0.0/16,outboundIPv4 rangeAdd no-resolve when possible
IP-CIDR6,fe80::/10,outboundIPv6 rangeSame as above
GEOIP,CN,outboundIP geolocationAdd no-resolve when possible
SRC-IP-CIDR,192.168.1.201/32,outboundSource IPPer-device routing on your LAN
DST-PORT,443,outboundDestination portRoute by destination port
PROCESS-NAME,telegram.exe,outboundProcess nameRequires find-process-mode
RULE-SET,name,outboundExternal rule setReferences a rule-providers definition
MATCH,outboundCatch-allMust be the last rule

Rule Providers

Rule providers pull hundreds or thousands of rules out into an external file, leaving just one reference line in the main config. When type is http, it periodically fetches from url (every interval seconds) and caches it to path; when it's file, it reads a local file instead. behavior has three values: domain (a pure set of domain suffixes, loaded as a domain tree for the fastest matching), ipcidr (pure IP ranges), and classical (classic rule lines that can mix any rule type). The rule set file itself can be YAML (a payload list) or plain text with one rule per line.

rule-providers:
  reject-ads:
    type: http
    behavior: domain
    url: "https://example.org/rules/ads.yaml"
    path: ./ruleset/ads.yaml
    interval: 86400
  local-direct:
    type: file
    behavior: classical
    path: ./ruleset/direct.yaml

rules:
  - RULE-SET,reject-ads,AD-BLOCK
  - RULE-SET,local-direct,DIRECT
  - DOMAIN-SUFFIX,lan,DIRECT
  - GEOSITE,private,DIRECT
  - GEOSITE,cn,DIRECT
  - GEOIP,CN,DIRECT,no-resolve
  - GEOSITE,geolocation-!cn,PROXY
  - MATCH,PROXY

Always Add a Catch-All

Without a MATCH rule at the end, unmatched requests fall back to default behavior (which varies by core and version), which shows up as "some sites just won't load for no reason." Every config should end with a MATCH rule, usually pointing at your main entry proxy group.

Override & Merge: Subscriptions and Custom Config Together

Why You Shouldn't Edit the Subscription File Directly

A subscription is essentially a full config file pushed down by the provider, and the client re-fetches and overwrites it wholesale on a schedule. Any custom rules you add or proxy group edits you make directly in the subscription file vanish on the next update. The right approach is to split "subscription content" and "local customization" into two layers: the subscription handles nodes, your overrides handle your rules and fields, and the client merges the two into a final config at runtime. That way the subscription can update freely while your local layer stays untouched.

Client Override Mechanisms

Take Clash Verge Rev as an example: right-click a subscription entry to edit it, and you can maintain a global extended config (Merge) that applies across all subscriptions. Merge supports two kinds of keys: append keys with a prepend- / append- prefix, which insert content at the start or end of the matching list; and override keys sharing a name with a top-level field, which replace the subscription's value outright. Clash Plus offers a similar custom-config entry point outside the subscription itself. If you're still on Clash for Windows: that project is no longer maintained and its Mixin mechanism hasn't been updated in years — see "Migrating to Clash Verge Rev and Clash Plus" for a path to an actively maintained client.

# Global extended config (Merge) example
prepend-rules:
  - DOMAIN-SUFFIX,internal.example.com,DIRECT
append-proxy-groups:
  - name: "MY-SELECT"
    type: select
    use: ["provider-main"]
mixed-port: 7897

Three Approaches Compared

ApproachCharacteristicsBest For
Client override (Merge / custom config)No external dependencies, travels with the clientEveryday rule tweaks, port changes, extra proxy groups — recommended
Local template + subscription conversionGenerates a complete config in one passMerging multiple subscriptions, fine-grained template management
Self-hosted subconverterFull control, shareable across multiple usersUsers with their own server, advanced setups

Recommended Approach

Most users only need the client's built-in override, which covers the vast majority of customization needs — adding rules, changing ports, appending proxy groups. For subscription format differences and conversion methods, see the technical note "What Format Are Clash Subscription Links?"; grab the client itself from the download page, where Clash Plus is the top pick across all platforms.

Validation, Debugging & Common Errors

Static Checking with the Core

mihomo has a built-in config check: put config.yaml in a folder and run ./mihomo -t -d ./folder — it prints a success message if the check passes, or the field name and line number if it fails. For big changes, run -t from the command line first, then let the GUI load it — much faster than repeatedly restarting the client to find out what broke. Windows users can put the core executable and config in the same folder and run the same command from PowerShell.

Hot Reload & Logs

With external-controller enabled, you can reload the config without restarting the core:

curl -X PUT "http://127.0.0.1:9090/configs?force=true" \
  -H "Content-Type: application/json" \
  -d '{"path": "/absolute/path/config.yaml"}'

force=true forcibly disconnects and reconnects existing connections — you can usually skip it for a routine reload. On the logging side, bumping log-level to debug prints the matching process for every connection (domain, matched rule, exit node), which is your first tool for figuring out "why didn't this traffic go through the proxy?" A GUI client's logs / connections panel is essentially a visual layer over this same data.

Common Errors at a Glance

Symptom / ErrorLikely CauseFix
yaml: line N: did not find expected keyInconsistent indentation or tabs mixed inStandardize on 2-space indentation
cannot unmarshalWrong field type (e.g. port written as a string)Check the type against the tables on this page
Config loads fine but every node times outSubscription is outdated, or node domains are poisonedUpdate the subscription; set proxy-server-nameserver
Some sites won't loadNo rule matched and there's no MATCH catch-allAdd a MATCH rule at the end
Something breaks under fake-ipThat domain needs a real, resolved IPAdd it to fake-ip-filter
Panel can't connect to the coreWrong external-controller address or secretDouble-check the listen port and token

Don't Paste Your Config Into Online Tools

When troubleshooting, don't paste your whole config into an online YAML validator — it contains server addresses, ports, and passwords. A local editor's YAML plugin (VS Code, for instance) is enough for syntax checking, and the core's -t flag is enough for semantic checking.

That covers the full path from skeleton to fields, from rules to overrides. For more specific everyday issues — can't connect, slow speeds, an app not going through the proxy — the FAQ page organizes fixes by symptom; if you haven't installed a client yet, the download page lists actively maintained options by platform; and if you'd rather get the minimum setup running first and look up fields later, start with the setup guide.

Download Clash