CONFIG REFERENCE

Clash Config File Reference

config.yaml handbook: structure overview, general fields, DNS, proxies, proxy groups, rule syntax, and override/merge, with YAML examples for each section. Fields follow the mihomo core (formerly Clash Meta); differences from the original Clash are noted separately.

  • CORE · MIHOMO
  • TYPE · Reference Handbook
  • PAIRS WITH · SETUP.HTML Tutorial

First, know what each page is for. The tutorial page is the fast-track path: install the client, import a subscription, pick a mode, and verify connectivity — just follow along and it works. This page is a reference handbook — it doesn't walk through steps, it explains what each field in config.yaml means, what values it accepts, and how to write it. Most subscription configs already ship pre-written; the handbook's job is to help you read and correctly edit them. The clients covered here match the lineup on the download page: Clash Plus is the top pick on every platform, and along with Clash Verge Rev and FlClash, they all share the mihomo core ecosystem — this handbook applies equally to all of them.

01YAML Structure Overview

config.yaml is the core's sole input. GUI clients like Clash Verge Rev, Clash Plus, and FlClash all do fundamentally the same thing: manage this file and hand it off to the mihomo core to execute. Every click in the UI — switching a node, changing a mode, adjusting a port — ultimately rewrites this YAML text. Understand this file and you understand everything a client does; edit it correctly and every client behaves correctly.

You don't need to memorize file paths. In Clash Verge Rev's config page, right-click any subscription card and choose "Open File" to jump straight to the raw config; the same menu also has update and edit options. For headless setups (servers, routers) with no GUI, the file location comes from startup flags — -f for a specific file or -d for a directory — independent of any client.

The top-level structure splits into five sections by responsibility. Order isn't enforced, but the community convention is fixed as follows:

# ① General fields: ports, mode, logging, external control
mixed-port: 7897
mode: rule
log-level: info

# ② DNS: the core handles domain resolution
dns:
  enable: true

# ③ Proxies: the outbound node list
proxies: []

# ④ Proxy groups: how nodes are organized
proxy-groups: []

# ⑤ Rules: routing logic, evaluated top to bottom
rules:
  - MATCH,DIRECT

Beyond these five sections is an extension zone: proxy-providers aggregates subscription nodes, rule-providers pulls in external rule sets, tun takes over system traffic, hosts defines a static domain table, and listeners adds extra listening ports. Look up whichever you need in its dedicated section.

Six hard rules of YAML syntax — break any one and activation fails:

  • Use spaces for indentation only, never tabs; indentation at the same level must be consistent width, conventionally two spaces.
  • key: value requires a space after the colon — key:value is invalid syntax.
  • List items start with - , also followed by a space; the - can align with the parent key or be indented further — just stay consistent throughout the file.
  • Quote a string if it contains # or : , starts with @, &, or *, or looks like a number or boolean. Password fields almost always need quoting.
  • # starts a comment; leave a space between it and the preceding content, or the # gets swallowed into the string before it.
  • Anchors &name and references *name can reuse repeated blocks — the core parses standard YAML and supports this feature.

A minimal working config. In twenty lines, this already delivers full routing: local mixed port + a single node + direct connection for domestic traffic, proxy for everything else:

mixed-port: 7897
allow-lan: false
mode: rule
log-level: info
dns:
  enable: true
  nameserver:
    - 223.5.5.5
    - 119.29.29.29
proxies:
  - name: NodeA
    type: ss
    server: ss.example.com
    port: 8388
    cipher: aes-128-gcm
    password: "your-password"
proxy-groups:
  - name: Default Proxy
    type: select
    proxies:
      - NodeA
      - DIRECT
rules:
  - GEOSITE,cn,DIRECT
  - GEOIP,CN,DIRECT,no-resolve
  - MATCH,Default Proxy

Changes take two steps: edit the file, then have the core reload it. In Clash Verge Rev, re-activating the config triggers a hot reload; GUI settings like system proxy, TUN, and launch-on-startup are client-side settings, not part of the subscription file — don't conflate the two.

