For users who have already imported nodes and want precise control over direct, proxied, and blocked traffic. You’ll learn to distinguish full hostnames, root domains, keywords, CIDR ranges, and geosite categories, understand top-to-bottom first-match behavior, and verify a configuration with a fallback rule.
What routing rules actually handle
The V2Ray or Xray routing module does not change the node protocol or increase the node’s bandwidth. It receives the connection’s destination information, then decides which outbound should handle it. Common outbound tags are proxy, direct, and block, meaning send the connection through a proxy node, connect directly, or reject it. Tag names are customizable, but outboundTag in a rule must exactly match the outbound configuration’s tag, including letter case.
A request can provide more matching data than just a domain. The destination may also include an IP address, port, and network type; some clients can provide a process name or inbound tag as well. This article focuses on the three conditions most often confused: domain, ip, and geosite. geosite is actually written inside the domain array: it references a prebuilt domain category, rather than being a standalone rule field.
Routing follows one core principle: “top to bottom, first match wins.” The core reads the rule list in order. Once every condition in a rule is satisfied, it uses that rule’s outbound and stops checking the rules below it. So if a domain belongs to both an advertising category and a regional category, an earlier blocking rule takes effect before a later direct-connection rule.
| Rule conditions | Information read | Typical use | Key limitation |
|---|---|---|---|
domain |
Request destination domain | Route a specific site directly or through a proxy | Cannot match when the destination is initially available only as an IP |
geosite: |
Domain category data | Route traffic in bulk by region or purpose | Depends on a local category data file |
ip |
Destination IP or resolved address | Route private networks, regional IPs, and CIDR ranges | Whether domain requests are resolved depends on policy |
port |
Destination port | Limit matching to ports such as 53, 80, and 443 | Combined with other fields in the same rule using AND logic |
How domain and geosite syntax differ
The domain array supports several prefixes. Rather than writing every address as a plain string, first decide whether you need an exact host, an entire root domain, or sites whose names contain a particular string. A scope that is too broad sends unrelated domains through the same outbound; one that is too narrow may miss static assets, APIs, or login subdomains.
Full domain matching
- Syntax
- full:api.example.com
- Matches
- api.example.com
- Does not match
- www.example.com
- Scope
- Single hostname
Best when changing routing for one API or host without affecting other subdomains on the site.
Root-domain matching
- Syntax
- domain:example.com
- Matches
- example.com
- Matches both
- cdn.example.com
- Scope
- Root domain and subdomains
Best for sending an entire site through one outbound; this is the most common form for hand-written rules.
Keyword matching
- Syntax
- example
- Method
- String contains
- Scope
- Every domain containing the string
- Risk
- Can expand the match scope unexpectedly
Use only when fuzzy matching is genuinely needed, and check short terms for false positives first.
Category-list matching
- Syntax
- geosite:cn
- Source
- Local domain category data
- Method
- Bulk-matched entries
- Maintenance
- Updated with the data file
Useful for broad traffic splitting, but custom domains should still come before category rules.
regexp: accepts regular expressions, such as regexp:^([a-z0-9-]+\.)*example\.com$. It can describe more complex hostname structures, but costs more to maintain than domain:. If you only need the root domain and all subdomains, domain:example.com is clearer and less likely to fail because of an escaping mistake.
geosite:cn references the domain category named cn, while geosite:category-ads-all is commonly used to match advertising-related domains. Category contents come from the data file used by the client; the rule itself stores only the category name. After upgrading the core, if logs report that a category is missing, check that the geosite data is complete and that the category name is supported by the current data version.
{
"type": "field",
"domain": [
"full:api.example.com",
"domain:static.example.com",
"geosite:category-ads-all"
],
"outboundTag": "block"
}
How ip, CIDR, and domainStrategy work together
The ip field accepts a single address, a CIDR range, or a geoip category. In CIDR notation, the number after the slash indicates the network prefix length. For example, 192.168.0.0/16 covers addresses from 192.168.0.0 through 192.168.255.255, while 10.0.0.0/8 covers the entire private range beginning with 10. For home routers, storage devices, and LAN services, geoip:private is usually easier than listing ranges one by one.
Whether a domain request can reach an IP rule depends on routing’s domainStrategy. With AsIs, the routing module uses the original destination and does not proactively resolve the domain for IP matching. With IPIfNonMatch, it resolves the IP after domain rules fail, then tries IP rules. With IPOnDemand, encountering a rule that requires the destination IP may trigger resolution during matching.
- Route by domain only: choose
AsIsto avoid extra resolution during routing. - Domain first, IP as a fallback: choose
IPIfNonMatchso hand-written domains and geosite categories decide the route first. - Earlier rules depend on IP data: consider
IPOnDemand, and confirm that DNS can return usable results. - LAN traffic must stay direct: place the
geoip:privaterule before regional IP rules and the final proxy fallback.
{
"routing": {
"domainStrategy": "IPIfNonMatch",
"rules": [
{
"type": "field",
"ip": [
"geoip:private",
"192.168.0.0/16",
"10.0.0.0/8"
],
"outboundTag": "direct"
},
{
"type": "field",
"ip": [
"geoip:cn"
],
"outboundTag": "direct"
}
]
}
}
A common mistake is putting both domain:example.com and geoip:cn in one rule. The intended meaning may be “send it direct if either the domain or regional IP matches,” but the actual meaning is “send it direct only when the domain belongs to example.com and its resolved address belongs to that IP category.” To express OR logic, split them into two rules and point both to the same outboundTag.
Conclusion: put domain rules first and use IP rules to catch the rest
Start with full, domain, and geosite to express clear intent, then place geoip and CIDR rules afterward. With IPIfNonMatch, only requests that miss domain rules proceed to IP matching, making the order easier to predict.
Match priority and a ready-to-use rule order
Rule priority is not determined by whether the condition uses domain, ip, or geosite, and there is no mechanism that automatically ranks “exact rules” above category rules. The only reliable priority is the order of the array. Place custom exceptions before broad categories, put blocking rules before direct-connection or proxy categories that could otherwise override them, and finish with fallback rules covering both TCP and UDP.
The example below is ordered as custom direct routes, ad blocking, private-address direct routes, regional domain direct routes, regional IP direct routes, and proxy everything else. Before using it, confirm that the outbound tags are actually direct, block, and proxy. If your subscription template uses different tags, replace only the tag values; do not casually change the condition order.
{
"routing": {
"domainStrategy": "IPIfNonMatch",
"rules": [
{
"type": "field",
"domain": [
"full:portal.example.com",
"domain:intranet.example"
],
"outboundTag": "direct"
},
{
"type": "field",
"domain": [
"geosite:category-ads-all"
],
"outboundTag": "block"
},
{
"type": "field",
"ip": [
"geoip:private"
],
"outboundTag": "direct"
},
{
"type": "field",
"domain": [
"geosite:cn"
],
"outboundTag": "direct"
},
{
"type": "field",
"ip": [
"geoip:cn"
],
"outboundTag": "direct"
},
{
"type": "field",
"network": "tcp,udp",
"outboundTag": "proxy"
}
]
}
}
| Order | Rule purpose | Why it goes here |
|---|---|---|
| 1 | Hand-written exception | Override the default decision made by later categories |
| 2 | Explicit blocking | Prevent the request from being captured first by a regional direct category |
| 3 | Private address | Keep routers and LAN devices directly accessible |
| 4 | Regional domain | Decide by domain category first and reduce dependence on resolution |
| 5 | Regional IP | Handle destinations with no domain or no category entry |
| 6 | Final fallback | Give unmatched connections a deterministic exit |
A fallback rule is not required in every core configuration, since unmatched connections may still use the default outbound. An explicit fallback is easier to read and migrate, however. When moving to another configuration, the last rule tells you where uncategorized traffic goes, so you do not have to guess which item comes first in the outbound array.
Conclusion: exceptions must come before categories
If a site must always use a proxy, place its full or domain rule before geosite:cn. If it comes later, the earlier category will match first and the core will never reach the exception.
Enter and verify rules in v2rayN
Using the v2rayN 7.12.x desktop interface as an example, open “Settings” → “Routing settings,” copy the rule set currently in use, and edit the copy. Copying instead of overwriting makes it easy to revert immediately if parsing fails. Button locations may shift between minor versions, but the fields to verify remain the rule order, domain list, IP list, destination ports, and outbound tags.
After finishing, select the new rule set and restart the core. Then open “Settings” → “Parameter settings” and confirm the local listening port. The common local port is 10808, but use the value shown in the current interface; if the port has changed, update the test command too. If the system proxy is enabled, visit one custom direct domain, one proxied domain, and one LAN address separately, then check whether each request reaches the expected outbound.
- Prepare four types of test targets: a custom exception, a geosite category, an IP range, and an uncategorized domain.
- Run three consecutive tests for each type, recording 12 requests in total to rule out caching or intermittent resolution failures.
- In the live log, verify the destination address, matched outbound tag, and reason for failure.
- Restart the core after changing rules; do not use an existing connection to judge whether the new rules work.
- For a LAN test, enter the device IP directly, such as
192.168.1.1, and confirm that it matches the private direct rule.
curl --proxy socks5h://127.0.0.1:10808 https://example.com
curl --proxy socks5h://127.0.0.1:10808 https://www.example.org
In socks5h, the h means the proxy handles domain resolution. This helps the core see the original domain and apply domain or geosite rules. If you instead resolve locally and send only the IP to the proxy, the log may show only the destination IP, so domain rules naturally cannot match.
Common problems and how to diagnose them
When the rules look correct but the result is wrong, the cause is usually not syntax itself. The active configuration may not have enabled the rules, the destination information may differ from expectations, or an earlier rule may already have matched. During troubleshooting, do not repeatedly reorder every rule. Narrow the issue to one target domain first, then use the log to confirm whether the core actually received a domain or an IP.
Why didn’t my domain rule match?
First check whether the destination in the log has already become an IP. If the test tool resolved it locally, switch to a method that passes the domain to the proxy. Also confirm that the rule appears before the geosite category and final fallback.
What if a geosite category cannot be found?
Check the data directory read by the current core and the geosite data file, then verify the category spelling. After upgrading the core, update the corresponding data as well and restart the core to load it again.
What if an internal site is sent through the proxy?
Add a geoip:private direct rule before the proxy fallback. If you use custom ranges, add the actual CIDR as well, such as 172.16.0.0/12, and check whether a broader forced-proxy rule appears earlier.
Is putting domain and IP in the same rule more accurate?
Only do this when both conditions genuinely need to be true. To express “domain or IP,” split them into two adjacent rules and use the same outbound tag for both.
Why does the site still use the old outbound after saving the rules?
First confirm that the new rule set is selected, then restart the core and create a new connection. Browser connection reuse can preserve the old outbound, so close the relevant pages, reopen them, and check the log again.
Finish with one order check: are hand-written exceptions before categories, blocking rules before broad direct routes, private addresses before the proxy fallback, and domainStrategy consistent with a domain-first design? If these four points are directly visible in the configuration, later additions are much less likely to break existing traffic splitting.
- Start with a
full:rule for one target and confirm that the outbound tag works. - Expand it to
domain:, then test the root domain and two subdomains. - Add the geosite category while keeping custom exceptions before it.
- Finally enable geoip, CIDR, and TCP/UDP fallbacks, checking the logs layer by layer.