Clash Custom Rules: DOMAIN-SUFFIX, GEOIP, and MATCH Order

Learn common Clash rule syntax, how rules are evaluated from top to bottom with the first match taking priority, and how to use rule-providers and resolve conflicts.

Check the rule mode first, then adjust the order

Put specific exceptions first, broader rules later, and use MATCH last to catch any remaining connections. Clash checks the regular rule list from top to bottom and applies the policy for the first matching rule. It does not keep searching for a “more specific” rule. A more specific rule does not automatically get higher priority.

Before making changes, confirm that the client is in Rule mode. Global mode does not use this regular routing list to choose an outbound route, and Direct mode cannot be used to verify proxy rules. The label may vary by client; select “Rule” in the mode switcher and confirm that the active configuration contains mode: rule.

Understand rules, policies, and the actual outbound route

  • Rule: Checks whether a connection meets a condition, such as a domain ending in example.com.
  • Policy: Determines how a connection is handled, such as by the built-in DIRECT or REJECT policies, or a policy group named PROXY.
  • Actual outbound route: The node or direct option currently selected in a policy group. Matching PROXY does not necessarily mean traffic goes through a remote node; check the selection within the group.

Here, PROXY is a custom group name, not a built-in policy included in every configuration. If your subscription names the group “Node Selection,” use that exact name in the rule. A name mismatch, different capitalization, or a missing group can prevent the configuration from loading.

Know the difference between DOMAIN, DOMAIN-SUFFIX, and IP rules

Choose a rule type based on the information you want to match. Domain rules work well for routing websites and services; IP rules are useful for fixed addresses, local networks, and IP geolocation. The usual format is TYPE,match value,policy. MATCH has no match value, so it uses just two fields.

Type Example rule What it matches
DOMAIN DOMAIN,api.example.com,DIRECT Matches this exact domain only
DOMAIN-SUFFIX DOMAIN-SUFFIX,example.com,PROXY Matches the root domain and its subdomains
DOMAIN-KEYWORD DOMAIN-KEYWORD,example,PROXY Matches domains containing this string
IP-CIDR IP-CIDR,192.168.0.0/16,DIRECT,no-resolve Matches the specified IPv4 range
GEOIP GEOIP,CN,DIRECT Uses IP data to determine whether the address is in CN
MATCH MATCH,PROXY Catches connections not matched by earlier rules

Use only the hostname in the domain field

Write DOMAIN-SUFFIX,example.com,PROXY, not https://example.com/path. Do not include a protocol, path, query string, or port, and do not add *.example.com. The suffix rule already covers example.com and www.example.com; it will not match notexample.com just because the strings look similar.

DOMAIN-KEYWORD has a broader scope. The keyword example may match domains unrelated to the site you want to route. Use it only when substring matching is actually needed; for website routing, prefer an exact domain or domain suffix.

The difference between IP-CIDR and GEOIP

IP-CIDR checks whether an address falls within a subnet; GEOIP relies on the geolocation data loaded by the core. GEOIP checks only the location associated with the destination IP, not the page content, domain registration location, or visitor’s location. A Chinese-language site using an overseas CDN may not match GEOIP,CN.

For IPv6 ranges, use the corresponding IP-CIDR6 rule, such as IP-CIDR6,fc00::/7,DIRECT,no-resolve. Adding an IPv4 exception alone does not mean IPv6 connections to the same service will use the same route.

Order specific exceptions and fallback rules by first match

Consider this specific conflict: route api.example.com directly, while proxying other example.com subdomains. The exact-domain exception must come before the suffix rule.

rules:
  - DOMAIN,api.example.com,DIRECT
  - DOMAIN-SUFFIX,example.com,PROXY
  - GEOIP,CN,DIRECT
  - MATCH,PROXY
  1. For api.example.com, the first rule matches and selects DIRECT.
  2. For www.example.com, the first rule does not match, so the second one does and routes it to PROXY.
  3. For other domains, continue checking the IP location; if it matches CN, route directly.
  4. If none of the earlier rules match, the final MATCH rule routes the connection to PROXY.

