Start with the structure: entry points, DNS, nodes, policies, and rules
When you open a Clash config file, there’s no need to guess your way through it line by line. Start with the top-level fields: mixed-port determines where apps connect, dns manages built-in name resolution, proxies stores nodes, proxy-groups organizes available outbound options, and rules determines how each connection is handled. Understanding these five sections makes it easier to distinguish between a file that loads and traffic that routes as expected.
A typical reference chain works like this: a rule selects a policy group, and the group then selects a node. If the rule targets DIRECT, the connection goes straight to its destination without using a remote proxy node. The order of top-level fields usually doesn’t determine execution order, but the order of entries in rules directly affects which rule matches.
| Config section | Purpose | What to check first |
|---|---|---|
mixed-port | Accepts HTTP and SOCKS5 proxy connections | Port conflicts and the address configured in the app |
dns | Built-in resolver and enhanced mode | Whether it’s enabled and requests reach the core |
proxies | Connection parameters for remote nodes | Protocol, address, port, and authentication |
proxy-groups | Selects or tests an outbound route | Name references and current selection |
rules | Routes connections based on conditions | Match order and the final fallback policy |
YAML syntax: check indentation before field meanings
YAML uses indentation to represent nesting. Stick to two spaces throughout and don’t use tabs. Align fields at the same level, and start list items with a hyphen followed by a space. The file extension can be .yaml or .yml, but changing the extension won’t fix invalid syntax.
proxy-groups:
- name: "Proxy Selection"
type: select
proxies:
- DIRECT
This config uses two related concepts in different places: top-level proxy-groups is a list of policy groups, while proxies inside a group is a list of selectable names, not full node definitions. name, type, and the group’s proxies belong to the same object, so they must use the same indentation.
Check these four things in your editor
- Space after the colon: Write
mode: rule; don’t run the key and value together. - Boolean values:
allow-lan: falseuses a boolean, so don’t putfalsein quotes. - Strings with special characters: Quote node names, passwords, and values containing
#to prevent them from being misread as comments or syntax. - Duplicate keys: Don’t add a second top-level
dnsorrulesat the end of the file. Depending on the parser, the config may fail to load or the earlier value may be overwritten.
Syntax errors usually include a line number, but the actual problem may be on the previous line. For example, a list item with two spaces missing may trigger an error on the next field. Start at the reported line, then check the nearest preceding group of fields and list structure.
Top-level settings: mixed-port, allow-lan, and mode
mixed-port: 7890
allow-lan: false
mode: rule
log-level: info
ipv6: false
mixed-port: 7890 lets a single listening port accept both HTTP and SOCKS5 proxy connections. For local use, set the app’s address to 127.0.0.1 and port to 7890. The port number isn’t fixed; if the log says address already in use, first check whether another client or process is using it.
allow-lan: false prevents devices on your local network from connecting through this proxy entry point. To share access, you’ll also need to check the listening address, system firewall, and whether the current network is trusted. Don’t disable all inbound restrictions just to troubleshoot a local connection.
How the three modes relate to rules
rule: Routes traffic according to the rule list, making it useful for testing routing configs.global: Routes traffic through a global policy rather than evaluating the standard rule list one entry at a time.direct: Sends connections directly; changing proxy rules usually won’t have the expected effect in this mode.
log-level: info is suitable for checking connections and errors in everyday use. You can briefly switch to debug while troubleshooting, then switch back to avoid excessive logs. ipv6: false is a choice for this starter config, not a recommendation to disable IPv6 on every network. If you need IPv6, check DNS responses, system routes, and node support together.
The dns section: enabling the resolver and choosing an enhanced mode
dns is a nested object. First decide whether to enable built-in DNS, then choose an enhanced-mode. The example below uses traditional IP addresses for upstream servers to make the field relationships easy to see; it isn’t a universal recommendation for every network.
dns:
enable: true
ipv6: false
enhanced-mode: fake-ip
fake-ip-range: 198.18.0.1/16
nameserver:
- 223.5.5.5
- 119.29.29.29
nameserver is a list of upstream DNS servers. Plain IP addresses like these typically use traditional DNS transport. For DoH, DoT, or more granular DNS routing, check core support and upstream reachability. Top-level ipv6 and dns.ipv6 aren’t interchangeable switches; the latter mainly affects how built-in DNS handles AAAA queries.
fake-ip returns synthetic addresses for domains that meet the configured criteria. The core maps those addresses to domain names, then handles traffic using the domain associated with each connection. 198.18.0.1/16 is in an address range reserved for benchmarking; it isn’t a node’s public IP and shouldn’t go in a node’s server field.
Why hasn’t system DNS changed even though dns is configured?
enable: true only enables built-in DNS; it doesn’t guarantee that all system DNS requests will use it. A domain sent through a proxy protocol, one resolved directly by the system, and one handled through TUN with DNS hijacking take different paths. To troubleshoot, first identify which path the app uses, then check whether the client is configured to intercept DNS on that path.
If local network names, games, or device discovery stop working, consider using fake-ip-filter to let specific domains bypass synthetic addresses. Don’t paste in a long list of filters from an unknown source; verify both the resulting real DNS answers and the routing that follows.
proxies and proxy-groups: keep node settings separate from outbound names
Top-level proxies stores node objects. Each object needs at least a name that can be referenced; other required fields depend on the protocol. For example, a Shadowsocks node typically needs type, server, port, cipher, and password. Other protocols may also require UUID, TLS, or transport settings. Don’t apply one protocol’s field list to another.
Node settings should come from your actual service config or a trusted subscription. This article doesn’t provide made-up server or authentication details. The complete example below uses proxies: [] to represent an empty node list. That lets you validate YAML, the listening port, and rule references without mistaking a config that looks complete for one with a working node.
proxies: []
proxy-groups:
- name: "Proxy Selection"
type: select
proxies:
- DIRECT
select is a manual selection group. The group name “Proxy Selection” can be used as a rule target, and its only current option is the built-in DIRECT. To add a real node, first include its complete node object in the top-level node list, then add its name exactly as written to the group’s list. Spaces, capitalization, and symbols must match. Adding a node name alone won’t create a node.
What else does an automatic latency test group need?
Group types such as url-test also require settings like a test URL and check interval. Test results only reflect a particular request; they don’t guarantee that every site is reachable or indicate your actual download speed. When you’re first learning to read a config, start with select to verify each route, then add automatic selection.
The rules section: match from top to bottom and put MATCH last
In rule mode, the core checks entries in list order and uses the target of the first match. A target can be a policy group, a node name, or a built-in policy such as DIRECT or REJECT. For easier maintenance, rules usually point to policy groups rather than scattering node names throughout the list.
rules:
- DOMAIN,example.com,DIRECT
- DOMAIN-SUFFIX,example.org,Proxy Selection
- IP-CIDR,192.168.0.0/16,DIRECT,no-resolve
- MATCH,Proxy Selection
DOMAINmatches an exact domain. This entry matches onlyexample.com, not all of its subdomains.DOMAIN-SUFFIXmatches a domain suffix. This entry can matchexample.organd its subdomains.IP-CIDRmatches a destination IPv4 address range.192.168.0.0/16is one common private network range, not the full range of local networks.no-resolvemeans this IP rule won’t trigger a DNS lookup; it can still match when the destination IP is already known.MATCHis the fallback rule. It catches connections that didn’t match earlier entries and usually goes last in the list.
This example uses a reserved example domain to demonstrate syntax, not real-world routing. If you move MATCH,Proxy Selection to the first line, none of the rules below it will be reached. Another common misconception is that the order of sections in the file determines rule priority; what matters is the order of entries inside rules.
Using GEOIP or RULE-SET also requires the relevant database or rule provider. If the log reports a resource loading error, fix that first. Repeatedly editing rule text won’t help if the data those rules reference hasn’t loaded.
Minimal working config: verify direct connections first, then add real nodes
Below is a complete config for testing direct connections on your local machine, with the main nesting levels covered in this article. It starts a proxy listener and uses a policy group and rules to send requests directly; it does not include a remote proxy node, so it can’t test a remote proxy service. Unlike the earlier Fake-IP example, this one uses redir-host to reduce reliance on synthetic address mapping during initial testing.
mixed-port: 7890
allow-lan: false
mode: rule
log-level: info
ipv6: false
dns:
enable: true
ipv6: false
enhanced-mode: redir-host
nameserver:
- 223.5.5.5
- 119.29.29.29
proxies: []
proxy-groups:
- name: "Proxy Selection"
type: select
proxies:
- DIRECT
rules:
- DOMAIN,example.com,DIRECT
- IP-CIDR,127.0.0.0/8,DIRECT,no-resolve
- IP-CIDR,192.168.0.0/16,DIRECT,no-resolve
- MATCH,Proxy Selection
Save and verify the config
- Save a backup of your current working config, then save the content above as
config.yamlusing UTF-8 encoding. Make sure your editor hasn’t automatically added.txt. - To check the config with the mihomo command line, run
mihomo -t -f config.yaml. In a GUI client, use its built-in config validation or import feature; the menu name depends on the version. - After validation succeeds, load the config and check the logs for errors such as a port conflict, unsupported field, or missing policy. GUI clients may layer settings such as ports, DNS, and TUN on top of the original file, so check the final running config.
- Make a request with the proxy explicitly specified so the test doesn’t depend on whether the system proxy is enabled. Whether the test site is reachable also depends on your network’s direct connectivity.
curl -I --proxy http://127.0.0.1:7890 https://example.com
In Windows PowerShell, if curl runs as a different command, use curl.exe instead. Check the client’s connection log during the test: the destination should be example.com, the exact-domain rule should match, and the outbound policy should be DIRECT. An HTTP response alone doesn’t confirm that the request followed the expected path in your config.
After verifying direct connections, add nodes from your actual subscription or service config, add the node names to “Proxy Selection,” and select one manually in the client. Change one type of setting at a time: nodes first, then policy groups, then routing rules. That makes it easier to pinpoint the most recent change if something breaks.
Config fails to load or rules don’t work: troubleshoot by layer
| Symptom | Check first | What to do |
|---|---|---|
| YAML parsing error | Indentation, tabs, colons, and quotes | Check the reported line and the list above it |
| Policy or node not found | Rule target and name in the group | Compare it character by character with the corresponding name |
| Unable to bind to port 7890 | Another client or background core | Close duplicate processes, or change the port and update the app settings |
| Rule changes have no effect | Current mode, active config, and overrides | Switch back to rule mode, check the running config, then create a new connection |
| Changes disappear after updating the subscription | Whether you edited a file generated from the subscription | Use an override or merge method supported by the client |
Existing connections may keep using the route they had when they were established, even after you change rules or outbound settings. To verify a change, make a new request and, if needed, close the target app’s existing connections. Then check the log for a new rule match. Don’t assume the config failed to load just because a long-lived connection still uses its original route.
Subscription management also requires distinguishing the original content from the running result. A client may combine a subscription, global settings, and overrides into a final config. If you edit the downloaded subscription file directly, an update may replace your changes. For port or routing changes you need to keep, use an override mechanism explicitly supported by the client’s documentation, then check the final config after updating.
If you don’t have a client yet, visit the client downloads page to choose one for your operating system. For help importing a config, enabling the system proxy, and making your first connection, see the setup guide. Before sharing logs or configs for help, remove subscription links, passwords, UUIDs, and other credentials.