Shadowrocket Rules Explained: What DOMAIN, GEOIP, IP-CIDR and FINAL Match

In a Shadowrocket rule list, every line is made up of three parts — type, value and policy — and matching runs from top to bottom, stopping at the first hit. This article breaks down what DOMAIN, DOMAIN-SUFFIX, DOMAIN-KEYWORD, GEOIP, IP-CIDR and FINAL each match, and gives copy-ready syntax plus a troubleshooting order.

At a glance

The rule list is plain text, one rule per line, written as type, value, policy. This article covers the domain rules (DOMAIN, DOMAIN-SUFFIX, DOMAIN-KEYWORD), the IP rules (GEOIP, IP-CIDR) and the catch-all (FINAL), explaining what each one matches and the order they are evaluated in, with examples you can copy straight into Config → Rules. It is written for users who have already imported a subscription and want traffic routed the way they intend; by the end you can write a rule list that does not fight with itself.

The three-part structure of a rule

The Shadowrocket rule list is plain text stored in the configuration file, one rule per line. Each line is split by an ASCII comma into three parts: type (TYPE), value (VALUE) and policy (POLICY). The type decides what is compared, the value decides what it is compared against, and the policy decides what happens once the rule matches.

There are only three policies: PROXY sends the connection through the currently selected server, DIRECT connects directly, and REJECT drops the connection. A whole line starting with # is a comment and is skipped when parsing; blank lines are ignored as well. The commas in a rule must be ASCII commas — a full-width comma invalidates the entire line.

# Format per line: type / value / policy
DOMAIN-SUFFIX,example.com,PROXY
DOMAIN-KEYWORD,analytics,DIRECT
IP-CIDR,192.168.0.0/16,DIRECT,no-resolve
FINAL,DIRECT

Of the three parts, the value is where mistakes happen most: for domain rules the value is a hostname, for IP rules it is a CIDR block or a two-letter country/region code, and for port rules it is a number. A malformed line raises no error — it is silently skipped. So when a rule seems to have no effect, check the commas and the value format on that line first, before suspecting anything else.

Domain rules: DOMAIN, DOMAIN-SUFFIX, DOMAIN-KEYWORD

Domain rules match the hostname in the request. For HTTPS requests the hostname usually comes from the SNI in the TLS handshake, and for HTTP requests from the Host header. Both are available as soon as the connection is established, with no need to wait for DNS resolution, so domain rules are evaluated fastest and are least affected by changes in resolution results. The three common forms get progressively broader:

TypeMatchesExampleScope
DOMAINThe exact hostname, character for characterDOMAIN,api.example.com,PROXYMatches api.example.com only; www.api.example.com does not match
DOMAIN-SUFFIXA domain suffix, including the domain itself and all subdomainsDOMAIN-SUFFIX,example.com,PROXYexample.com, a.example.com and a.b.example.com all match
DOMAIN-KEYWORDA keyword appearing anywhere in the hostnameDOMAIN-KEYWORD,analytics,DIRECTanalytics.example.com and cdn-analytics.example.net both match
DOMAIN-SETAn external domain list file, one domain per lineDOMAIN-SET,example-domains.txt,DIRECTDomains listed in the file follow the policy on that line

The broader the match, the higher the chance of catching something you did not intend. DOMAIN-KEYWORD,analytics,DIRECT matches any request whose hostname contains analytics anywhere, including sites you never meant to send direct; DOMAIN-SUFFIX,example.com,PROXY will also push mail.example.com and static.example.com through the proxy. Before writing a broad rule, confirm that no subdomain under that suffix needs to be treated differently.

When choosing a form, work through this order:

DOMAIN,www.example.com,DIRECT
DOMAIN-SUFFIX,example.com,PROXY
DOMAIN-KEYWORD,adservice,REJECT
DOMAIN-SET,example-domains.txt,DIRECT

IP rules: GEOIP and IP-CIDR compare the resolved address

IP rules ignore the domain name and look only at the IP address of the connection target. Getting that IP requires a DNS lookup first, so IP rules take effect one step later than domain rules — and they lose the domain dimension: when the same domain resolves to different addresses, the result can change with it.

GEOIP summarises where an address belongs with a two-letter country/region code; GEOIP,CN,DIRECT means that if the target IP is in China, connect directly. IP-CIDR takes an exact network block; the usual way to keep local networks direct is IP-CIDR,192.168.0.0/16,DIRECT and IP-CIDR,10.0.0.0/8,DIRECT; for IPv6 targets use IP-CIDR6. Port-based decisions are handled by DST-PORT and SRC-PORT — for example DST-PORT,443,PROXY sends connections whose destination port is 443 through the proxy.

GEOIP,CN,DIRECT
IP-CIDR,192.168.0.0/16,DIRECT,no-resolve
IP-CIDR,10.0.0.0/8,DIRECT,no-resolve
IP-CIDR6,fc00::/7,DIRECT,no-resolve
DST-PORT,443,PROXY

