Clash Custom Rule Syntax: Match Types, Format & Priority Order
Covers DOMAIN, DOMAIN-SUFFIX, IP-CIDR, PROCESS-NAME syntax and use cases, plus top-to-bottom match priority, the no-resolve flag, and practical rule ordering.
Clash's traffic routing all comes down to the rules field in the config file. Each rule decides which proxy group a category of traffic goes through — get the order wrong or use the wrong match type, and at best one site's routing fails to work, at worst an entire proxy group stops functioning or DNS resolution breaks. This article walks through the common rule types as implemented in the mihomo core (Clash Meta), covers their syntax and use cases one by one, explains how rules take effect "top to bottom," and finishes with an ordering approach you can copy directly.
Basic Structure and Evaluation Order of the Rule System
The rules field in a Clash config is an array, with each line being one rule, in a consistent format:
rule-type,match-content,proxy-group[,extra-param]
For example:
rules:
- DOMAIN-SUFFIX,github.com,🚀 Proxy Select
- IP-CIDR,192.168.0.0/16,DIRECT,no-resolve
- GEOIP,CN,DIRECT
- MATCH,🚀 Proxy Select
When the core processes a network request, it checks the rules in array order from top to bottom. As soon as a rule matches, it immediately applies that rule's assigned proxy group and skips every rule below it — matching stops there. This "match and stop" behavior is the part of the rule system most often overlooked, and the most common source of bugs. Many people assume rules are evaluated by "whichever is more specific wins," but in reality it's entirely about position — earlier in the list beats later, full stop.
That's exactly why a rule file almost always ends with a MATCH,proxy-group fallback: it represents "where traffic goes if nothing above matched," essentially the last row of the routing table. It must sit at the very end of the array — putting it earlier makes every rule after it permanently unreachable.
Common Match Types Explained
mihomo supports many rule types, but the ones that actually show up in day-to-day configs fall into just a few categories: domain-based, IP-based, geolocation-based, and process-based.
Domain-Based: DOMAIN / DOMAIN-SUFFIX / DOMAIN-KEYWORD
DOMAIN,www.example.com,proxy-group— Matches one exact, complete domain only; subdomains won't be caught. Good for handling a single specific domain in isolation.DOMAIN-SUFFIX,example.com,proxy-group— Matches the domain and all its subdomains (bothwww.example.comandapi.example.comcount as matches). This is the most commonly used type in day-to-day configs — one rule can cover an entire domain family.DOMAIN-KEYWORD,example,proxy-group— Matches if the domain simply contains this keyword. It has the widest match range and can easily catch unrelated domains by accident, so only use it when the keyword is distinctive enough not to collide with other services.
IP and Subnet-Based: IP-CIDR / IP-CIDR6
Rules like IP-CIDR,192.168.1.0/24,DIRECT,no-resolve match the target IP against a CIDR block, commonly used to force local network ranges, internal servers, or fixed CDN IP blocks to bypass the proxy or route through a specific path. IPv6 addresses use IP-CIDR6 with identical syntax. These rules usually need to sit near the top of the rule table, because if local network access gets intercepted first by a domain-based or GEOIP rule, it ends up being routed through the proxy for no reason.
Geolocation-Based: GEOIP
GEOIP,CN,DIRECT relies on a GeoIP database to determine which country or region an IP belongs to. A common pattern is mapping GEOIP,CN to a direct-connect policy so that IPs in mainland China skip the proxy. These rules naturally belong toward the end of the rule table, since their match range is extremely broad — placing one too early will scoop up a huge amount of traffic that should have been handled by more specific rules first.
Process-Based: PROCESS-NAME / PROCESS-PATH
PROCESS-NAME,com.apple.WebKit.Networking,DIRECT matches based on the process that originated the request, useful for assigning a policy to a specific client — for example, always connecting a download tool directly, or always routing a certain app through a specific node. On desktop platforms, process-based rules offer the finest granularity, but process names can vary across OS versions, so if one isn't matching, check the exact name first.
Logical Combinations and the Fallback: AND / OR / NOT / MATCH
mihomo also supports logical rules that combine several sub-conditions into one, for example:
- AND,((DOMAIN-SUFFIX,example.com),(NETWORK,tcp)),proxy-group
This kind of syntax suits fine-grained scenarios where multiple conditions must all hold. It doesn't come up often in everyday configs, but understanding the structure helps a lot when reading community-shared rule sets. Finally, MATCH,proxy-group is the only rule type that needs no match content — it's the fallback policy, should appear exactly once in the rule table, and must be the last line.
The no-resolve Flag and DNS Resolution Timing
IP-CIDR and IP-CIDR6 rules can take a trailing no-resolve flag — one of the details most often overlooked in practice, and the most common reason a rule "looks like it isn't working."
By default, a domain-based rule (such as DOMAIN-SUFFIX) doesn't need any IP resolved beforehand — it matches directly on the domain string. IP-CIDR rules, on the other hand, essentially compare a "target IP" against a subnet; if the request itself is a domain rather than an IP, the core first has to resolve that domain to an IP before it can check it against the IP-CIDR rule. Adding no-resolve skips that resolution step entirely — the rule then only applies to connections whose request is already an IP (not a domain), and the core won't trigger an extra DNS lookup just to test the rule.
If an IP-CIDR rule's target subnet is exactly the kind of thing that should intercept resolution triggered by a domain request (for example, an internal service that's only ever accessed by domain name), leaving out no-resolve means every match triggers an extra DNS lookup — adding latency and potentially sending the resolution request through the wrong DNS server. Conversely, if a rule is only meant for traffic that's already pure-IP direct-connect traffic, adding no-resolve avoids unnecessary resolution overhead.
The practical rule of thumb is simple: if an IP-CIDR rule targets traffic that will clearly show up as an IP already (internal devices, services that expose IPs directly), add no-resolve. If the target is normally accessed by domain and its routing depends on the resolved result, it's more direct to let an earlier domain-based rule handle it instead of routing it through IP-CIDR.
Practical Ordering Principles and Common Mistakes
Once you understand the "top-to-bottom, match-and-stop" mechanism, rule ordering follows a fairly fixed approach — arranged from narrowest match range to broadest:
- Local network and internal direct-connect rules first. Put private IP ranges and internal domains at the very top so they don't get intercepted early by broader rules further down, which would otherwise route internal traffic through the proxy.
- Then specific domains or apps needing special handling. For example, streaming domains that need a specific node, or commonly used mainland Chinese service domains that need direct connection — cover these precisely with DOMAIN-SUFFIX.
- Next, rule sets (rule-providers). Community-maintained categorized rule sets — ad filtering, common service categories — ordered by however they need to be referenced.
- Then geolocation-based rules. Typically
GEOIP,CN,DIRECT, letting through any mainland China IP not already caught by an earlier rule. - The last line is always the MATCH fallback. All unmatched traffic goes here, usually pointing to a proxy-select or proxy group.
Ordering mistakes tend to fall into two categories: one is placing GEOIP,CN,DIRECT too early, so traffic that should have gone through the proxy ends up direct-connected simply because the resolved IP happens to fall in a mainland China range; the other is accidentally putting the MATCH fallback rule somewhere in the middle, which makes every rule written after it permanently unreachable — easy to mistake for a syntax error in some specific rule when it's actually an ordering problem. When troubleshooting, check the array order first, then the syntax — if the order is wrong, no amount of syntax precision will make it work.
Organizing Large Rule Sets with Rule Providers
Once custom rules grow to dozens or hundreds of lines, stacking them all directly in the rules array becomes hard to maintain. mihomo supports a rule-providers field that splits rules into separate remote or local files, referenced from rules via RULE-SET,rule-set-name,proxy-group:
rule-providers:
my-direct:
type: http
behavior: domain
url: "https://example.com/rules/direct.yaml"
path: ./rule-providers/direct.yaml
interval: 86400
rules:
- RULE-SET,my-direct,DIRECT
- GEOIP,CN,DIRECT
- MATCH,🚀 Proxy Select
The behavior field determines whether entries in that rule set are parsed as domains, IP ranges, or classification rules, and must match the actual file content; interval sets how often it's automatically re-fetched. This approach decouples "categorization logic" from "specific rule content" — updating a rule set doesn't require touching the ordering in the main config, which is noticeably less maintenance than hand-writing every rule into one file.
Two Checks Worth Doing Before and After Writing Rules
Before putting a rule set into use, it's worth doing two checks: first, confirm the rule array has exactly one MATCH and that it's at the very end — this is the easiest thing to miss; second, for any new IP-CIDR rules, confirm no-resolve was added or omitted as intended, especially for rules touching internal network access, since getting this wrong in either direction causes connection issues that look "random." If the routing results in the logs don't match expectations, re-read the rule array in order first — that's usually faster than checking syntax line by line.