Silently Ignored A misspelled field name won't throw an error — the core silently ignores fields it doesn't recognize. If a change "has no effect," check the field name letter by letter first, then check indentation.

Watch out for core differences. The original Clash is no longer maintained; Clash Meta was renamed mihomo and continues development — fields aren't shared across the three core generations. Node types and logic rules added by mihomo (vless, hysteria2, tuic, wireguard) aren't recognized by older cores. See the blog post "Clash Core Versions Explained" for which core each client bundles. This page's field reference follows mihomo throughout.

02General Fields: Ports, Mode & Baseline Behavior

General fields sit at the top level and control the core's listening ports, mode, and baseline behavior. They're not tied to any specific node — changing any of them affects the whole setup. Subscription configs usually ship with sane defaults; the ones you'll actually touch are ports, mode, and logging.

The port family. port is the HTTP proxy port, socks-port is the SOCKS5 port, and mixed-port merges both protocols onto one port — since system proxy settings and most apps only recognize one type, a mixed port skips the either-or dilemma; Clash Verge Rev defaults to mixed port 7897. redir-port and tproxy-port are for Linux transparent proxying only — leave them blank on desktop.

LAN access. allow-lan: true opens up LAN access, bind-address picks which network interface to listen on, and * means all interfaces. For the full setup letting phones and TVs share your computer's proxy, see the blog post "Setting Up Mixed Port & LAN Sharing". Opening it up means exposing it — only enable this on networks you trust, and keep it false on public Wi-Fi.

Mode. mode has three options: rule routes traffic by rule (the everyday default), global sends everything through one selected proxy group, and direct sends everything unproxied. Switching modes in the GUI just rewrites this field. The standard two-step debug for rules: switch to global first to confirm the proxy chain itself works, then switch back to rule and check rules one by one.

Logging and external control. log-level has five tiers, from silent to debug — use info day-to-day, bump to debug temporarily while troubleshooting, then dial it back down; debug logs are large and include every domain visited. external-controller is the listening address for the core's RESTful API — the GUI and any third-party dashboard control the core through it; if you bind it to a non-loopback address, you must also set secret, or you're effectively handing proxy control to anyone on the same network.

Fine-tuning. unified-delay: true normalizes latency measurement, canceling out protocol handshake differences so node latencies are actually comparable. tcp-concurrent: true dials candidate addresses concurrently and keeps the fastest. find-process-mode controls process matching, which PROCESS-NAME rules depend on. profile.store-selected: true makes the core remember manually selected nodes across restarts. ipv6: false is a common fix for "connects but won't load" symptoms on networks with broken IPv6.

FieldTypical valueDescription
port7890HTTP proxy port
socks-port7891SOCKS5 proxy port
mixed-port7897Mixed port for HTTP and SOCKS, recommended
redir-port / tproxy-port7892 / 7893Linux transparent proxy only, leave blank on desktop
allow-lanfalseWhether to allow LAN devices to connect
bind-address*Listening address when allow-lan is enabled
moderuleOne of rule / global / direct
log-levelinfosilent / error / warning / info / debug
ipv6falseWhether to allow AAAA resolution and IPv6 outbound
external-controller127.0.0.1:9090Core API listening address
secretemptyAPI access key, required for non-loopback listening
profile.store-selectedtrueRemembers manually selected nodes
port: 7890
socks-port: 7891
mixed-port: 7897
allow-lan: false
bind-address: "*"
mode: rule
log-level: info
ipv6: false
unified-delay: true
tcp-concurrent: true
find-process-mode: strict
external-controller: 127.0.0.1:9090
secret: "your-secret"
profile:
  store-selected: true
  store-fake-ip: false
Port In Use A bind: address already in use error on activation means the port is taken: switch mixed-port, or find and stop the process using it. On Windows, locate it with netstat -ano | findstr :7897; on macOS and Linux, use lsof -i :7897.

