CONFIG REFERENCE

Clash Configuration File YAML Guide

Everything Clash and its actively maintained fork mihomo do is driven by a single YAML config: which ports to listen on, how domains get resolved, and which rule sends traffic to which node — the answers all live in this one file. This page walks through the file top to bottom, section by section, explaining what each field means and how to write it, with a usable example for every part. Think of it as a "reference manual," not a beginner tutorial.

How this page fits with the rest of the site: if you haven't made your first connection yet, start with the quick-start tutorial to complete the "import subscription → pick a mode → verify connectivity" flow, then come back here for field-level detail. If you run into unfamiliar terms while reading, check the concept glossary anytime; client installers live on the Get the Client page, where Clash Plus is the top pick across all platforms. The fields on this page use mihomo (the Clash Meta kernel) as the baseline — every mainstream GUI client listed on the download page is built on this kernel — and any field unsupported by classic Clash is called out separately in the text.

Applicable kernels: Clash Classic / mihomo Chapters: 9 Last updated: July 2026

01Config Overview: YAML Syntax and Top-Level Structure

Clash configs are written in YAML. YAML expresses hierarchy through indentation, and while the rules are few, none of them are optional: indent with spaces only (two spaces per level is the convention), never mix in tabs; a colon separating key and value must be followed by a space; list items start with "dash + space"; anything after # to the end of the line is a comment. Strings usually don't need quotes, but when a value contains special characters like colons, hashes, or curly braces, or starts with a digit, *, or ~, wrap it in quotes — otherwise parsing may not go the way you expect. As a rule of thumb, always quote passwords, subscription URLs, and wildcard domains.

Nine times out of ten, a config that fails to load has an indentation problem: a stray tab on some line, a level indented two spaces too few or too many, or a missing space after a colon. The kernel's error message includes a line number — start there and work backward to your most recent edit instead of re-reading the whole file line by line.

Where the config file lives

GUI clients (Clash Plus, Clash Verge Rev, FlClash, Clash Nyanpasu, and others) manage their own config directories: once you import a subscription, it's saved as a profile, and you view or edit it through the client's built-in editor or override feature — you generally don't need to go hunting through the filesystem. If you're running the kernel directly (servers, routers), it reads config.yaml from the working directory by default; on Linux the conventional path is ~/.config/mihomo/config.yaml, or you can point to a specific file with -f and a working directory with -d.

Top-level blocks at a glance

A complete config is made up of several top-level blocks, each handling its own concern without overlapping the others. The table below is the map for the rest of this page, and the following chapters expand on each block in this order:

BlockResponsibilityRequired?
port / mixed-port etc.Which ports to listen on locally and what inbound types to acceptAt least one inbound port
mode / log-level etc.Working mode, log level, IPv6, and other global switchesBest declared explicitly
dnsHow domains resolve, fake-ip and upstream serversRequired when TUN is enabled
tunVirtual network adapter that takes over system trafficOptional
proxiesList of proxy nodes, describing each server and protocol parameterEither this or proxy-providers, or both
proxy-groupsProxy groups — organizing nodes into selectable, speed-testable setsEssential for any practical config
rulesRouting rules, deciding which group each connection goes throughRequired in rule mode
proxy-providers / rule-providersExternal node and rule sets, updated independently of the main configOptional

Minimal working example

The config below strips out every optional field and keeps just "one inbound port, one node, one proxy group, three rules." It's the minimal skeleton that passes syntax validation and actually works. Once you understand it, every following chapter is just adding flesh to this skeleton:

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

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

proxy-groups:
  - name: "Proxy Selector"
    type: select
    proxies:
      - "Example Node"
      - DIRECT

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

How the three blocks work together: proxies defines "what exits exist," proxy-groups organizes those exits into "how to choose one," and rules decides "which traffic goes to which group." A rule's target can be a group name, a node name directly, or the built-in DIRECT (bypass the proxy) or REJECT (block). For a more conversational, line-by-line breakdown of this skeleton, see the blog post Clash Configuration File Structure, Line by Line.

02General Fields: Ports, Mode, and Runtime Control

