This guide is for desktop users who have imported nodes and are ready to adjust routing. Start by distinguishing domain rules from IP rules, put specific exceptions before broader rules, then verify the outbound route in the logs. Examples follow Xray/V2Fly routing semantics. v2rayN’s GUI generates the configuration; you can’t paste a complete core JSON config into a single domain field.
1. Start with Rule Evaluation Order
The system proxy determines whether an app sends requests to v2rayN; routing rules determine which outbound route the core uses after receiving a request. These are separate controls. If an app bypasses the system proxy and connects directly, changing domain rules won’t affect it. With TUN mode, also verify that traffic is actually reaching the core.
Xray/V2Fly normally checks routing rules in array order. The first matching rule determines the outbound route. Rules aren’t automatically ranked by specificity, and full: doesn’t take precedence over domain:. If the first rule sends domain:example.com through the proxy, a later direct exception for full:files.example.com won’t take effect.
| Position | Rule purpose | Example outbound route |
|---|---|---|
| First | Specific domain exceptions or destinations to block | direct / block |
| Middle | Service domains, private-network IPs, and geographic groups | proxy / direct |
| Last | Fallback for remaining TCP and UDP requests | proxy or direct, as needed |
Multiple entries in a domain array within one rule usually work as OR conditions; the same is generally true for entries in an ip array. Different condition fields are combined with AND. For example, putting a domain, destination port, and network type in one rule means all three conditions must match—not just any one of them.
2. How to Use domain, full, and regexp
Domain rules take a hostname, not a protocol, path, or query string. For https://api.example.com:443/v1/status, enter api.example.com as the domain condition. Port 443 is a separate condition, and the path /v1/status can’t be matched by this type of routing rule.
| Pattern | What it matches | Common pitfall |
|---|---|---|
domain:example.com | Matches the root domain and its subdomains | Matches api.example.com, but not notexample.com |
full:api.example.com | Matches only this exact hostname | Doesn’t match v2.api.example.com |
regexp:^api[0-9]+\.example\.com$ | Matches using a regular expression | Escape dots; use anchors to match the entire hostname |
example | Without a type prefix, matches by substring | Broader than suffix-domain matching and may match unintended hosts |
domain: isn’t a wildcard expression. To cover a domain and its subdomains, use domain:example.com; don’t use domain:*.example.com. Use full: when only one hostname should use a specific route. Consider a regular expression only when you need to match hosts with a consistent naming pattern.
{
"type": "field",
"domain": [
"full:files.example.com",
"regexp:^api[0-9]+\\.example\\.com$"
],
"outboundTag": "direct"
}
The snippet above is an inner-core JSON fragment, so backslashes are written as \\. In a regular v2rayN domain text field, you’ll usually enter a single backslash: regexp:^api[0-9]+\.example\.com$. Pasting JSON-escaped text into a regular text field can change the meaning of the expression. First check whether you’re editing JSON or a rule field.
Bottom line: Start with full, then broaden the match
When adding a service exception, start by testing the outbound route with a single full: hostname. Once it matches as expected, broaden it to domain:. Use regexp: only when pattern matching is actually needed—it’s easier to track down misrouted traffic this way.
3. IP, CIDR, and geoip: What They Match
IP conditions go in the ip field and commonly use a single address, a CIDR range, or a geoip: group. This matches the destination IP. Don’t enter the source device’s private-network address as a destination rule. Routing by source uses different conditions; the destination IP field isn’t a substitute.
{
"type": "field",
"ip": [
"192.168.50.0/24",
"203.0.113.10/32",
"2001:db8::10/128",
"geoip:private"
],
"outboundTag": "direct"
}
192.168.50.0/24 covers this IPv4 subnet. 203.0.113.10 and 2001:db8::10 are documentation example addresses, not connectable nodes. Replace them with the destination ranges you need to route. For direct access to your local network, start with explicit ranges, then add geoip:private if needed.
geoip:cn matches against the loaded IP data set; geoip:private covers private or local address ranges defined by that data set. The contents can change when the data files are updated, so don’t treat a group rule as a permanent statement about a location. CDNs, cross-region services, and newly allocated ranges may need individual exceptions.
- When the destination is a domain, whether an IP rule can match depends on whether the core gets the destination address and on the routing resolution strategy.
- When the destination is already an IP, there’s no need to resolve a domain just to match an IP rule.
- A service may use both IPv4 and IPv6. An IPv4-only rule won’t cover its IPv6 connections.
- Before routing all private destinations directly, check what access your network requires. Direct routing doesn’t bypass your local firewall.
4. Geosite Data Sets and DNS Strategy
geosite:cn is a domain group; geoip:cn is an IP group. They aren’t interchangeable. The former doesn’t mean a domain resolves to an IP in mainland China, and the latter doesn’t mean a domain belongs to a mainland China service. Use domain groups to classify services and IP groups to classify addresses to avoid confusing the two.
{
"type": "field",
"domain": [
"geosite:cn"
],
"outboundTag": "direct"
}
Traditional Xray/V2Fly configurations require the core to locate the data files referenced by these groups. Common filenames are geosite.dat and geoip.dat. Available group names depend on the data actually loaded. Don’t assume a category exists just because it’s mentioned in an article. Missing data files or group labels can prevent the config from loading.
| domainStrategy | Routing resolution behavior | What to check |
|---|---|---|
AsIs | The routing stage doesn’t resolve domains for IP-rule matching | A domain request missing an IP group doesn’t necessarily mean the rule syntax is wrong |
IPIfNonMatch | If no domain rule matches, try resolving the domain and matching again | A broad rule that matches too early may prevent this second pass |
IPOnDemand | Try resolving the domain when matching requires a destination IP | Check that DNS works and returns the expected results |
Pay particular attention to how IPIfNonMatch interacts with a fallback rule: if the final network: tcp,udp rule sends the request through the proxy during the first pass over domain rules, the core won’t retry just because the earlier geoip:cn rule didn’t match. To route by IP group, plan for the resolution strategy and default route as well—you can’t just add an IP rule and expect it to work.
5. Proxy, Direct, and Block: Example Configuration
This snippet first blocks a specific hostname, then sends a file host directly, and routes other requests under the same domain through the proxy. Private-network destinations go direct; remaining TCP and UDP requests use the proxy. The example uses IPOnDemand so domain destinations can also be checked against IP conditions. Before using it, make sure the core’s DNS configuration works.
{
"routing": {
"domainStrategy": "IPOnDemand",
"rules": [
{
"type": "field",
"domain": ["full:telemetry.example.com"],
"outboundTag": "block"
},
{
"type": "field",
"domain": ["full:files.example.com"],
"outboundTag": "direct"
},
{
"type": "field",
"domain": ["domain:example.com"],
"outboundTag": "proxy"
},
{
"type": "field",
"ip": ["192.168.50.0/24", "geoip:private"],
"outboundTag": "direct"
},
{
"type": "field",
"network": "tcp,udp",
"outboundTag": "proxy"
}
]
}
}
This is a routing snippet, not a complete node configuration. proxy, direct, and block must match the tag values in the actual outbounds. A tag is only a name; it doesn’t create an outbound. Direct routing usually uses a freedom outbound, blocking usually uses a blackhole outbound, and the proxy route must use an existing node.
Put specific exceptions first
RecommendedPut individual hosts and explicit IP ranges before broad categories to make the scope of each change easy to review. When adding an exception, check that no earlier rule can match it first.
Best for: Adding a few service exceptions to an existing everyday routing setup
Proxy fallback
Send requests not covered by direct or block rules through the proxy. The scope is clear, but the proxy node must handle more requests; exclude private-network destinations first.
Best for: Proxying most external services while maintaining a direct-access list
Direct fallback
Proxy only the services listed explicitly and send everything else directly. New services and forgotten domains won’t use the proxy unless you keep the list up to date.
Best for: A small, stable set of proxy destinations with direct access as the default
When no routing rule matches, Xray/V2Fly typically uses the first outbound in the list. Don’t assume that means traffic will always go direct. v2rayN may reorder outbounds when generating a config, so check the active configuration or express your intended fallback with an explicit rule that works with the current resolution strategy.
6. Add and Test Rules in v2rayN
Back up your current routing setup before making changes. The steps below use the v2rayN desktop UI, where the menu path is Settings → Routing Settings; buttons may be in different places in the cross-platform and classic interfaces. Edit a routing profile, then select it from the routing menu on the main screen. Saving a rule without activating its profile is a common reason it appears not to work.
-
Back up the profile
Go to Settings → Routing Settings and export or record the current profile and rule order. Keep the original profile, and create a separate one for testing so a change won’t disrupt all your regular routing.
-
Open the rules
In Routing Settings, select the profile you want to edit and open its rule editor. When adding a rule, choose the outbound route first, then enter
full:files.example.comin the domain field. Don’t paste a complete JSON config. -
Reorder the rules
Move specific hostname exceptions above broad rules such as
domain:example.com. Create separate rules for domain and IP fields. For multiple entries, follow the separators shown in the UI; don’t assume commas will be split automatically. -
Activate the profile
After saving, return to the routing selector on the main screen and choose the profile you edited. Confirm that the core has reloaded. If the logs show a configuration error, restore your backup before adding more rules.
-
Check the outbound route
Replace the example domain with a real hostname you’re authorized to test. Make a new connection and check the core logs. If access logging is enabled, verify the destination and outbound tag; a page loading successfully doesn’t prove it went direct.
Routing is usually determined when a connection is established. Existing browser connections may keep using their original route, so after saving a rule, close connections to the test site or restart the test app before trying again. Don’t judge a rule by refreshing an already-open page. If the request isn’t in the logs, check the system proxy, app proxy, or TUN capture before rewriting your regular expression.
- Check the syntax first: Verify prefixes, regex escaping, group names, and outbound tags, and confirm that the core starts successfully.
- Then check the destination: Confirm the hostname used by the actual request. A site’s main, login, and download domains may all be different.
- Finally, check the order: Starting from the first rule, find the first one that could match. Pay special attention to broad domain groups and fallback rules.
7. v2rayN Custom Routing FAQ
Why is traffic still using the proxy after I added a full rule?
First, go to Settings → Routing Settings and confirm which profile is active. Then move the exact-match rule above broader domain rules. Close existing connections before testing again, and check whether the request actually uses a different download or login hostname.
Should I add both geosite:cn and geoip:cn?
There’s no need to add both by default. If you need both types of matching, create two rules: one for the domain group and one for the IP group. Also check the routing resolution strategy; otherwise, a domain request may not be checked against the IP group.
Can I route only a specific HTTPS path directly?
The domain rules covered here can’t route by URL path inside an encrypted request. You can route by exact hostname or, when needed, add destination port 443. A port condition still can’t distinguish two paths on the same host.
Why does the core fail to start after I add geosite?
Check the startup logs for a missing data file or group. Update the data using the routing data update feature provided by the client, then verify the data path used by the active core. Temporarily test the configuration with a full: rule; don’t swap in random group names.
Can subscription updates change my custom rules?
Node subscriptions and routing profiles are separate configurations, but switching nodes, cores, or routing presets may change the generated configuration. After an update, check the active routing profile and outbound tags. Don’t paste a node subscription URL into a domain rule field.
When troubleshooting routing, check in this order: request reaches the core → destination information is available → first matching rule → corresponding outbound exists. Domain syntax describes the destination, resolution strategy supplies IP information, and rule order determines priority. Change one thing at a time and keep a restorable profile; this makes issues easier to isolate than repeatedly switching entire presets.