03DNS Fields: Resolution Path & fake-ip

DNS gets its own chapter because routing accuracy depends on it more than anything else. Rules like GEOIP and IP-CIDR both need a resolved result first; if resolution is tampered with, routing follows it into the wrong path. dns.enable: true is the prerequisite — set it to false and this whole section is inert, the core falls back to system resolution, and fake IPs and policy-based resolution both stop working.

Listening and mode. listen sets the address the core's DNS service listens on; in TUN mode, queries are intercepted internally by the core, so nothing needs to change on the system side. enhanced-mode: fake-ip is the mainstream choice: domain lookups immediately return a fake address from the 198.18.0.1/16 pool, and once the connection is actually established, the core matches rules by domain — skipping the wait for a real resolution and noticeably speeding up page loads. The old redir-host mode has been removed from mihomo; delete it if you see it in an older config.

fake-ip boundaries. fake-ip-range defaults to 198.18.0.1/16 — only change it if it conflicts with an internal subnet. fake-ip-filter is a whitelist of domains that never get a fake address: LAN domains, NTP time servers, and system connectivity checks must be listed, or printers, router admin panels, and time sync will mysteriously fail. The example below covers the common entries — copy it, then add your own internal domains.

Resolver groups. nameserver is the default group, supporting multiple formats (see table below); multiple servers are queried concurrently and the fastest reply wins. proxy-server-nameserver resolves proxy node domains specifically — since a node's address is itself often a domain, use this to specify a trusted resolver, avoiding the deadlock of "needing the proxy to resolve the proxy." direct-nameserver serves directly connected domains, typically your ISP's or a domestic public DNS.

FormatProtocolDescription
223.5.5.5UDP 53Plaintext query, fastest, can be intercepted
tls://dns.alidns.comDoTTLS-encrypted channel
https://doh.pub/dns-queryDoHHTTPS encryption, can run over port 443
quic://dns.alidns.comDoQQUIC transport, low latency
dhcp://en0DHCPUses the DNS handed out by DHCP on the network interface

Policy-based resolution. nameserver-policy routes queries to a resolver by domain — the key can be a specific domain, a geosite: category, or a rule-set: rule set. This is how you send domestic domains through an ISP DoH server and everything else through a trusted overseas DoH server. The old fallback field is deprecated and replaced by nameserver-policy in mihomo; when migrating, rewrite it as "which resolver group serves which domain category."

Other switches. respect-rules: true makes even overseas resolvers route their own traffic by rule, and needs to be paired with proxy-server-nameserver. use-hosts and use-system-hosts control where the hosts table comes from. prefer-h3 prioritizes HTTP/3 for DoH queries.

dns:
  enable: true
  listen: 0.0.0.0:53
  ipv6: false
  enhanced-mode: fake-ip
  fake-ip-range: 198.18.0.1/16
  fake-ip-filter:
    - "*.lan"
    - "*.local"
    - "time.*.com"
    - "ntp.*.com"
    - "+.msftconnecttest.com"
    - "+.msftncsi.com"
  use-hosts: true
  use-system-hosts: true
  prefer-h3: true
  nameserver:
    - https://doh.pub/dns-query
    - https://dns.alidns.com/dns-query
  proxy-server-nameserver:
    - https://doh.pub/dns-query
  direct-nameserver:
    - 223.5.5.5
    - 119.29.29.29
  nameserver-policy:
    "geosite:cn":
      - 223.5.5.5
      - https://doh.pub/dns-query
    "geosite:geolocation-!cn":
      - https://dns.cloudflare.com/dns-query
      - https://dns.google/dns-query
Internal Domains Need Two Entries Internal domains need to go into both fake-ip-filter and nameserver-policy (or direct-nameserver). Add it to only one and you may still get a fake address, showing up as an internal site that works sometimes and not others.