General fields sit at the very top of the config, outside any block, and determine "how the kernel behaves at runtime." Getting these wrong won't break your routing rules, but it can leave you unable to connect, connecting to the wrong port, or drowning in log output — worth going through one by one.

Inbound ports and LAN sharing

port listens for HTTP proxy traffic, socks-port listens for SOCKS5, and mixed-port accepts both protocols on the same port — modern configs essentially just write one line, mixed-port: 7890, and skip maintaining two separate ports. The Linux-only redir-port and tproxy-port are for transparent proxy setups; desktop users can ignore them. allow-lan controls whether other devices on the same LAN can connect: set it to true and phones or TVs on the same Wi-Fi can point their proxy settings at this machine; bind-address further restricts which network interface is being listened on, with the default "*" meaning all interfaces.

mode: three working modes

mode only accepts three values. rule matches traffic against the rules block entry by entry and is the only mode recommended for everyday use; global sends all traffic through the same exit and is meant for temporarily testing a single node; direct bypasses the proxy entirely, effectively keeping the listening ports open while doing no proxying at all. All three modes have corresponding toggles in client UIs, matching this field exactly — for guidance on which to pick in practice, see step two of the tutorial page.

Logging, external control, and state persistence

log-level ranges from least to most verbose: silent, error, warning, info, debug. Use info day to day; switch to debug temporarily when troubleshooting routing issues to see exactly which rule each connection matched, then switch it back — log files grow quickly otherwise. external-controller enables the RESTful API (conventionally listening on 127.0.0.1:9090), which is how web dashboards and third-party tools read status and switch nodes; secret is the access token for that API, and it must be set if the listening address is exposed beyond localhost. The profile block controls two kinds of persistence: store-selected remembers each group's manually chosen node across restarts, and store-fake-ip persists the fake-ip mapping table to disk, reducing resolution hiccups after a restart.

config.yaml · General Fields
mixed-port: 7890
allow-lan: false
bind-address: "*"
mode: rule
log-level: info
ipv6: false
external-controller: 127.0.0.1:9090
secret: "your-secret"
profile:
  store-selected: true
  store-fake-ip: true

ipv6: false is the safe default: local IPv6 quality varies widely, and enabling it carelessly can cause some sites to resolve to a v6 address and then time out. Flip it to true only once you've confirmed IPv6 works cleanly on both your network and your node.

Turning on allow-lan: true opens your proxy port to the entire local network. On networks you don't fully trust — dorms, shared offices — you should also configure authentication (a list of usernames and passwords) or tighten bind-address to a specific interface.

03The dns Block: Domain Resolution and fake-ip

A large share of routing rules match on domain name, and domain resolution itself can be tampered with or hijacked — if the kernel simply trusts whatever the system DNS returns, both rule matching and the actual connection get thrown off. The dns block lets the kernel handle resolution itself: which upstream to use, whether to go over an encrypted channel, whether to enable fake-ip — it's all declared here. This block must be enabled whenever TUN mode is on, otherwise hijacked DNS queries have nowhere to go.

enhanced-mode: fake-ip vs. redir-host

fake-ip is the mainstream mode today: when an app queries a domain, the kernel immediately hands back a fake address from fake-ip-range (conventionally 198.18.0.1/16) while recording the "fake address ↔ domain" mapping; when the app then connects to that fake address, the kernel looks up the mapping to recover the domain, matches it against rules, and performs the real resolution. The upside is skipping an extra round of upfront resolution and letting rules always match precisely on domain; the downside is that some programs get confused seeing a 198.18.x address — LAN device discovery, NTP time sync, and some game platforms are classic cases. This is exactly what fake-ip-filter is for: domains listed there skip fake-ip and get the real address directly. redir-host mode always returns the real IP, offering the best compatibility at the cost of rule-matching precision and performance — fall back to it only after confirming fake-ip is causing actual problems.

The three tiers of upstream servers