If you swap the first two rules, the suffix rule catches api.example.com first, and the exact-domain rule never runs. Putting MATCH first is even more direct: none of the regular rules below it will get a chance to match.

Use a loadable minimal configuration to verify names

This configuration structure demonstrates how the parts fit together and does not include any remote proxy nodes. To make PROXY a valid reference, the example defines a selection group with that name and temporarily adds only DIRECT to it. So even when a rule matches this group, the final route is still direct. In real use, keep the nodes and policy groups already present in your subscription.

mixed-port: 7890
mode: rule
log-level: info

proxy-groups:
  - name: PROXY
    type: select
    proxies:
      - DIRECT

rules:
  - DOMAIN,api.example.com,DIRECT
  - DOMAIN-SUFFIX,example.com,PROXY
  - IP-CIDR,192.168.0.0/16,DIRECT,no-resolve
  - GEOIP,CN,DIRECT
  - MATCH,PROXY

mixed-port: 7890 is simply the local mixed HTTP/SOCKS proxy port in this example, not a rule-matching condition. If your client uses a different port, test against the actual listening port. When editing an existing configuration, do not add a second top-level rules: key; modify the existing list or use the client’s merge feature.

How GEOIP, DNS, and no-resolve work together

Let domain rules handle clear matches first, then consider whether IP-based checks are needed. If a connection has a domain but no destination IP yet, an IP rule may trigger DNS resolution. Placing common domain exceptions before GEOIP makes your routing intent clearer and can avoid unnecessary IP checks.

no-resolve does not disable DNS

Append no-resolve to an IP rule that supports it to prevent that rule from actively resolving a domain just to check for a match. If the connection already has a usable IP, the rule can still evaluate it. This does not disable system DNS or guarantee that the connection will never be resolved later in processing.

rules:
  - DOMAIN-SUFFIX,example.com,PROXY
  - IP-CIDR,192.168.0.0/16,DIRECT,no-resolve
  - GEOIP,CN,DIRECT,no-resolve
  - MATCH,PROXY

This configuration may behave differently from one without no-resolve. If no destination IP is available, GEOIP with this option may not match, leaving the request to fall through to MATCH. Do not add this option mechanically to every IP rule just to reduce DNS lookups.

Domain rules cannot match a domain the core never receives

An app may connect directly to an IP address or perform its own encrypted DNS resolution. In that case, the connection record may not include the original domain. TUN captures network traffic, but that does not guarantee every request’s domain will be preserved. A correctly configured Fake-IP mapping or sniffing can help recover domain information in supported cases, but not every protocol can be assumed to support it.

  • First, check the hostname and destination IP in the connection details to see what information the core actually has.
  • Next, check that DNS requests are reaching the core as expected, rather than taking separate resolution paths through the app and system.
  • Finally, adjust DNS or sniffing settings. Do not try to fix missing domain information by continually adding more domain rules.

Split rule files with rule-providers

Move rules that you maintain repeatedly into a separate file, then reference them with RULE-SET. rule-providers declares where the rules come from; the reference position in rules determines their priority. Declaring a provider without referencing it does not make its rules part of routing.

Declare a local rule set in the main configuration

The example below uses a local file provider supported by mihomo. The file path is resolved relative to the core’s working directory. Clients may use their own configuration directory, so confirm the core’s working directory before creating rules/local-direct.yaml.

rule-providers:
  local-direct:
    type: file
    behavior: classical
    format: yaml
    path: ./rules/local-direct.yaml

rules:
  - DOMAIN,api.example.com,PROXY
  - RULE-SET,local-direct,DIRECT
  - GEOIP,CN,DIRECT
  - MATCH,PROXY

Put match conditions only in the rule file

Save the following content to the path above. The classical format supports rule conditions such as domains and subnets. Do not include a final policy on each line; the main configuration already specifies it in RULE-SET,local-direct,DIRECT.