Debugging clues. If a page "connects but won't load," loads painfully slowly, or resolves to the wrong region, check DNS first: look up the domain in the logs to see which resolver group it went through and what result it got, then go back and fix the corresponding field.

04Proxy Fields: the proxies Array

proxies is an array — one element per node. Every node shares four base fields: name, type, server, port; everything else depends on type. Misspell a type-specific field and the core won't error out — it just ignores it, and the node shows up as unreachable.

name is the node's identity: proxy groups reference it by name, a duplicate name has the later one override the earlier one, and renaming it breaks every reference. Quote the name if it contains spaces, colons, or #. server can be a domain or an IP; when it's a domain, proxy-server-nameserver handles the resolution, as covered in the previous chapter.

Common optional fields. udp: true enables UDP forwarding, needed for QUIC, gaming, and voice calls. skip-cert-verify: true skips certificate validation — use it only as a temporary troubleshooting step. tfo enables TCP Fast Open. interface-name specifies the outbound network interface, useful on multi-NIC machines.

Shadowsocks

  - name: ss-node
    type: ss
    server: ss.example.com
    port: 8388
    cipher: aes-128-gcm
    password: "your-password"
    udp: true

cipher common values: aes-128-gcm, aes-256-gcm, chacha20-ietf-poly1305, 2022-blake3-aes-128-gcm. plugin can load obfs or v2ray-plugin for traffic obfuscation, with parameters under plugin-opts.

VMess

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

alterId should always be 0 on modern servers. network supports tcp, ws, grpc, h2, http; for ws transport, the path and headers.Host under ws-opts must exactly match the server side — a single mismatched character means a 400 error.

VLESS + Reality

  - name: vless-reality
    type: vless
    server: 192.0.2.10
    port: 443
    uuid: 00000000-0000-0000-0000-000000000000
    network: tcp
    tls: true
    udp: true
    flow: xtls-rprx-vision
    servername: www.microsoft.com
    client-fingerprint: chrome
    reality-opts:
      public-key: "your-public-key"
      short-id: "0123456789abcdef"

flow only recognizes xtls-rprx-vision. Reality's public-key and short-id come from the server config; client-fingerprint should generally be chrome, and servername is the domain the server is disguising as.

Trojan

  - name: trojan-node
    type: trojan
    server: trojan.example.com
    port: 443
    password: "your-password"
    sni: trojan.example.com
    alpn:
      - h2
      - http/1.1
    skip-cert-verify: false
    udp: true

sni must match the server certificate's domain, and alpn is typically h2 or http/1.1. The password is a credential — leaking it leaks the node.

Hysteria2

  - name: hy2-node
    type: hysteria2
    server: hy2.example.com
    port: 443
    password: "your-password"
    obfs: salamander
    obfs-password: "obfs-password"
    sni: hy2.example.com
    skip-cert-verify: false
    up: 50
    down: 200

Built on QUIC, always UDP, so the udp field doesn't need to be set. obfs currently only supports salamander, with the password under obfs-password. up and down are in Mbps — overstating bandwidth backfires on congestion control, so fill in real values. On heavily throttled networks, ports enables port hopping.

TUIC

  - name: tuic-node
    type: tuic
    server: tuic.example.com
    port: 443
    uuid: 00000000-0000-0000-0000-000000000000
    password: "your-password"
    alpn:
      - h3
    congestion-controller: bbr
    udp-relay-mode: native
    reduce-rtt: true
    sni: tuic.example.com

The fifth-generation protocol. congestion-controller accepts bbr, cubic, or new_reno — bbr tends to be more stable on lossy links. udp-relay-mode defaults to native; reduce-rtt: true reduces handshake latency.

WireGuard

  - name: wg-node
    type: wireguard
    server: 198.51.100.20
    port: 51820
    ip: 172.16.0.2
    private-key: "your-private-key"
    public-key: "peer-public-key"
    mtu: 1420
    udp: true

ip is the assigned tunnel address; private-key is your own key and public-key is the peer's — don't swap them. mtu is commonly 1420; some providers require three reserved byte values, which you should copy directly from the official client's export.

