1. Check the config source, core, and YAML structure
config.yaml is a common configuration filename, but not every client uses it. Subscription downloads, local configs, and client-generated runtime configs may be stored in different locations. Before editing, check the client's config list to identify the active item, then inspect the runtime config. Editing an inactive file won't change the current connection.
Clash is a proxy configuration ecosystem; Clash Meta later adopted the name mihomo. Many core fields remain compatible, but protocol support, DNS options, and TUN settings vary by core and version. This guide covers the common structure first, then calls out mihomo-specific extensions. Client interfaces use different names, so the app icon alone won't tell you which core is running.
Three things to look for in YAML
- Indentation shows nesting. This guide uses two spaces, never tabs. Top-level keys like
dns,proxies, andrulesstart in the same column. - A colon separates each key and value. Write
mode: rule, not with a non-ASCII colon. A-at the start of a line marks a list item. - Names are references. Every policy group used by a rule must exist, and every node or other group referenced by a group must also be defined.
2. Ports and modes: decide how traffic reaches the core
Near the top of the file, you'll usually find listener ports, the operating mode, and the log level. Ports accept requests; the mode determines how the core selects an outbound route. These are separate steps: even correct rules won't proxy traffic if the app doesn't send it to the client.
mixed-port: 7890
allow-lan: false
mode: rule
log-level: info
ipv6: false
| Field | What the example means | What to check |
|---|---|---|
mixed-port | Accepts both HTTP and SOCKS proxy requests on port 7890 | The port configured in the app must match and must not be in use by another process |
allow-lan | false means other devices on the local network cannot connect | When sharing access, also check the listening address and firewall |
mode | rule routes connections according to the rules list | Global mode can't be used to verify rule matching order |
log-level | info logs routine runtime details | Switch to debug temporarily for troubleshooting, then switch back |
ipv6 | false disables IPv6 support in the relevant core component | It does not disable all IPv6 traffic on the operating system |
Choosing between mixed-port, port, and socks-port
port is the HTTP proxy port; socks-port is the SOCKS proxy port. Use mixed-port when you want a single entry point for both. For example, if an app is configured to use the HTTP proxy at 127.0.0.1:7890, the core must accept the corresponding requests on port 7890. Don't assign the same address and port to multiple listeners.
mode: rule routes traffic according to rules; global uses a global policy; direct connects directly. Changing modes doesn't automatically change your browser's system proxy settings. For an initial test, use the system proxy and enable TUN only after basic connectivity works. This avoids troubleshooting multiple traffic paths at once.
If you see external-controller: 127.0.0.1:9090, that's usually the listening address for the control API, not the proxy port. Don't enter 9090 in your browser's proxy settings, and don't expose the control interface to the public internet for convenience.
3. dns: understand resolvers, the listening address, and fake-ip
dns controls how the core handles DNS requests sent to it. The example below uses redir-host to explain the basics; the two IPs are sample DNS servers, not universal recommendations. Before using them, make sure they're reachable on your network and consider your DNS privacy requirements.
dns:
enable: true
listen: 127.0.0.1:1053
ipv6: false
enhanced-mode: redir-host
nameserver:
- 223.5.5.5
- 119.29.29.29
enable: trueenables the core's DNS feature, but doesn't automatically route every system DNS request through the core.listensets the local DNS listening address. The example uses port 1053, not the commonly used system port 53. Without additional forwarding, the system won't automatically query this port.nameserverlists upstream DNS resolvers. Traditional DNS usually uses port 53; DoH and DoT use their respective protocol endpoints.dns.ipv6: falsecontrols how this DNS module handles AAAA queries; it is not an operating-system IPv6 firewall.
Why does fake-ip return an address starting with 198.18?
enhanced-mode: fake-ip assigns a mapped address to each domain, then the core associates connections with the original domain. A common setting like fake-ip-range: 198.18.0.1/16 is used for this mapping; it isn't the website server's actual public IP address. Seeing an address in this range doesn't by itself mean DNS has been poisoned.
Some local network services, device discovery features, or apps that rely on real resolved addresses may need exceptions. Configure fake-ip-filter for the specific issue instead of adding a large number of broad domain patterns. Behavior differs between redir-host and fake-ip; after switching, start a new connection before testing.
mihomo: pay attention to how proxy server hostnames are resolved
Proxy servers may also be specified by hostname. mihomo offers fields such as proxy-server-nameserver to control how node hostnames are resolved. If an upstream DoH endpoint is itself a hostname, bootstrap resolution may also involve default-nameserver. If resolving a node's hostname requires the proxy, while establishing the proxy requires that hostname, you have a circular dependency. If every node times out after startup, check the logs for DNS resolution errors before changing rules.
4. proxies: node settings aren't subscription links
proxies is a list of node definitions. A node typically has name, type, server, and port; authentication and transport fields depend on the protocol. Policy groups refer to nodes by name. The server port is on the remote host, so don't confuse it with local port 7890.
This guide doesn't invent server addresses or passwords. To understand the structure, use proxies: [] for an empty node list, then temporarily point a policy group to the built-in DIRECT. This demonstrates direct routing, but it doesn't provide a proxy service. For actual proxying, add node settings supplied by your service provider and supported by your current core.
Where should you add a subscription URL?
- Full configuration subscription: Import it from the client's subscription or config page. The response may already include nodes, policy groups, and rules.
- Node collection subscription: A supported
proxy-providersfeature can load it, and policy groups can reference it withuse. - Single-node share link: The client must recognize or convert it. You can't paste it directly into
proxiesas a YAML node object.
A regular web page, login page, or expired-subscription notice isn't a node list. If importing fails to parse, check the response content before changing indentation over and over. Also distinguish a full config from the node collection format expected by a provider: sharing a .yaml extension doesn't make their internal structures interchangeable.
With proxy-providers, interval: 3600 commonly means the provider updates every 3,600 seconds. In a policy group's health check, interval: 300 means checks run every 300 seconds. These settings serve different purposes; neither is a latency threshold or a replacement for the client's full-subscription update settings.
5. proxy-groups: organize nodes into selectable routes
Rules usually point to a policy group, which then selects a node or another outbound route. That way, you can change nodes without editing every rule. The group below is named "Outbound" and contains only the built-in direct route, making it useful for checking references.
proxies: []
proxy-groups:
- name: Outbound
type: select
proxies:
- DIRECT
Three common policy group types
| Type | How it selects a route | What to keep in mind |
|---|---|---|
select | Choose a route in the group manually | Useful for pinning a node while troubleshooting rules and connectivity |
url-test | Automatically selects a low-latency route based on test results | The response time of the test URL isn't the same as download speed |
fallback | Selects the first available route in the list | Priority follows the list order, not lowest latency |
url-test and fallback usually also need settings such as a test URL and check interval. The URL must be reachable from your current environment; if the test site is down, several working nodes may all appear unavailable. Don't delete nodes based on a single latency test.
Keep group and node names unique and clear. If a rule points to "Outbound" and you rename the group to "Proxy Egress," update the rule too. Groups can be nested, but avoid circular references: if A points to B and B points back to A, route selection can't complete correctly.
6. rules: match from top to bottom; put MATCH last
In rule mode, connections are generally checked against the list in order, and the first matching rule determines the policy. More specific rules don't automatically take priority: a broad rule near the top may prevent a later, precise rule from ever running. An easy-to-check order is to put specific exceptions first, broader rules next, and the catch-all last.
rules:
- DOMAIN,example.com,Outbound
- DOMAIN-SUFFIX,example.org,DIRECT
- IP-CIDR,192.168.0.0/16,DIRECT,no-resolve
- IP-CIDR,10.0.0.0/8,DIRECT,no-resolve
- IP-CIDR,172.16.0.0/12,DIRECT,no-resolve
- MATCH,Outbound
DOMAINmatches an exact domain. The example matches onlyexample.com; it doesn't automatically include all its subdomains.DOMAIN-SUFFIXmatches the domain and its subdomains. The example includesexample.organdwww.example.org.IP-CIDRmatches a destination IP range. The example lists three common private IPv4 ranges, not a complete list of all special-use addresses.no-resolvemeans don't actively resolve a domain just to match this IP rule; it doesn't disable DNS for the entire connection.MATCHcatches connections that didn't match earlier rules, so put it at the end of the list.
GEOIP, RULE-SET, and rule providers
GEOIP,CN,DIRECT matches based on an IP geolocation database, not on the domain's text. A missing or outdated database, a failed update, or an IP geolocated to a different region can all affect the result. If you encounter a database error, continue with the GEOIP checks in Troubleshooting.
RULE-SET refers to a rule collection defined in rule-providers. It is not the same as proxy-providers, which stores nodes. Before using a rule collection, check its format, behavior type, and compatibility with your core. After importing one, you still need to reference it in rules; defining the resource alone doesn't make it part of rule matching.
7. Build a minimal config, then add TUN and subscription nodes
The example below combines the basic fields into a local learning config. It has no proxy servers, so every route ultimately connects directly. Use it to verify YAML nesting, listening ports, and policy references. If the client automatically overrides ports or DNS, trust the runtime config and logs.
mixed-port: 7890
allow-lan: false
mode: rule
log-level: info
ipv6: false
dns:
enable: true
listen: 127.0.0.1:1053
ipv6: false
enhanced-mode: redir-host
nameserver:
- 223.5.5.5
- 119.29.29.29
proxies: []
proxy-groups:
- name: Outbound
type: select
proxies:
- DIRECT
rules:
- DOMAIN,example.com,Outbound
- IP-CIDR,192.168.0.0/16,DIRECT,no-resolve
- MATCH,Outbound
Why leave TUN out for now?
TUN is a separate way to capture traffic, not a proxy node or a switch that makes rules work automatically. Common mihomo settings include tun.enable, stack, auto-route, and dns-hijack, but the results also depend on the operating system, service permissions, network adapter, and routing environment. Don't copy a TUN config written for an unspecified platform to every device.
First verify the path from app proxy to port 7890, then through the rules to the outbound route. Enable TUN afterward using a method supported by your client. If the internet stops working, check routing, DNS interception, and conflicts with other VPNs before rewriting proxies. For basic setup, see the TUN mode guide in Getting Started.
8. Config changes not taking effect? Check syntax, references, and runtime status
- Make sure you're editing the active config. Check the selected item and its last-updated time in the config list, then reload it. A client may store multiple files with the same name.
- Check the line reported in the syntax error. For a
yaml: linemessage, inspect that line and the one above it for colons, quotes, list markers, and indentation. Quote passwords containing special characters according to YAML string syntax. - Check for duplicate top-level keys. Don't append a second
dns:orrules:at the end of the file. Duplicate keys may be rejected or may overwrite each other, depending on the parser. - Check outbound references. If you see an error saying a proxy or policy doesn't exist, compare group names, node names, and capitalization character by character. Valid YAML syntax doesn't guarantee valid references.
- Check listeners and traffic entry points. If you see
address already in use, check whether port 7890 or the DNS listening port is already occupied. Changing the system proxy address won't resolve a port conflict. - Check which rule actually matched. Make sure the core is in rule mode, then inspect a new connection's rule details. If an app bypasses the system proxy, fix the traffic entry point or use TUN if needed.
Test the config with the core's built-in checker
If you can run mihomo from the command line, execute the command below from the config directory. Config tests may also need external resources and databases, so use the same working directory and resource environment as the actual runtime.
mihomo -t -f config.yaml
A passing test means the core accepted the config; it doesn't prove node credentials are valid, all upstreams are reachable, or a website used the intended route. Check the connection logs next. During troubleshooting, change one thing at a time and record the errors before and after. This usually makes the cause easier to find than changing DNS, rules, and TUN all at once.
The key to understanding a config is to trace each layer: entry port → DNS resolution → rule match → policy group → final route. For more rule troubleshooting, see config.yaml checks. Before switching clients, check which core the new client uses and which fields it supports, then migrate your config.