Advanced 11 min read

Clash custom rules

Learn Clash rule syntax, top-down matching, RULE-SET placement, MATCH fallbacks, and how to fix rules that don’t work.

Rule priority: match first, then choose an outbound

Clash does not automatically sort rules by how specific their domain matches are. It checks them from top to bottom and stops at the first match. Exact domains, domain suffixes, and rule sets have no hidden ranking that makes exact rules take priority. For a maintainable list, put exceptions first, general rules next, and MATCH last.

Keep the two stages separate: a rule assigns a connection to a policy, and a policy group selects the outbound. If a rule matches “Work Policy” but that group currently selects DIRECT, the connection still goes direct. The rule is working; the group’s active selection just isn’t what you expected.

What happens when the order is reversed

rules:
  - DOMAIN-SUFFIX,example.org,Work Policy
  - DOMAIN,login.example.org,DIRECT
  - MATCH,Work Policy

When you visit login.example.org, the first suffix rule matches, so the exact-domain direct rule never gets a chance to run. Swap the two rules and the login subdomain will match DIRECT first. “More specific” is a useful way to organize your configuration, not a sorting algorithm the core applies for you.

Rule syntax and domain matching boundaries

A common rule uses the format type,match value,target policy. Commas separate the rule fields; the leading hyphen is YAML list syntax. Don’t confuse the two. The target can be a built-in policy such as DIRECT or REJECT, or the name of a node or policy group defined in your configuration.

Common rule types and what they match
TypeExampleWhat it matches
DOMAINDOMAIN,login.example.org,DIRECTMatches this exact domain only, not its subdomains.
DOMAIN-SUFFIXDOMAIN-SUFFIX,example.org,Work PolicyMatches the root domain and its subdomains, but not notexample.org.
DOMAIN-KEYWORDDOMAIN-KEYWORD,example,Work PolicyMatches a string in the domain; generally broader than a suffix rule.
IP-CIDRIP-CIDR,192.168.0.0/16,DIRECT,no-resolveMatches the subnet containing the target IPv4 address. An optional resolution-control parameter can be added at the end.
RULE-SETRULE-SET,work-domains,Work PolicyReferences a defined rule provider. If any applicable entry matches, the connection goes to the specified policy.
MATCHMATCH,Work PolicyRequires no match value; catches anything not matched by an earlier rule.

Don’t put a full URL in a domain rule

After DOMAIN-SUFFIX, use example.org, not https://example.org/path. The scheme, port, path, and query parameters are not part of a domain. Standard domain rules also can’t route /video through a proxy while sending /account direct on the same site. With HTTPS, ordinary traffic routing does not match against the full URL path.

Suffix matching respects domain-label boundaries. Both example.org and api.example.org match the suffix rule above, but example.org.other.net does not. To route a domain and its subdomains under one policy, prefer a suffix rule over a broad keyword that could catch unrelated sites with similar names.

RULE-SET priority comes from where you reference it

rule-providers declares rule sources; a RULE-SET entry in rules is what adds one to the matching flow. Downloading a rule file without referencing it in the main rule list won’t route any traffic. Conversely, referencing a provider name that doesn’t exist can cause configuration loading or rule initialization errors.

Example: a local mihomo domain rule set

This example uses a file-based rule provider. Its path is resolved relative to the core’s working directory, not necessarily the directory containing the configuration file open in your editor. In a desktop client, first check which working directory it actually uses. “Work Policy” must already exist in the current configuration’s proxy-groups or node list.

rule-providers:
  work-domains:
    type: file
    behavior: domain
    path: ./rules/work-domains.yaml

rules:
  - DOMAIN,login.example.org,DIRECT
  - RULE-SET,work-domains,Work Policy
  - MATCH,Work Policy

The rules/work-domains.yaml file contains:

payload:
  - '+.example.org'
  - 'api.example.net'

With this domain rule-set format, +.example.org covers the root domain and its subdomains, while api.example.net matches one exact domain. The login-domain exception still takes priority because it appears before RULE-SET. Reordering entries inside the rule file won’t change the priority of this external direct-connection exception.

Make sure behavior matches the file contents

  • behavior: domain: entries are domain match patterns. Don’t paste full three-field rules such as DOMAIN-SUFFIX,example.org,Work Policy into the file.
  • behavior: ipcidr: entries are IP ranges, such as 192.168.0.0/16, not domain lists.
  • behavior: classical: entries can be rule expressions such as DOMAIN-SUFFIX,example.org. The outer RULE-SET specifies the outbound policy, so don’t append it to every payload entry.

Remote providers also need a valid download URL, update settings, and a writable local storage path. A successful update only means the source was reachable; it doesn’t guarantee that the contents match the specified behavior. When troubleshooting, check both provider loading logs and match records—not just whether the “last updated” time changed.

Rule order: exceptions, general rules, fallback

The following is an example of a main rules list, not a complete subscription file. It assumes the work-domains provider and “Work Policy” from above are already defined. When merging it into an existing configuration, edit the existing rules list; don’t add a second top-level key with the same name at the end of the file.