Subscription Aggregation: proxy-providers

Nodes delivered via subscription are managed by proxy-providers: the core periodically fetches updates and runs health checks, and proxy groups reference the whole set with use. Hand-written nodes and provider nodes can coexist in the same config.

proxy-providers:
  provider-01:
    type: http
    url: "https://example.com/subscribe?token=xxxx"
    path: ./providers/provider-01.yaml
    interval: 86400
    health-check:
      enable: true
      url: http://www.gstatic.com/generate_204
      interval: 300
The Cost of skip-cert-verify Leaving it on long-term means giving up TLS identity verification entirely, letting a man-in-the-middle impersonate the node. Only enable it briefly while diagnosing a certificate issue, then flip it back to false once the connection is confirmed.

How to check fields: when a node won't connect, find its type's example section and compare field by field — extra fields are ignored, missing fields fall back to defaults, and typos raise no warning. Subscription nodes' fields follow what the provider sends; core field names are consistent everywhere.

05Proxy Group Fields: proxy-groups

Proxy groups decide who traffic gets handed to. Rules only reference a policy name; behind that name is a group, and inside the group are nodes — this three-layer separation means you can swap nodes without touching rules, and edit rules without touching nodes.

Five types, each behaving differently:

typeBehaviorUse case
selectManual selectionOutermost entry group, GUI uses whatever you click
url-testPeriodic latency test, picks the lowestAuto-picks the best among nodes in the same region
fallbackTakes the first available in orderPrimary/backup failover, auto-reverts when primary recovers
load-balanceSpreads connections across multiple nodesHeavy downloads, multi-link aggregation
relayChained linksRelay acceleration, special exit nodes
  • select: manual selection, ideal as the outermost entry group with other groups nested underneath it.
  • url-test: periodically tests latency across nodes in the group and picks the lowest; set tolerance to 50 (ms) to stop latency jitter from causing constant node flapping.
  • fallback: takes the first node in proxies order that passes the health check; automatically reverts once the primary node recovers.
  • load-balance: strategy has three options — consistent-hashing pins the same domain to the same node, keeping site sessions stable; round-robin cycles through nodes; sticky-sessions keeps the same session on the same node.
  • relay: traffic enters through the first node and exits through the last, with every hop in between required to be working — if any hop fails, the whole chain breaks.

Common fields. proxies lists node names, use lists proxy-providers names, and the two can be mixed. filter uses a regex to match node names (e.g. Hong Kong|HK), exclude-filter excludes by the same logic in reverse, and exclude-type excludes by protocol type. icon supplies a GUI icon, hidden: true hides the group in the GUI, and disable-udp: true blocks the group from forwarding UDP.

Health check fields. url defaults to http://www.gstatic.com/generate_204, and a 204 response marks it healthy; interval is in seconds — too short drains battery, too long feels sluggish, with 300 being a common middle ground; timeout is the per-test timeout; lazy: true (the default) skips testing a group nobody's using; max-failed-times sets how many consecutive failures mark a node unhealthy; expected-status sets the expected HTTP status code.

Nesting. A group's proxies list can itself contain other group names — that's how a three-tier structure like "Default Proxy → Auto Select → individual nodes" gets built; rules only need to reference the outermost group name. Subscription configs usually already ship with several layers of groups — when adding nodes manually, add them to the outermost group so the change applies everywhere.

proxy-groups:
  - name: Default Proxy
    type: select
    proxies:
      - Auto Select
      - Failover
      - DIRECT
    use:
      - provider-01

  - name: Auto Select
    type: url-test
    use:
      - provider-01
    url: http://www.gstatic.com/generate_204
    interval: 300
    tolerance: 50
    lazy: true

  - name: Failover
    type: fallback
    proxies:
      - Hong Kong Node
      - Japan Node
    url: http://www.gstatic.com/generate_204
    interval: 120

  - name: Load Balance
    type: load-balance
    use:
      - provider-01
    strategy: consistent-hashing
    url: http://www.gstatic.com/generate_204
    interval: 300

  - name: Relay Chain
    type: relay
    proxies:
      - Entry Node
      - Exit Node

