2026-07-18 Advanced 9 min read

Clash Config File Structure Explained Block by Block: What port, dns and rules Actually Do

A top-to-bottom walkthrough of a Clash config file covering the responsibilities of the global fields, proxies, proxy-groups and rules blocks, with a minimal working example showing how they work together.

Overview of the Config File Structure

A Clash config file is essentially a YAML document that, read from top to bottom, splits into four broad categories: global runtime parameters, proxy definitions, proxy group definitions, and routing rules. These four sections are independent yet interconnected — proxy groups reference proxies, rules reference proxy groups, and rules ultimately decide which path traffic takes. Understanding this reference chain is more useful than memorizing every field's possible values, because when a config throws an error or routing doesn't behave as expected, you troubleshoot by tracing back along this chain.

Clash Meta (i.e. mihomo) extends the original Clash's fields, adding TUN mode, DNS sniffing, GeoIP database paths and more, but the core four-part structure hasn't changed. This article uses the field scope of the Meta core as the baseline — the original Clash's fields are a subset of it, so reading this won't cause any confusion if you're on the original core.

Global Fields: port, mode, log-level, dns

Near the top of the file you'll usually find a set of standalone key-value pairs that govern how the client itself runs, without referencing any specific proxy.

  • port / socks-port: open an HTTP proxy port and a SOCKS5 proxy port respectively; the OS or an app connects to whichever one it needs.
  • mixed-port: a single port that accepts both HTTP and SOCKS5 requests. Most clients expose only this one port by default to cut down on config items.
  • allow-lan: whether other devices on the local network can use this machine's proxy, paired with bind-address to control the listening scope.
  • mode: takes rule (route by rules — the normal mode), global (send everything through the same proxy — useful for troubleshooting), or direct (bypass the proxy entirely — useful for temporarily disabling it).
  • log-level: controls log verbosity. It's often switched to debug temporarily while troubleshooting; for everyday use, info or warning is recommended to keep log files from growing endlessly.

The dns block is its own section and decides how domain names get resolved. This is the root cause behind many "routing looks like it works but actually doesn't" issues — if the DNS resolution stage already resolves a domain to the wrong IP, no amount of rule tweaking afterward can fix it. A basic, working dns section looks roughly like this:

config.yaml · dns snippet
dns:
  enable: true
  ipv6: false
  default-nameserver:
    - 223.5.5.5
  nameserver:
    - https://doh.pub/dns-query
  fake-ip-range: 198.18.0.1/16
  use-hosts: true

fake-ip-range is a common setting on the Meta core, used together with TUN mode. It assigns domains a fake local IP range, and the core handles the actual resolution and forwarding at the traffic layer, avoiding DNS pollution issues that come from relying directly on system DNS. Going deeper into this would pull in the full TUN mode pipeline, which isn't the focus here — the key takeaway is that the dns section and the rules section are two independent pipelines that still affect each other.

proxies: Defining Nodes

proxies is a list, and each item describes one specific proxy node. Fields vary slightly by protocol, but they all follow the same structure: name is the unique identifier used to reference this node later on, type determines the protocol (such as ss, vmess, trojan, hysteria2), and the remaining fields are connection parameters specific to that protocol.

config.yaml · proxies snippet
proxies:
  - name: "HK-01"
    type: ss
    server: hk01.example.com
    port: 443
    cipher: aes-256-gcm
    password: "your-password"
  - name: "SG-01"
    type: vmess
    server: sg01.example.com
    port: 443
    uuid: 0000-uuid-example
    alterId: 0
    cipher: auto

The easiest thing to overlook here: name is just a string label — the client never checks whether it matches the node's real geographic location. A mislabeled entry won't throw an error; it'll only cause confusion later when you're picking nodes inside a proxy group. The subscription link a provider gives you is essentially a complete generated proxies list, and what a subscription-conversion service does is normalize node info from different formats into this exact structure.

proxy-groups: Proxy Groups

proxy-groups is also a list, but each item doesn't define a node — it defines "how to pick one out of a set of nodes or other groups." A group's proxies field can list node names or the name of another proxy group, and this nesting is the basis for building multi-layer routing (for example, wrapping "auto-select" inside "manual select").

  • select: manual selection. The client UI shows a clickable list to switch between, suited to cases where you want to pick a node by hand.
  • url-test: automatic selection by latency. The client periodically pings a configured URL and auto-switches to the lowest-latency node.
  • fallback: failover. Tries nodes in list order and auto-switches to the next one when the current node becomes unavailable.
  • load-balance: load balancing across multiple nodes, distributing connections using a strategy such as consistent hashing.