mode: rule

rules:
  - DOMAIN,login.example.org,DIRECT
  - DOMAIN-SUFFIX,example.net,Work Policy
  - RULE-SET,work-domains,Work Policy
  - IP-CIDR,127.0.0.0/8,DIRECT,no-resolve
  - 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
  - MATCH,Work Policy

This example puts domain-based policies before private IP rules, so recognized business domains use Work Policy first. If your goal is to send all private addresses direct, move the private IP rules up—but also consider whether the destination IP is already known and whether a DNS lookup may be triggered. Rule order reflects real policy trade-offs; there’s no single list that fits every network.

no-resolve does not disable all DNS

At the end of an IP-CIDR rule, no-resolve means the rule won’t trigger a domain lookup just to test that IP rule. If a connection already has a destination IP, the core can still check its subnet. If it has only a domain and no destination IP is available, the rule won’t perform an extra lookup for its own match. This does not stop your browser from resolving domains or disable mihomo’s DNS module.

The IPv4 private-network example above doesn’t cover every LAN scenario. For IPv6 destinations, consider IP-CIDR6; single-label LAN hostnames also involve local DNS. TUN exclusion routes are configured at the traffic-capture layer, so a few DIRECT rules alone don’t prove that all local traffic bypasses the virtual interface.

Putting MATCH first cuts off later rules

MATCH has no filter condition. Put it on the first line and it catches every connection that reaches this list, leaving later rules no chance to run—even if their syntax is correct. Usually, keep one fallback at the end and specify which policy it uses. Don’t rely on implicit behavior when there’s no fallback to decide whether your configuration works as intended.

How to troubleshoot Clash custom rules

  1. Confirm traffic reaches the core. Only connections captured by the system proxy, an app proxy, or TUN can match rules. An app that bypasses the system proxy won’t start using Clash just because you added a domain rule.
  2. Confirm which configuration is active. Check the selected item on the client’s Subscription or Profiles page, then make sure the mode is Rule. Menu names vary by client, so don’t assume one client’s settings path applies to every product.
  3. Check the rules in the loaded configuration. Subscription merges, overrides, or scripts may append custom rules after MATCH. The position in the editor may not match the position in the final running configuration.
  4. Check names and indentation. Work Policy and Business Proxy are different names; provider keys must also match exactly. YAML uses spaces for indentation—don’t use tabs.
  5. Test with a new connection. Existing HTTP/2, HTTP/3, or WebSocket connections may keep using their original outbound. After applying the configuration, close the relevant connection or restart the target app, then test again.
  6. Compare the matched rule with the actual outbound. In the connection details, check the target host, rule type, rule content, and policy chain. Seeing the outbound node change doesn’t prove that the rule you expected was matched.

Start with a test that explicitly uses the proxy port

If your configuration explicitly sets mixed-port: 7890, use the command below to send a request through that HTTP proxy listener. Port 7890 is just an example, not a default for every client; use the value from your configuration. -q ignores your curl configuration file, and --noproxy "" prevents environment bypass settings from interfering with the test.

curl -q --noproxy "" --proxy http://127.0.0.1:7890 \
  --connect-timeout 10 --max-time 20 \
  --head https://example.org/

This command creates a test connection that’s easier to troubleshoot; it is not a performance benchmark. If the request fails, distinguish between a refused proxy connection, a DNS error, an outbound connection failure, and a remote HTTP response. A timeout alone doesn’t prove the rule syntax is wrong. The page’s response status is no substitute for checking the rule match record.

If curl matches the expected rule but the browser shows no connection for the same domain, first check whether the browser is using the proxy, reusing an old connection, or routing traffic through another extension. DNS caching can affect the destination address, but “clear the cache” is no substitute for checking traffic capture and the active configuration.

Subscription updates and rule maintenance

Direct edits to a subscription-generated configuration may be overwritten the next time it updates. A safer approach is to use the client’s supported override, merge, or scripting mechanism to keep a small set of exception rules locally. The details depend on the client; there are just two things to verify: that your custom rules are still present and that they appear before general rules and MATCH.

Test one target at a time

  • Start with an exact domain: Use one DOMAIN rule to confirm the rule path and policy name work, then switch to a suffix rule if you need broader coverage.
  • Then move it into a rule set: Once the single rule works, move it into a file that matches the relevant behavior. This helps separate syntax issues from source issues.
  • Handle exceptions last: Put exceptions such as login domains and corporate intranet sites before any general rules that would catch them. Don’t keep appending duplicate rules instead of fixing the order.
  • Recheck after updates: Reopen the final rule list, start a fresh connection to one exception domain and one ordinary domain, then verify the matched rule and outbound for each.

The key to Clash custom rules isn’t how many you have; it’s making sure the match scope, list order, and outbound behavior align. Confirm traffic reaches the core, check the final rule order, then check the policy group’s selection. That usually pinpoints issues faster than repeatedly switching nodes. For client-specific traffic capture and configuration imports, see the client setup guide; for rule types and core capabilities, consult the protocol handbook.