For the finer details of when to use each auto-managed type — latency-based selection, failover, or load balancing — see the blog post "Which Clash Proxy Group Type Should You Use". There's one design principle for groups: keep them lean, since every extra group is another thing to maintain; group names get referenced by rules, so renaming one means updating the rules too.

06Rule Syntax: Top to Bottom, First Match Wins

Rules are the brains of routing, and the matching model boils down to one sentence: top to bottom, stop at the first match. Order is priority; MATCH matches unconditionally and must be the last rule — anything after it never runs.

A single rule has three parts: type,parameter,policy, with some types accepting a fourth part, no-resolve. The policy can be a proxy group name or a built-in policy: DIRECT connects directly, REJECT rejects and returns an error, REJECT-DROP silently drops the connection, and PASS skips the current branch and keeps matching (mainly used with SUB-RULES).

TypeParameterDescription
DOMAINFull domainExact match on a single domain
DOMAIN-SUFFIXDomain suffixMatches the domain and all its subdomains
DOMAIN-KEYWORDKeywordMatches if the domain contains it — use sparingly, high false-positive risk
GEOSITECategory nameDomain category database, e.g. cn, category-games@cn
IP-CIDR / IP-CIDR6SubnetMatches by destination IP
IP-ASNASN numberMatches by destination autonomous system
GEOIPCountry codeMatches by destination IP's country
SRC-IP-CIDRSubnetMatches by source IP
SRC-PORT / DST-PORTPortMatches by source / destination port
PROCESS-NAMEProcess nameMatches by the initiating process
PROCESS-PATHFull pathMatches by process path
RULE-SETRule set nameReferences rule-providers
AND / OR / NOTSub-ruleLogical combination, mihomo-specific
SUB-RULESSub-rule group nameEnters a sub-rule branch
MATCHnoneCatch-all, must be the last rule

Domain-based and IP-based rules are a dividing line. Domain rules (DOMAIN, DOMAIN-SUFFIX, DOMAIN-KEYWORD, GEOSITE) match directly against the connection's domain — no resolution needed. IP-based rules (IP-CIDR, GEOIP, IP-ASN) need the destination IP — when a domain-based connection reaches an IP rule, the core is forced to resolve it first just to evaluate the rule. The fourth part, no-resolve, blocks that resolution: domain connections skip the rule entirely, and only connections that are already pure IP get matched against it. Every IP-based rule should carry no-resolve unless you specifically want routing to depend on the resolved result.

GEO data. GEOSITE and GEOIP depend on the geosite.dat and geoip.dat (or mmdb) data files; geodata-mode: true switches to the dat format. These files update alongside the core, and if missing, the corresponding rules silently fail to match, showing up as broken routing.

Logical Rules

mihomo supports combining sub-rules with AND, OR, and NOT, wrapped in double parentheses:

rules:
  - AND,((DOMAIN-SUFFIX,example.com),(PROCESS-NAME,chrome.exe)),Default Proxy
  - OR,((DOMAIN-KEYWORD,blog),(DOMAIN-SUFFIX,notes.io)),Default Proxy
  - NOT,((GEOSITE,cn)),Default Proxy

Rule Sets: rule-providers

Large, frequently updated rules are handed off to rule-providers as external rule sets, referenced in rules with RULE-SET,name,policy; behavior has three options: domain (domain suffix), ipcidr (IP range), classical (the classic three-part format).

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

rules:
  - RULE-SET,ad-list,REJECT
  - MATCH,Default Proxy

Process matching. PROCESS-NAME and PROCESS-PATH depend on find-process-mode; on Windows, process names include the .exe extension, while macOS and Linux use the executable's file name.