default-nameserver is the bootstrap tier, responsible only for resolving the domains of your DoH servers themselves, so it must be a plain IP; nameserver is the main tier that handles everyday queries — DoH (starting with https://) or DoT (starting with tls://) addresses are recommended; fallback is the backup tier, working together with fallback-filter — a typical policy is geoip: true plus geoip-code: CN: if the main tier's result lands in a mainland China IP range, trust it; otherwise use the fallback result instead, countering DNS tampering. There's also a precision tool, nameserver-policy, that lets you assign a dedicated upstream to specific domains — for example, routing your company's internal domains to an internal DNS server.

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

fake-ip-filter supports two wildcards: * matches a single level, and + matches any number of levels (+.local covers a.b.local too). After changing fake-ip related fields, restart the kernel and clear the client's fake-ip cache so old mappings don't linger. There's also an easily overlooked causal chain between TUN-mode DNS handling and browser HTTPS certificate errors — see the breakdown in the blog post Browser Shows an HTTPS Certificate Error After Enabling a Proxy?.

04The tun Block: Virtual Adapter and System-Level Takeover

A regular "system proxy" only registers an HTTP/SOCKS port with the operating system — whether an app actually uses it is entirely up to that app. Command-line tools, some game clients, and plenty of background services never honor that setting. TUN mode takes a different approach: it creates a virtual network adapter and points the default route at it, intercepting all traffic at the network layer with no way for apps to route around it. The trade-off is needing higher system privileges, and it runs as a separate mechanism alongside the system proxy, not in place of it.

Key fields

stack picks the protocol stack implementation: system uses the OS network stack, offers the best performance, and is the default recommendation on desktop platforms; gvisor is a userspace implementation kept around for compatibility edge cases; mixed splits the difference. auto-route automatically rewrites the routing table to point the default route at the virtual adapter — this should almost always be on; auto-detect-interface automatically identifies the real physical uplink interface, preventing traffic from looping back through the virtual adapter. dns-hijack declares which DNS targets to intercept; setting any:53 catches every query headed to port 53 and hands it to the kernel's dns block to answer — which is exactly why "TUN requires dns" holds true. strict-route tightens routing further to prevent bypass, but can occasionally break connectivity in certain VM and container environments — turn it off if that happens.

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

Permissions and differences by platform

PlatformPermission requiredThings to watch for
WindowsClient needs to run as administrator, or install the system service the client providesFirst activation installs a virtual adapter driver — allow it if security software blocks it
macOSRequires a password prompt the first time it's enabledRemember to allow the network extension in System Settings for newly installed clients
Linuxroot, or grant the kernel binary network management capabilitiesCan interact with existing firewall rules (nftables/iptables)
AndroidNo root needed — the client achieves the same effect via VpnServiceShows up in the system as a VPN session; there's no tun block concept here

Use either TUN or the system proxy — never both at once: stacking the two mechanisms routes traffic through two layers of local forwarding, which at best makes troubleshooting harder and at worst creates a loop. After switching between them, restart your browser once to clear out stale proxy connections.

05proxies: Proxy Node Fields

proxies is a list, with each entry fully describing one node. With a subscription, this block is generated by the provider and updated along with the subscription, so you usually don't need to hand-write it — but understanding the fields still matters, since checking these parameters is always the first step when a specific node won't connect. All protocols share four base fields: name (the node's name, referenced by groups and rules — must be unique within a config), type (protocol type), and server plus port (server address and port); udp: true declares that the node supports UDP forwarding, which voice calls and games rely on.

Shadowsocks (ss)

The protocol with the fewest fields: cipher specifies the encryption method (commonly an AEAD cipher such as aes-128-gcm or chacha20-ietf-poly1305), and password is the pre-shared key. The cipher and password must match exactly on both ends — getting either one wrong shows up as an immediate connection failure with no helpful error message.

VMess

Uses a uuid as the identity credential; alterId is fixed at 0 in modern deployments, and cipher is usually set to auto. VMess is often paired with transport-layer disguising: network: ws enables WebSocket transport, and within ws-opts, path and headers.Host must exactly match what the server expects; tls: true wraps another TLS layer on top. This protocol has a lot of interdependent fields — when it won't connect, compare each field against the subscription source first.

Trojan and newer protocols

Trojan runs over TLS by design, with only two core fields: password and sni (the server name declared during the TLS handshake). skip-cert-verify controls whether certificate validation is skipped — see the warning below. Newer protocols like VLESS, Hysteria2, and TUIC require the mihomo kernel and can't be parsed by classic Clash. The clients listed on this site's download page — Clash Plus, Clash Verge Rev, FlClash, Clash Nyanpasu — are all built on mihomo, so these protocols work out of the box.

Protocol (type)Required fieldsCommonly configured fields
sscipher, passwordudp, plugin
vmessuuid, alterId, ciphertls, network, ws-opts, servername
trojanpasswordsni, udp, alpn
vless (mihomo only)uuidflow, tls, network, reality-opts
hysteria2 (mihomo only)passwordsni, up, down, obfs
config.yaml · proxies example
proxies:
  - name: "SS-HK-01"
    type: ss
    server: hk01.example.com
    port: 8388
    cipher: aes-128-gcm
    password: "your-password"
    udp: true

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

  - name: "Trojan-SG-01"
    type: trojan
    server: sg01.example.com
    port: 443
    password: "your-password"
    sni: sg01.example.com
    udp: true

skip-cert-verify: true means giving up all validation of the server's certificate, leaving a clear opening for a man-in-the-middle to intercept traffic unnoticed. Use it only temporarily while debugging certificates on a self-hosted node — leaving it on long-term amounts to voluntarily abandoning TLS protection.

06proxy-groups: Proxy Group Fields

Proxy groups sit as a middle layer between nodes and rules: rules don't point directly at a node, they point at a group, and the group decides which node is actually used at any given moment. That layer of indirection means switching nodes doesn't require touching your rules, and a failed node can be switched away from automatically. Every group needs a name, a type, and a member list (proxies), where members can be node names, other group names, or the built-in DIRECT and REJECT.

Five group types

select is manual selection — whoever you click in the panel is who gets used, making it the most common "master switch" group. url-test periodically sends requests to the address specified by url and automatically picks the member with the lowest latency; interval is the test interval in seconds, and tolerance sets the switch threshold — if the latency difference between the old and new node is smaller than this many milliseconds, it won't switch, avoiding rapid back-and-forth between two roughly equal nodes; lazy: true keeps the group from running tests until a rule actually routes traffic through it, saving bandwidth. fallback tries members in list order and uses the first one that's reachable — if the primary goes down it moves on automatically, and switches back once the primary recovers, which suits a "one primary, several backups" setup. load-balance spreads connections across multiple members, with strategy set to either consistent-hashing (the same destination site always goes through the same node) or round-robin. relay chains members together in order, with traffic passing through each hop in sequence — latency and failure rate stack up with chain length, so use it only for special needs.

Nesting groups

Groups can reference other groups. A common layered pattern: build one url-test group per region (Auto-HK, Auto-JP, and so on), then build a top-level select group that pulls in all those regional groups plus DIRECT. Day to day you only touch the top-level group, and let automatic speed testing handle picking the best node within each region. Keep in mind that the millisecond figure shown for a url-test group measures "round-trip time to the test address," not actual bandwidth — don't pick nodes on latency alone. The mechanics behind this are broken down in the blog post How Clash Node Latency Testing Actually Works.

config.yaml · proxy-groups example
proxy-groups:
  - name: "Proxy Selector"
    type: select
    proxies:
      - "Auto Speedtest"
      - "Fallback Group"
      - "SS-HK-01"
      - "VMess-JP-01"
      - DIRECT

  - name: "Auto Speedtest"
    type: url-test
    url: https://www.gstatic.com/generate_204
    interval: 300
    tolerance: 50
    lazy: true
    proxies:
      - "SS-HK-01"
      - "VMess-JP-01"
      - "Trojan-SG-01"

  - name: "Fallback Group"
    type: fallback
    url: https://www.gstatic.com/generate_204
    interval: 300
    proxies:
      - "SS-HK-01"
      - "VMess-JP-01"

Convention is to use a lightweight endpoint that returns a 204 status (like generate_204) as the test URL — empty response body, minimal overhead. Don't set interval too short: every test round sends a request to every member, and a large group with a short interval can generate noticeable background traffic.

07rules: Syntax and Matching Order

rules is a list of strings, each shaped like TYPE,VALUE,TARGET (a handful of types skip the value or carry extra parameters). The kernel checks every new connection against this list top to bottom, and the first matching rule takes effect immediately — every rule after it is skipped for that connection. Order is priority, and that's the key to understanding all routing behavior. The list must end with a MATCH catch-all, otherwise unmatched traffic has undefined behavior.

Rule type quick reference

TypeExampleNotes
DOMAINDOMAIN,dl.google.com,Proxy SelectorMatches only on exact domain equality
DOMAIN-SUFFIXDOMAIN-SUFFIX,github.com,Proxy SelectorMatches the domain and all of its subdomains
DOMAIN-KEYWORDDOMAIN-KEYWORD,youtube,Proxy SelectorMatches if the domain contains the keyword — very broad, use with care
GEOSITEGEOSITE,cn,DIRECTMatches against a GeoSite domain category database (mihomo)
IP-CIDRIP-CIDR,192.168.0.0/16,DIRECT,no-resolveMatches when the destination IPv4 falls in the range
IP-CIDR6IP-CIDR6,fd00::/8,DIRECT,no-resolveIPv6 version of the range match
SRC-IP-CIDRSRC-IP-CIDR,192.168.1.50/32,DIRECTMatches on source IP — pairs with allow-lan to set a policy for a specific device
DST-PORTDST-PORT,22,DIRECTMatches on destination port
PROCESS-NAMEPROCESS-NAME,Telegram.exe,Proxy SelectorMatches on the local process name that initiated the connection (desktop platforms)
GEOIPGEOIP,CN,DIRECTMatches on the GeoIP location of the destination IP
RULE-SETRULE-SET,cn-domains,DIRECTReferences an external rule set defined under rule-providers
MATCHMATCH,Proxy SelectorMatches unconditionally — must be the last rule, and only the last

no-resolve: keep IP rules from triggering resolution

When the destination is still a domain name, IP-type rules (IP-CIDR, GEOIP, and so on) will by default resolve that domain to an IP first before comparing it — a resolution that may be entirely unnecessary and slows down matching. Appending the no-resolve parameter to an IP rule makes it skip domain-based traffic entirely, matching only connections that already target an IP directly. Rule of thumb: any IP rule placed after your domain rules that's only meant to catch leftover IP-based traffic should always carry no-resolve — the one exception is when you specifically want GEOIP,CN,DIRECT to apply to domain traffic too.

Ordering advice and common pitfalls

Recommended order: process-name and exact-domain rules first, DOMAIN-SUFFIX bulk rules in the middle, RULE-SET and GEOSITE large collections after that, GEOIP,CN,DIRECT second-to-last, and MATCH at the very end. Two frequent mistakes: first, DOMAIN-KEYWORD matches extremely broadly — a short keyword like DOMAIN-KEYWORD,go,Proxy Selector will catch a huge number of unrelated domains by accident; second, placing a broad rule before a narrower one means the narrower rule never gets a chance to fire — when troubleshooting, turn on log-level: debug and check which rule a connection actually matched. GEOIP and GEOSITE depend on local database files, and the database version directly affects match results — for sourcing and updating them, see the blog post How to Update Clash's GeoIP and GeoSite Databases.

config.yaml · rules example
rules:
  - PROCESS-NAME,Telegram.exe,Proxy Selector
  - DOMAIN,dl.google.com,Proxy Selector
  - DOMAIN-SUFFIX,github.com,Proxy Selector
  - DOMAIN-SUFFIX,githubusercontent.com,Proxy Selector
  - DOMAIN-KEYWORD,youtube,Proxy Selector
  - IP-CIDR,192.168.0.0/16,DIRECT,no-resolve
  - IP-CIDR,10.0.0.0/8,DIRECT,no-resolve
  - GEOSITE,cn,DIRECT
  - GEOIP,CN,DIRECT
  - MATCH,Proxy Selector

08proxy-providers and rule-providers

The provider mechanism pulls "the node list" and "the rule set" out of the main config into external resources that can be downloaded and updated on their own. The benefit is immediate: a subscription refresh only updates the node file, without touching the proxy groups and rules you've carefully tuned; rule sets can reference ready-made lists maintained by the community instead of forcing you to maintain thousands of lines of domains yourself.

proxy-providers: external node sources

Each provider has a custom key name. type: http means pulling periodically from url (interval is in seconds, so 86400 means once a day), with the result cached to path; type: file reads directly from a local file instead. The health-check sub-block configures health checking for this batch of nodes, with parameters matching those of a url-test group. Proxy groups reference a provider via the use field, and it can coexist with a proxies list — mixing hand-written persistent nodes with subscription nodes in the same group is completely valid. Subscription links themselves come in several formats — Base64, Clash YAML, and others — and providers need a Clash-YAML-formatted address; format detection and conversion is covered in the blog post Common Clash Subscription Link Formats Explained.

rule-providers: external rule sets

The fields largely mirror proxy-providers, with one key addition: behavior. domain means the file content is entirely domain names (the kernel matches efficiently via a domain tree), ipcidr means it's entirely IP ranges, and classical allows mixing arbitrary rule types but comes with the highest matching overhead — use the first two whenever possible instead of classical. The rule file's format is declared via format (yaml or text). Once defined, reference it in rules with RULE-SET,key-name,target; that entry follows exactly the same ordering principles as any other rule.

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

proxy-groups:
  - name: "Proxy Selector"
    type: select
    use:
      - airport
    proxies:
      - DIRECT

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

rules:
  - RULE-SET,cn-domains,DIRECT
  - GEOIP,CN,DIRECT
  - MATCH,Proxy Selector

When path uses a relative path, it's resolved against the config directory — don't point two providers at the same file. Once interval expires, the update happens on the next config load or kernel trigger, not on a precise timer.

09Override & Merge: Editing Config Without Losing Changes

A subscription is a complete config generated by the provider, and every update overwrites your local profile wholesale — edit dns or add rules directly inside the subscription file, and the next update wipes it all out. The override (or merge) mechanism solves this: your changes are stored separately, and the client automatically layers them on top every time it loads the subscription. The subscription can update freely and your changes keep applying.

Where to find override in each client

The mainstream clients listed on this site's download page — Clash Plus, Clash Verge Rev, FlClash, Clash Nyanpasu — all offer subscription override, though the entry point's name varies slightly, commonly labeled "Override," "Global Extended Config," or "Merge." Most clients offer two forms: declarative YAML merging (write a snippet, layered on by key) and script-based override (a JavaScript function that manipulates the config object directly, offering more flexibility). YAML merging covers everyday needs; scripting is best saved for programmatic cases like "batch-rename nodes based on a condition."

Merge semantics: what gets overwritten, what gets appended

The key to understanding merging is telling value types apart: scalar keys (like mode, log-level) and mapping keys (like the whole dns block), when present in an override snippet, directly replace the same-named content from the subscription; list keys (like rules, proxies) default to full replacement too — which usually isn't what you want, so Clash Verge Rev-style merging offers prepend- and append- prefixes: prepend-rules inserts a set of rules at the very front of the subscription's rule list (taking advantage of "first match wins" to claim priority), while append-rules adds to the tail, just before MATCH. The same pattern applies to prepend-proxies, prepend-proxy-groups, and similar keys.

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

append-rules:
  - DOMAIN-KEYWORD,tracker,REJECT

dns:
  enable: true
  enhanced-mode: fake-ip
  nameserver:
    - https://doh.pub/dns-query

Validate before you trust it

Whether you're editing directly or overriding, it's worth running a syntax check before saving. GUI clients' built-in editors typically flag error lines in real time; on the command line, mihomo ships with a config-check flag that validates without starting anything:

Terminal · Config validation
mihomo -t -f config.yaml

Once validation passes, reloading the config is enough to apply it — no need to restart the entire client. If behavior after loading doesn't match what you expected, check in the order this guide follows: is the inbound port correct → is dns enabled → has a broad rule stolen priority from a more specific one further down → which node is currently selected in each proxy group. If the issue persists, check the "Troubleshooting" category of the FAQ first; for the full setup path from scratch, revisit the quick-start tutorial; and if you haven't installed a client yet, head to the Get the Client page — Clash Plus is the top pick.