config.yaml · proxy-groups snippet
proxy-groups:
  - name: "Auto"
    type: url-test
    proxies:
      - HK-01
      - SG-01
    url: "https://www.gstatic.com/generate_204"
    interval: 300

  - name: "Select"
    type: select
    proxies:
      - Auto
      - HK-01
      - SG-01
      - DIRECT

Notice DIRECT and REJECT in the last group — these are two built-in special identifiers that don't need to be defined under proxies: DIRECT means bypass the proxy, and REJECT means block the connection outright. They can show up in any group's candidate list, and they can also appear directly as the target policy in the rules section — a detail many people miss.

rules: Routing Rules

The rules section decides which proxy group ultimately handles each request. It's matched line by line, top to bottom, in order — rules listed earlier take priority, and once a rule matches, later rules are no longer checked. The basic format is "match type, match value, target policy," with these common match types:

  • DOMAIN-SUFFIX: matches by domain suffix, covering a domain and all its subdomains.
  • DOMAIN-KEYWORD: matches any domain containing the given keyword — a much wider net than a suffix match, and easy to over-match unintentionally.
  • IP-CIDR / IP-CIDR6: matches by IP range, commonly used to send local IP ranges direct.
  • GEOIP: matches by the geographic location an IP belongs to, relying on a local GeoIP database.
  • MATCH: the catch-all rule, placed last, matching any traffic not caught by earlier rules.
config.yaml · rules snippet
rules:
  - DOMAIN-SUFFIX,cn,DIRECT
  - DOMAIN-KEYWORD,google,Select
  - GEOIP,CN,DIRECT
  - MATCH,Select

The third field in a rule is a proxy group name (or DIRECT/REJECT) — not a node name directly. This is one of the most common mistakes beginners make. If you write a name in rules that only exists under proxies and doesn't appear in any proxy group, the client will throw an error when loading the config. To quickly check whether a given rule is actually being matched, temporarily set log-level to debug — the log will print exactly which rule matched each request.

The more rules you have, the more overhead each line-by-line match adds. Use GEOIP,CN,DIRECT or rule providers (rule-providers) instead of hundreds or thousands of hand-written domain rules — easier to maintain and it keeps the config file smaller.

Minimal Working Example: Putting All Four Sections Together

Stitch the four sections above together in order and you get a minimal config that runs right out of the box. It doesn't cover every scenario, but it's enough to show how the blocks work together — dns decides how domains resolve, proxies supplies the available nodes, proxy-groups organizes those nodes into switchable policies, and rules assigns specific traffic to a given policy.

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

dns:
  enable: true
  nameserver:
    - https://doh.pub/dns-query

proxies:
  - name: "HK-01"
    type: ss
    server: hk01.example.com
    port: 443
    cipher: aes-256-gcm
    password: "your-password"

proxy-groups:
  - name: "Select"
    type: select
    proxies:
      - HK-01
      - DIRECT

rules:
  - DOMAIN-SUFFIX,cn,DIRECT
  - GEOIP,CN,DIRECT
  - MATCH,Select

Drop this config into any Clash Meta-core client, swap in the real server address and password, and it'll run as-is. The full configs or subscription links most providers hand out are, when unfolded, essentially an expanded version of these same four sections — more proxy groups, longer rule lists, and possibly extra rule providers and speed-test URLs bolted on — but the skeleton never changes.

Common Troubleshooting Order

When a config throws an error or routing misbehaves, check things in roughly this order:

  1. YAML syntax

    Is indentation consistent (never mix tabs and spaces)? Is there a space after every colon? Is a password containing special characters wrapped in quotes? Most clients will point to the exact line number when loading fails.

  2. Do the referenced names exist

    Check whether the target proxy group names in rules and the node names referenced inside proxy-groups actually exist in their respective sections — spelling has to match exactly, including case.

  3. Is the rule order sensible

    Make sure a broad match (like a DOMAIN-KEYWORD rule) isn't placed ahead of a more specific rule, which would prevent that later, more precise rule from ever matching.

  4. Test DNS in isolation

    Temporarily switch mode to global to rule out whether the issue is a routing rule or the DNS resolution stage, then narrow it down section by section.

Get a Clash Client

Once you understand the config structure, install a Meta-core-capable client first before you start editing configs — it saves a lot of trial and error.

Download Clash