Ordering principles:

  • Specific before broad: DOMAIN before DOMAIN-SUFFIX, suffix rules before GEOSITE.
  • Put LAN and internal subnets at the top, routed directly, never through a proxy.
  • Wrap up domestic direct-connect rules with GEOSITE,cn plus GEOIP,CN — don't list domains one by one.
  • MATCH always comes last, pointing to the main proxy group or DIRECT.
rules:
  # LAN and internal subnets, direct connect
  - DOMAIN-SUFFIX,local,DIRECT
  - IP-CIDR,10.0.0.0/8,DIRECT,no-resolve
  - IP-CIDR,172.16.0.0/12,DIRECT,no-resolve
  - IP-CIDR,192.168.0.0/16,DIRECT,no-resolve
  # Processes and apps
  - PROCESS-NAME,steam.exe,Game Boost
  # Domain rules, specific first
  - DOMAIN,api.example.com,Default Proxy
  - DOMAIN-KEYWORD,telegram,Default Proxy
  # Wrap up with category databases
  - GEOSITE,category-games@cn,DIRECT
  - GEOSITE,cn,DIRECT
  - GEOIP,CN,DIRECT,no-resolve
  # Catch-all
  - MATCH,Default Proxy

07Override & Merge: Keep Your Changes Across Subscription Updates

Manually editing a subscription file has a catch: the moment the subscription updates, every change gets wiped out. Clash Verge Rev solves this with a three-layer editing system, split by "what you're changing and whether it should stick around."

Layer one: edit file. Right-click a subscription card on the config page and choose "Edit File" to modify the raw YAML directly — changes take effect on save, but get overwritten on the next subscription update. Use this only for quick debugging or testing an idea; once verified, move the change to one of the two layers below.

Layer two: subscription-level editing. Right-click and choose "Edit Rules," "Edit Proxies," or "Edit Proxy Groups" to prepend or append changes to that subscription's rules, nodes, or groups respectively. It's tied to the current subscription and survives updates — this is the layer you'll use most: adding a LAN direct-connect rule or a self-hosted node both happen here.

Layer three: global extension config. Accessed from the top right of the config page, in two forms: Merge declares merge intent in YAML, while Script gets full access to the config object via JavaScript to rewrite it freely. It applies to every config and survives switching subscriptions — good for personal baseline settings unrelated to any particular subscription.

Merge: Declarative Merging

Six dedicated keys control array merge direction — prepend goes first, append goes last; prepending a rule means it takes priority over the subscription's own rules. Every other top-level key simply overrides the original value: scalars like mixed-port and mode are replaced as-is; nested sections like dns are replaced wholesale — when overriding, write out the entire section, not just half of it.

prepend-rules:
  - DOMAIN-SUFFIX,internal.example.com,DIRECT
append-rules:
  - DOMAIN-KEYWORD,download,Download Group
prepend-proxies:
  - name: Self-Hosted Backup
    type: ss
    server: 203.0.113.10
    port: 8388
    cipher: aes-256-gcm
    password: "your-password"
append-proxy-groups:
  - name: Download Group
    type: select
    proxies:
      - Self-Hosted Backup
      - DIRECT
mixed-port: 7897

Script: Programmatic Rewriting

Use a script when you need conditional logic. The entry point is fixed as main(config), where the parameter is the fully merged config, and the return value becomes the final config handed to the core:

function main(config) {
  config["mixed-port"] = 7897;
  const extra = {
    name: "Self-Hosted Backup",
    type: "ss",
    server: "203.0.113.10",
    port: 8388,
    cipher: "aes-256-gcm",
    password: "your-password"
  };
  config.proxies = config.proxies || [];
  config.proxies.push(extra);
  (config["proxy-groups"] || []).forEach(function (group) {
    if (Array.isArray(group.proxies)) {
      group.proxies.push("Self-Hosted Backup");
    }
  });
  return config;
}