payload:
  - DOMAIN-SUFFIX,example.com
  - DOMAIN,intranet.example.net
  - IP-CIDR,192.168.0.0/16,no-resolve

With this ordering, api.example.com matches the exact rule in the main configuration first and is routed to PROXY; other connections that match the rule set go to DIRECT. If you move RULE-SET to the top, its suffix condition catches the API domain first.

The content formats for behavior: domain, ipcidr, and classical differ. Changing the declaration while keeping incompatible file contents will not work. Before using someone else’s rule set, check its format, path, and core compatibility, then reload and review the logs. Remote providers also depend on download and update status; if a download fails, troubleshoot the provider rather than assuming the rule order is wrong.

Find rule conflicts in connection logs

Start a new connection to the same destination and note three things: the matched rule, the policy chain, and the actual outbound route. Do not rely only on whether the page loads to judge if a rule works; caching, existing connections, and policy group selections can make the result appear unchanged.

  1. Confirm traffic capture: Open the client’s connection list and make a request. If no connection appears, check the system proxy, app proxy, or TUN capture before changing rules.
  2. Confirm the mode: Make sure the client is in Rule mode and that the configuration you edited is active.
  3. Find the first matching rule: Check the rule type and match value in the connection details or logs, then locate it in the main configuration or rule set.
  4. Check the policy chain: Confirm the current selection within the group. For example, if the rule matches PROXY but the group selects DIRECT, the actual route is still direct.
  5. Start a new connection: Save and reload the configuration, close any existing connections to the test destination, and try again. Existing connections usually do not switch routes automatically when rules change.

Test through a specific local port

If the mixed proxy listens on 127.0.0.1:7890, use the command below to send a request through that port. Choose a real service you want to test; the reserved example domain here illustrates the command format only and says nothing about your proxy’s performance.

curl --proxy http://127.0.0.1:7890 --connect-timeout 10 --max-time 20 -I https://example.com

To test over SOCKS and let the proxy resolve the domain, use socks5h. This may provide different domain information than a test that resolves the domain locally, so note the protocol used when comparing results.

curl --proxy socks5h://127.0.0.1:7890 --connect-timeout 10 --max-time 20 -I https://example.com
Symptom Check first What to do
Every connection matches MATCH The position of MATCH, whether the destination has a domain, and whether earlier rules loaded Move the fallback to the end; check the active configuration and connection metadata
Exact-domain exception has no effect Earlier suffix, keyword, or RULE-SET rules Move the specific exception ahead of any rule that covers it
Matches a proxy group but still routes directly The group’s current selection and nested policy chain Select the desired outbound route, then start a new connection
Configuration reload fails YAML indentation, policy names, rule set path, and format Use the error log to locate the field; restore a working configuration, then change one thing at a time

Keep custom rules after subscription updates

Prefer persistent overrides, merge options, or custom rule features explicitly supported by the client. Direct edits to a configuration generated from a downloaded subscription may be overwritten the next time it updates. Menu names and workflows vary by client; first check whether the feature edits the source configuration, generates a configuration, or changes only the current runtime configuration.

Check the active configuration, not just the editor

  • Check where rules are inserted: “Prepend rules” is suitable for specific exceptions. Rules appended after MATCH will never get a chance to match.
  • Check how rule arrays are handled: Some merge methods append to rules; others replace the entire list. Test the behavior with a single rule first.
  • Check that names stay consistent: If a subscription update changes a policy group name, update your local rules too to avoid referencing a nonexistent policy.
  • Check provider status: The main configuration reference, provider file, and path must all be valid; any one of them being missing or invalid can prevent loading.

Change one type of rule at a time and keep a working configuration from before and after each change. Test one exact domain first, then expand to suffix rules or a rule set. This makes it easier to tell whether a rule did not match, selected the wrong policy, or routed through an unavailable outbound connection—and to recheck after subscription updates.

Download the latest Clash version