This guide is for users who can already connect to a server and want to define what goes direct or through a proxy. Start with domain and IP match syntax, then order rules from specific exceptions to category rules and a final fallback. Finally, check domain resolution strategy and actual outbound routing. Examples use the common V2Fly V2Ray and Xray field-routing format. A client editor is not a complete core configuration, so verify the generated settings after saving.
Routing basics: match conditions and outbound tags
Routing rules decide which outbound handles a connection. They do not create servers or change the VMess or VLESS protocol used by a server. V2Ray is a core project, and Xray is a separate core project. v2rayN is a desktop GUI client that converts its settings into a configuration the selected core can read.
A typical rule includes type: "field", match conditions, and outboundTag. The tag must match a tag defined in outbounds. This guide uses proxy, direct, and block as example names for proxy, direct, and block outbounds. These are examples only; do not assume every client configuration uses the same tags.
- Conditions within one field are usually ORed: any matching domain in the
domainarray satisfies that field. - Different fields are ANDed: if a rule includes both
domainandport, the domain and destination port must both match. - domain and ip are not alternatives: putting both in one rule requires both to match. To match a domain or an IP, use two separate rules.
- port means the destination port:
port: "443"matches the remote service port, not the local proxy listening port.
These are example values, not benchmark results or promised client defaults. Check the local settings for the actual listening port. HTTPS can also run on other ports, so port 443 alone does not identify all encrypted traffic.
Four domain match types: substring, regex, root domain, and exact domain
The domain field is easy to confuse with the domain: prefix: the former is a rule field, while the latter is a match pattern for a string inside that field. Of the four common forms, plain strings have no prefix; the other three use regexp:, domain:, and full:. Do not add a prefix to plain strings just for consistency.
| Syntax | What it matches | Notes |
|---|---|---|
example.com | A string contained in the domain | May match notexample.com; broader than a root-domain match |
domain:example.com | example.com and its subdomains | Matches www.example.com, but not notexample.com |
full:api.example.com | Only the exact domain api.example.com | Does not include cdn.api.example.com |
regexp:^api[0-9]+\.example\.com$ | The entire string must match the regular expression | Escape backslashes again in JSON |
For everyday site-based routing, use domain:. For an exception that applies to just one host, use full:. Domain conditions accept hostnames only—not https://, page paths, or query parameters. Routing rules cannot target an individual article path on a website.
{
"type": "field",
"domain": [
"full:api.example.com",
"domain:example.org",
"regexp:^api[0-9]+\\.example\\.com$"
],
"outboundTag": "proxy"
}
This regular expression matches api12.example.com. The leading ^ and trailing $ anchor the match to the start and end of the string. The backslash before the dot makes it match a literal period. The example domain illustrates the syntax; replace it with the hostname you want to route.
IP, GeoIP, and GeoSite: address ranges and data files
The ip field accepts an address, a CIDR range, or a geoip: reference. 192.168.1.10/32 covers a single IPv4 address, while 192.168.1.0/24 covers the subnet. An IPv6 address can use /128 for a single host. These conditions match the destination address. To match the request source, use the corresponding source field.
{
"type": "field",
"ip": [
"192.168.1.0/24",
"geoip:private"
],
"outboundTag": "direct"
}
geoip:private refers to the private-address category in the data file, while geoip:cn refers to its China IP category. Neither is a live geolocation service; the actual ranges depend on the loaded geoip.dat. Network ownership and data freshness can affect matches, so a category match does not prove where a server is physically located.
- Use geosite in domain: For example,
"domain": ["geosite:cn"]references a domain category, not an IP category. - Use geoip in ip: For example,
"ip": ["geoip:cn"]references IP address ranges and cannot match domain names in URLs. - The category must exist:
geosite:category-ads-allis an ad category in some commonly used datasets, but it is not included in every data file. - Attribute filters depend on the data: check whether the current file provides the relevant category or attribute for
geosite:geolocation-!cnand category forms containing@. Do not infer their meaning from the name alone.
Rule priority: first match wins
Rules are checked from top to bottom in the rules array. When a rule matches, the connection is sent to its outbound and rule evaluation stops. There is no built-in priority where an exact domain automatically beats GeoSite or an IP rule automatically beats a domain rule. A broad rule near the top can also mask a more specific condition below it.
- Put specific exceptions first: to proxy one exact hostname, place its
full:rule near the top. - Follow with category rules: add domain categories to proxy or block, then general categories to send direct.
- Order IP rules by their purpose: private network addresses are often sent direct, but their position relative to domain rules depends on the resolution strategy.
- Put the fallback last: if no rule matches, the first outbound in the
outboundsarray is typically used. To make the result explicit, add a final rule that covers the intended destination port range.
The following routing snippet is an example, not a complete runnable file. It requires existing proxy, direct, and block outbounds, and the data files must contain the referenced categories. If the tags differ from the names generated by your client, update the tags first.
{
"routing": {
"domainStrategy": "IPOnDemand",
"rules": [
{
"type": "field",
"domain": ["full:api.example.com"],
"outboundTag": "proxy"
},
{
"type": "field",
"ip": ["geoip:private"],
"outboundTag": "direct"
},
{
"type": "field",
"domain": ["geosite:category-ads-all"],
"outboundTag": "block"
},
{
"type": "field",
"domain": ["geosite:cn"],
"outboundTag": "direct"
},
{
"type": "field",
"ip": ["geoip:cn"],
"outboundTag": "direct"
},
{
"type": "field",
"port": "1-65535",
"outboundTag": "proxy"
}
]
}
}
This example handles exact-domain exceptions first, then private addresses. With IPOnDemand, a lookup may occur while checking an earlier IP rule. Placing the ad category after the private-address rule gives private-address matches higher priority. If blocking the category should take priority, change the rule order rather than expecting the core to infer which rule matters more.
domainStrategy: when domain resolution affects routing
Routing's domainStrategy controls whether domains are resolved to IP addresses to help determine a route. It is not a DNS server setting, nor is it the same as an option with the same name in a direct outbound. The available match conditions also depend on whether the connection initially supplies a hostname or an IP address.
AsIs
RecommendedThe routing stage does not proactively resolve domains to IP addresses. Domain destinations are matched primarily by rules such as domain and GeoSite; destinations that are already IP addresses can still match IP rules. A good starting point for predictable domain-based routing.
Best for: domain-based rules and initial checks of match order
IPIfNonMatch
If no rule matches a domain on the first pass, the core tries resolving it and checks the rules again. A broad port rule near the top may match on the first pass and end evaluation before the second, post-resolution check.
Best for: trying IP categories after domain rules fail
IPOnDemand
The core resolves a domain when it reaches a condition that requires an IP address. Earlier IP rules can affect whether later domain-category rules are checked, so plan DNS behavior and rule order together.
Best for: routing domain destinations using IP categories
Key takeaway: choose when to resolve before adding a fallback
With IPIfNonMatch, do not add a generic port fallback that always matches on the first pass and expect a later GeoIP rule to take effect. Let the first outbound handle the default route, or revise the strategy. Consider IPOnDemand and your actual DNS configuration when IP checks must happen before the final fallback.
If an app connects directly to an IP address, the core initially has no domain for a domain rule to match. Sniffing can extract a hostname from supported traffic, but it does not work with every protocol and is not a general-purpose way to recover domains. To determine where DNS queries originate, check the core's DNS settings, outbound, and client mode; changing routing strategy alone does not reveal this.
v2rayN setup: edit rules, then verify the generated configuration
The v2rayN routing editor can change between versions. On desktop, open it from Settings → Routing Settings. If your interface differs, check the version under Help → About and follow the options for that version. Do not paste the entire core JSON from this guide into a field that accepts only a list of domains.
-
Record the baseline
Note the client version under Help → About, then check the active core and local listening ports under Settings → Parameters. Back up the current routing rules and confirm that the active mode uses custom routing rather than switching directly to global proxy mode.
-
Add an exception
Go to Settings → Routing Settings and edit the active rule set. Add a
full:condition for a real hostname you use, choose its outbound, and place the rule before broader category rules. -
Check the strategy
Check
domainStrategyin the routing configuration, along with the domain and IP arrays and outbound tags. For category rules, confirm that the active core can load the required GeoSite and GeoIP data files. -
Restart and test
Save and enable the updated rule set, then restart the active core connection. Check the order in the generated configuration, make a new request, and review the logs. Existing connections usually keep their original outbound after a rule change.
On Android, v2rayNG uses the Xray core, while v2flyNG uses the V2Fly core. Even when their basic rule formats are similar, the editable fields, import options, and data-file management may differ. When migrating, check which fields the client supports; do not treat a desktop client's entire settings file as a universal core configuration.
Troubleshoot routing rules: traffic entry, data, and logs
A website loading proves only that the connection succeeded—not that it used the intended outbound. Test with a new connection to an exact hostname, a local network destination, and a destination in a category. First confirm that traffic reaches the core, then check the matching rule and outbound tag, and finally inspect DNS behavior and category coverage.
| Symptom | Check first | What to do |
|---|---|---|
| An exact-domain exception still goes direct | Did an earlier GeoSite or port rule match? | Move the full rule above relevant broad rules, then start a new connection |
| GeoIP does not affect a domain destination | Resolution behavior with AsIs or IPIfNonMatch | Check the strategy and whether a fallback matched on the first pass |
| An error appears immediately on startup | JSON syntax, category names, data files, and outbound tags | Remove the new category reference first, then isolate the issue with one full rule |
| Browser and command-line results differ | Do both use the same proxy entry, and is DNS resolved locally? | Specify the same SOCKS entry explicitly and compare hostname and IP requests |
| Results do not change after editing rules | Active rule set, core restart, and connection reuse | Confirm the edited rule set is active, close existing connections, and test again |
If your local SOCKS inbound is listening on the example port 10808, use the command below. socks5h sends the hostname to the proxy instead of resolving it locally first. This reduces differences in the test entry point, but the core may still resolve the hostname according to its DNS settings.
curl --proxy socks5h://127.0.0.1:10808 --head https://example.com/
- Check the entry point first: requests that never reach the core are not affected by its routing rules. System proxy settings also do not guarantee that every app will use them.
- Then check the logs: temporarily enable suitable access or debug logging and look for the destination address and inbound and outbound tags. Restore your usual log level when troubleshooting is complete.
- Finally, simplify the rules: keep one domain condition and one explicit outbound. After confirming it works, restore category and IP conditions one at a time to avoid changing multiple variables at once.
Before sharing logs, redact sensitive information such as subscription URLs, server credentials, and business hostnames. For the difference between a client, a core, and an outbound, see the glossary. To review the setup from the connection entry point, start with the quick-start guide.