The order of precedence is fixed: subscription source → subscription-level edits (rules/proxies/proxy groups) → global Merge → global Script → core. Debug by working backward through the chain: first check what the final config looks like at runtime, then narrow down which layer introduced the problem.

Common override scenarios:

  • Direct-connect internal domains: add a DOMAIN-SUFFIX rule via prepend-rules so it takes priority and matches first.
  • Inject a self-hosted node into every group: use a Script to loop through proxy-groups and append it everywhere.
  • Unify the port: write a single mixed-port line in Merge.
  • Add internal domains to fake-ip-filter: override the entire dns section, copying the subscription's existing filter list in full before appending to it.

Other clients and headless setups. Clash Plus and FlClash both offer a similar "subscription + local edits" mechanism, with the same idea but a different entry point; running the core headless has no override layer at all — you edit the source file directly, so it's worth tracking history with Git. For a comparison of ways to keep the same config synced across multiple devices, see the blog post "Three Ways to Sync Clash Config Across Devices".

Debug Layer by Layer Keep override-layer errors separate from source-file errors: when activation fails, the error message identifies exactly which layer and section is at fault — fix that layer first, and leave the subscription source alone.

08Validation & Troubleshooting: From Errors to Logs

Read the error first when activation fails. When Clash Verge Rev activates a config, the core does a full parse and returns an error with a line number and a reason; go back to that line in the file, and nine times out of ten that's where the problem is. If the error message gets cut off, check the logs page for the full output.

Common YAML errors, ranked by frequency:

  • Tab indentation: set your editor to convert tabs to spaces, and keep two spaces consistently throughout the file.
  • Missing space after colon: port:7890 is invalid syntax.
  • Unquoted password containing #: everything after the # gets treated as a comment, truncating the password.
  • Misaligned list indentation: the -'s relationship to its parent key gets scrambled, and the node ends up under the wrong key.
  • Duplicate node names: the later one overrides the earlier one, leaving only one node for proxy groups to reference.
  • A rule points to a policy name that doesn't exist: activation immediately errors with proxy not found — check the group name spelling.

Logs are your second line of defense. Temporarily bump log-level to debug, and the core's log page shows which rule each connection matched and which outbound it took; see the blog post "A Tour of the Clash Verge Rev Interface" for what each section of the UI does. Switch it back to info once you're done.

If a change "doesn't take effect," check in this order:

  1. You edited the file but never re-activated the config on the config page, so the core is still running the old one.
  2. You edited the subscription source, but the override layer changed it right back — check the final runtime config to confirm.
  3. The config is correct, but the GUI is still attached to the old core process — restart the core or reconnect.
  4. System proxy or TUN isn't enabled, so traffic never reaches the core in the first place — no config change will help.

Ports and LAN. A bind: address already in use error means the port is taken: switch mixed-port, or stop the process using it. If allow-lan is on but your phone can't connect: first check whether your computer's firewall allows the port, then confirm both devices are on the same subnet and you've entered the computer's internal IP.

fake-ip issues. Some apps (banking, government/enterprise, and some anti-cheat-enabled games) are sensitive to fake addresses: add the domain to fake-ip-filter, set up direct-nameserver, and if needed, route the whole rule direct. Re-activate the config after making changes for it to take effect.

TLS and time. For certificate errors, check your system clock first — even a few minutes of drift will break the handshake; skip-cert-verify is only a temporary troubleshooting step — turn it back to false once the connection is confirmed working.

DNS leaks and tampering. If you suspect a leak, check in order: whether all nameserver entries go through a trusted channel, whether proxy-server-nameserver is configured, whether respect-rules behaves as expected, and whether the geosite category names under nameserver-policy are spelled correctly — a misspelled category name fails silently with no error.

Wrapping up. Still stuck? Work through the categorized list on the FAQ page; client downloads and core details are on the download page, and a side-by-side comparison is on the client comparison page.

Keep a Backup First Save a working copy before editing your config. If any layer breaks, reverting to the backup and re-activating is faster than rewriting it from scratch.