Rules for local network ranges usually need a trailing no-resolve: it tells the app not to trigger a DNS lookup just to obtain an IP when matching this rule. Without it, a request that could have matched immediately is forced through a resolution first, which slows the decision down and may fail if the lookup fails.

The takeaway: put domain rules before IP rules

GEOIP and IP-CIDR need the target IP first. If they sit above the domain rules, a request that DOMAIN-SUFFIX could have matched exactly is forced through resolution first, and the outcome depends on which address was resolved at that moment. Put domain rules first and IP rules after, and the order stays stable.

Match order: top to bottom, first match wins

Shadowrocket compares the rule list line by line from the top; as soon as a line matches, its policy is applied and the remaining rules are not evaluated. This is what drives the ordering principle: the more specific a rule and the higher its priority, the higher it should sit; the broader the rule, the lower.

FINAL is a catch-all rule that has only a policy and no value to match; it handles every request the rules above did not match: FINAL,DIRECT sends unmatched traffic direct, FINAL,PROXY sends it through the proxy. It must be the last line in the list — rules written after FINAL are never executed.

# 1. Local and reserved addresses go direct first
IP-CIDR,192.168.0.0/16,DIRECT,no-resolve
# 2. Domains you explicitly want to block
DOMAIN-SUFFIX,ads.example.com,REJECT
# 3. Domains you explicitly want proxied
DOMAIN-SUFFIX,example.com,PROXY
# 4. Mainland China addresses go direct
GEOIP,CN,DIRECT
# 5. Catch-all
FINAL,PROXY

When ordering, you can stack five layers from top to bottom:

Making rules take effect: Global Routing and the configuration file

The rule list is only evaluated when Global Routing is set to Config. You switch modes in Settings → Global Routing; the three common options differ as follows:

Config

Recommended

Matches line by line against the rule list in the configuration file: a PROXY hit goes through the proxy, a DIRECT hit connects directly, a REJECT hit is dropped.

Best for: everyday use, when you want the rules to actually do their job

Proxy

Ignores the rule list; every connection goes through the currently selected server.

Best for: temporarily proxying everything, or troubleshooting rule problems

Direct

Ignores both the rule list and the server; every connection goes out directly.

Best for: checking whether a problem is introduced by the proxy

The other prerequisite is editing the right file. The Config tab can hold several configurations at once, and only the currently selected one (with a checkmark) is used. To edit, go to Config → select the configuration in use → Rules; the list shows one rule per line, the + button in the top right adds a line, and tapping any line lets you change its type, value and policy. The full flow is:

  1. Confirm the mode

    In Settings → Global Routing, choose Config; otherwise the rule list is not evaluated.

  2. Open Config

    Switch to the Config tab at the bottom and confirm the checkmark is on the configuration file you are actually using.

  3. Open Rules

    Tap into the configuration file and find the Rules row — that is the full rule list.

  4. Add a new line

    Use the + button in the top right to add a rule, filling in the three parts type, value and policy, with ASCII commas.

  5. Reorder

    Move specific rules above broad ones and keep FINAL as the last line.

  6. Save and reconnect

    After saving, go back to Home, disconnect and connect again so the new rule list takes effect.

The takeaway: set the mode first, then worry about rules

When a rule does not seem to work, first confirm that Global Routing is set to Config and that the selected configuration file is the one you edited, and only then check rule order and syntax. Checking in the opposite order usually means a long detour through syntax that was never the problem.

Four common questions

These four questions cover the most common rule-related reports, and the troubleshooting path always works down through mode → configuration file → rule order → rule syntax.

I added DOMAIN-SUFFIX,example.com,PROXY, but the domain still connects directly?

First check whether an earlier rule matches first — for example a GEOIP,CN,DIRECT placed above the domain rules, or a broad DOMAIN-KEYWORD. Then confirm Global Routing is set to Config and that the selected configuration is the one you edited, and finally disconnect and reconnect once.

Why does GEOIP,CN,DIRECT not send sites in mainland China direct?

GEOIP matches the resolved IP, not the domain. If it sits above the domain rules, or the address the target resolves to is not in the CN block, the rule will not match. Move it below the domain rules and above FINAL, then reconnect and watch again.

Why does an IP-CIDR rule not match the domain I wrote?

IP-CIDR compares IP addresses only and does not know about domains. To route by domain, use DOMAIN-SUFFIX; to route by network block, confirm that the address the target resolves to really falls inside the CIDR you wrote. When writing local network ranges, remember to add no-resolve.

I wrote REJECT, but some requests still go out?

Rules are matched top to bottom and the first match wins. If a PROXY or DIRECT rule above the REJECT already matches the same target, the request never reaches that line. Move the REJECT above the matching domain rule and try again.

App Store verification