For users who can already connect to a node and want to customize traffic routing. Starting with the difference between domain and IP fields, this guide walks through rule order, outbound tags, and DNS resolution strategies using five example rules, then shows how to reorder and test them in v2rayN. The focus is on finding out which rule a connection matches—not guessing whether a setting works based on whether a webpage loads.
Routing rules only apply after a connection reaches the core
System proxy settings, TUN capture, and Xray routing operate at different stages. The system proxy determines whether apps that support it send connections to the local inbound. Routing rules then handle connections that have reached the core and choose whether to send them direct, through a proxy, or block them. If a browser bypasses the system proxy, even perfectly configured routing rules won't affect its connections.
This guide uses Xray's native JSON fields. v2rayN and v2rayNG convert their settings into core configuration; v2flyNG uses the V2Fly core, so don't assume every Xray field can be copied over as-is. Before editing, check which core you're using and back up your current routing profile.
domainis a rule field. Its array can contain matching expressions such asdomain:,full:,regexp:, andgeosite:.ipis a separate rule field that accepts IP addresses, CIDR ranges, andgeoip:collections. Don't mistake it for anip:prefix in a domain array.outboundTagreferences an outbound; it isn't a free-form note. Its value must match atagdefined for an outbound in the final configuration.
domain, full and regexp: narrow down what matches
Overly broad matching is a common cause of misrouted traffic. A bare string is treated as a substring match by Xray. To match a domain and all its subdomains, use domain: explicitly. Enter a hostname in the rule—not a full URL with a scheme, path, or query string.
| Syntax | What it matches | Boundary example |
|---|---|---|
example.com | Substring in a domain name | May also match notexample.com |
domain:example.com | The domain and all its subdomains | Matches example.com and img.example.com, but not notexample.com |
full:example.com | Exact match for the full domain | Doesn't include www.example.com |
regexp:^img[0-9]+\.example\.com$ | Regular expression match | Matches img12.example.com, but not img.example.com |
geosite:cn | References the cn collection in the domain data file | Results depend on the current data file; this doesn't mean all servers in mainland China |
Regular expressions also require JSON escaping. The raw expression in the routing editor can contain \.; in a JSON string, each backslash must be written as \\. The example below is a valid JSON rule object, not a complete configuration that can start the core on its own.
{
"type": "field",
"domain": [
"full:example.com",
"domain:example.net",
"regexp:^img[0-9]+\\.example\\.com$"
],
"outboundTag": "direct"
}
ip, geoip and geosite: different data sources
geosite matches domain collections; geoip matches IP address collections. A site may use multiple content delivery addresses, so its domain category and the geographic location of its actual connection address may differ. To always route a specific site through a proxy, add a domain exception rather than relying only on the IP's location.
{
"type": "field",
"ip": [
"192.168.0.0/16",
"10.0.0.0/8",
"geoip:private",
"geoip:cn"
],
"outboundTag": "direct"
}
The first two entries above show CIDR notation and overlap partly with geoip:private; you can remove the duplicates in practice. IPv4 private ranges alone don't cover local IPv6 addresses, so check whether the private address collection you're using covers both in a dual-stack network.
- Check that the collection exists: Before using
geosite:cnorgeoip:cn, make sure the core can read the relevant data file and that the file contains the collection. - Collection update timing: Updating the client configuration and updating geo data are separate operations. A successful subscription update doesn't mean the domain classifications have been updated.
- Extended collection syntax:
geosite:collection@attributeis only useful when the data file defines that attribute. Adding a suffix to an arbitrary collection won't make it work. - Symptoms: A missing data file or collection may prevent the configuration from loading. Check the startup log first instead of assuming every failure means the node can't connect.
Rule priority: top to bottom, first match wins
For each routing decision, Xray checks the rules array in order and uses the first rule that meets all its conditions. A more specific domain in a later rule won't make Xray choose that rule automatically. Put exact exceptions before broader categories, and leave catch-all rules until the end.
Arrays within the same field generally work as “OR”: a domain array matches if any expression matches. Different filter fields within one rule generally work as “AND”: setting domain, port, and network requires all those conditions to be met. Putting domain and IP conditions in the same rule does not mean “match either one to go direct.”
{
"routing": {
"domainStrategy": "IPIfNonMatch",
"rules": [
{
"type": "field",
"domain": ["geosite:category-ads-all"],
"outboundTag": "block"
},
{
"type": "field",
"domain": ["full:example.com"],
"outboundTag": "direct"
},
{
"type": "field",
"domain": ["geosite:cn"],
"outboundTag": "direct"
},
{
"type": "field",
"ip": ["geoip:private", "geoip:cn"],
"outboundTag": "direct"
},
{
"type": "field",
"network": "tcp,udp",
"outboundTag": "proxy"
}
]
}
}
This routing snippet illustrates rule order. It assumes the configuration already has three outbounds—direct, proxy, and block—and that the data file includes the example collections. These tag names aren't reserved action names: a typical direct outbound uses freedom, a blocking outbound uses blackhole, and a proxy outbound references the protocol of the actual node.
First decide whether the exception should override the block
If a domain must go direct even though it's included in an ad-blocking collection, move its exact-match direct rule above the ad-blocking rule. Putting it only above the cn collection isn't enough.
domainStrategy: when domains are checked against IP rules
Whether an IP rule can match a domain target depends on the routing domainStrategy. This field controls whether the domain is resolved for routing matches; it is not the client's overall DNS setting, nor does it determine how the direct outbound ultimately resolves the target address.
AsIs: keep the original target type
- Field
routing.domainStrategy- Value
AsIs- Domain target
- Don't actively resolve to an IP for routing matches
- IP target
- Can match IP rules directly
Useful for testing domain rules first, without mixing DNS resolution results into initial troubleshooting.
IPIfNonMatch: resolve only if nothing matches
- Field
routing.domainStrategy- Value
IPIfNonMatch- Trigger
- No rule matches the domain target in the first pass
- What happens next
- Resolve the domain, then try matching again, including IP rules
A catch-all rule at the end already matches in the first pass, so it won't trigger a second IP-based check.
So although the five rules in the previous section specify IPIfNonMatch, a domain target that reaches the final TCP/UDP catch-all has already matched in the first pass. Xray won't resolve it to check the earlier geoip:cn rule. A target that is already an IP can still match the fourth rule. This is a common reason a domain continues to use the proxy despite having a geoip rule.
- To allow a second IP-based check: Remove the explicit catch-all rule and make sure the first outbound used when no rule matches is the intended default.
- To resolve when an IP rule is checked: Consider
IPOnDemand. It attempts resolution when a rule needs IP information, but may involve DNS in routing more often. - To pin one site to an outbound: Add a
full:ordomain:rule near the top. This is usually easier to verify than changing the overall resolution strategy.
Also, if the inbound receives only the target IP, domain rules can't recover the original hostname on their own. Sniffing may supply domain information for recognizable traffic, but it isn't a universal way to recover domains for every protocol. When troubleshooting, check the inbound target, sniffing settings, and final outbound together.
Reorder rules and verify matches in v2rayN
In v2rayN, open “Settings” → “Routing Settings,” select the routing profile you want to use, and open the rule editor. The edit and move up/down controls may appear in different places across versions. Check the order of the generated routing.rules, not the profile name or rule notes.
- Copy the profile: Keep the original as a fallback, then add a
full:rule for a test domain to the copy. Don't change DNS, sniffing, or the core at the same time. - Enter fields separately: Put
full:example.comin the domain condition andgeoip:cnin the IP condition. If the editor accepts one entry per line, enter one match per line; don't paste a whole JSON object. - Check the outbound: Select the required outbound in the outbound options. For a native configuration, make sure
outboundTagexactly matches the outboundtag, including capitalization. - Reorder the rules: Move the test rule above any rules that could match first. After saving, confirm that this routing profile is selected, then reload or restart the core as prompted.
- Start a new connection: Close existing webpage connections and visit the site again. Existing long-lived connections usually won't switch outbounds automatically after a rule change; close and reopen the browser if needed.
- Check the logs: Make sure the core starts successfully, then check the test target's access log and outbound tag. If needed, temporarily increase log verbosity in the logging settings, then restore it after testing to avoid recording large volumes of connection data.
To test “HTTPS only goes direct,” set a domain condition, port: "443", and network: "tcp" in the same test rule. HTTP/3 uses UDP, so it won't meet the TCP condition. A browser opening the same site doesn't prove the rule is failing. The protocol and port must match your test conditions.
Why is www still proxied when I used full?
full:example.com doesn't include www.example.com. First, confirm the actual target in the logs. To include subdomains, use domain:example.com; if you only need these two hosts, add a full match for each.
Why doesn't the rule work at the end of the list?
Check for an earlier network: "tcp,udp" rule, a collection that includes the domain, or a broader domain match. Move the test rule above them, reload the configuration, and start a new connection.
Did a geo data update cause the core to fail to start?
Check the file and collection names mentioned in the startup log. Make sure the data file is readable and the collection exists. Remove the failing collection reference first to confirm the core starts, then fix the data source. Don't keep switching nodes to mask a configuration error.
Does a webpage loading prove that direct was used?
No. A page can load successfully over either a direct or proxied connection. Check the target and outbound tag in the core's access log. If the current log doesn't show the rule number, compare the outbound before and after the change, changing only one rule at a time.