This guide is for v2rayN users who need to add domain exceptions or change the order of direct and proxy routing. It starts by distinguishing domain, IP and dataset matches, then uses a routing configuration to explain first-match behavior, DNS lookups as a fallback and default outbound handling. Examples use the classic JSON routing structure common to V2Ray and Xray; rules configured in a client interface still need to be translated into a format supported by the active core.
1. Routing Rule Structure and Match Targets
The system proxy forwards app connections to the inbound; routing selects an outbound after traffic enters the core. If a browser bypasses the system proxy, even correct rules may never be evaluated. Before troubleshooting custom rules, confirm that the app is connecting through the client instead of relying only on the exit address shown by a website.
Classic configurations store rules in the routing.rules array. Each rule typically has type set to field. Match conditions can include domain, ip, port or network, and outboundTag specifies the result. A tag is a reference name in the configuration, not a built-in action provided by the core.
{
"type": "field",
"domain": [
"full:api.example.com",
"domain:example.net"
],
"port": "443",
"network": "tcp",
"outboundTag": "proxy"
}
This rule requires the target domain to match any item in the array, the destination port to be 443 and the transport network to be TCP. Items within the domain array are combined with OR; different condition fields are combined with AND. The rule will not handle UDP connections on port 443 or TCP connections on port 80. The three numbers below illustrate configuration values, not speed test results.
2. Using domain, full, regexp and Plain Strings
domain is first a field name in a rule; domain: in the field array is a matching prefix. Do not confuse the two. Array items should contain only a domain or the relevant expression—not a URL scheme, path or query string. Standard domain routing cannot distinguish between URL paths on the same site.
| Syntax | What it matches | Example |
|---|---|---|
full:example.com | Exact match for the full hostname | Matches example.com, not www.example.com |
domain:example.com | A domain and its subdomains | Matches example.com and a.example.com, not notexample.com |
example.com | Substring match for a plain string | May match notexample.com; the scope is often broader than intended |
regexp:^api[0-9]+\\.example\\.com$ | Regular expression match | Matches api1.example.com, not api.example.com |
The regular expression in the table is shown as a JSON string. Use \. in the regex itself to match a literal period; JSON also requires the backslash to be escaped, so the configuration file uses \\.. In a client's plain-text input field, you usually enter a single backslash. Do not copy the JSON-escaped form into every interface.
{
"type": "field",
"domain": [
"full:example.com",
"domain:example.net",
"regexp:^api[0-9]+\\.example\\.com$"
],
"outboundTag": "proxy"
}
In short: use full or domain for domain exceptions
Use full: for one hostname and domain: for a domain and its subdomains. Use a regular expression only when hostnames follow a clear pattern, and add start and end anchors to avoid matching unrelated domains.
3. IP, GeoIP and GeoSite: What's the Difference?
The ip field matches destination addresses and accepts a single IP, a CIDR range or a geoip: dataset reference. geosite: goes in the domain array and matches a set of domains. These datasets contain different entries; matching a GeoSite entry does not guarantee a match in the corresponding GeoIP dataset.
{
"type": "field",
"ip": [
"192.168.0.0/16",
"10.0.0.0/8",
"geoip:private"
],
"outboundTag": "direct"
}
The example first shows a CIDR range, then a private-address set; their coverage overlaps, so a real configuration usually needs only the option that fits. A single IPv6 address can use /128. Covering IPv4 alone does not handle every local network connection—check IPv6 destination addresses on dual-stack networks too.
geosite:cn: References the domain set named cn in the data file; it does not mean “domains ending in .cn.”geoip:cn: Uses an address database to determine whether the destination IP falls within a particular region. It does not indicate the language of a site's content or the visitor's location.geoip:private: References a set of private and other locally scoped addresses. The actual coverage depends on the data file in use.- Data dependency: Classic dataset rules typically depend on
geosite.datandgeoip.datbeing accessible to the core. The dataset name must exist; do not guess category names.
A domain may resolve to CDN addresses in multiple regions, and IP-based rule results can change when DNS answers change. If a service must always use a particular outbound, add a specific domain exception instead of permanently pinning a single CDN address returned by one lookup.
4. Rule Priority: First Match in Order, Not by Specificity
During a rule scan, the core checks the rules array from top to bottom and selects the outbound specified by the first matching rule. It does not automatically let a more specific full: rule override an earlier geosite: rule, or reorder rules by CIDR prefix length. “Exceptions first” means moving exception rules higher in the configuration yourself.
{
"routing": {
"domainStrategy": "IPIfNonMatch",
"rules": [
{
"type": "field",
"ip": ["geoip:private"],
"outboundTag": "direct"
},
{
"type": "field",
"domain": ["full:api.example.com"],
"outboundTag": "proxy"
},
{
"type": "field",
"domain": ["geosite:cn"],
"outboundTag": "direct"
},
{
"type": "field",
"ip": ["geoip:cn"],
"outboundTag": "direct"
}
]
}
}
Here, the single-host exception comes before the domain-set rule. If a connection matches the second rule, the third will not be considered. Whether the first private-IP rule can match during the initial scan depends on whether the destination is already an IP and whether the domain strategy triggers a lookup. A rule being first does not mean DNS resolution happens first.
- If the target is
api.example.comand the inbound preserves the domain, the second rule can selectproxydirectly. - For other domains matching
geosite:cn, the third rule selectsdirectwithout waiting for an additional IP match. - Only if no rule matches at the domain stage does
IPIfNonMatchattempt a lookup and re-evaluate the rules using the resulting IP. - If nothing matches, a classic configuration uses the first outbound in the
outboundsarray. It is not necessarily namedproxy.
5. How domainStrategy Affects DNS and Routing
routing.domainStrategy controls whether routing resolves a target domain to an IP address for matching. It is not the system DNS setting or the similarly named strategy used by a direct outbound. Despite the similar names, these settings belong to different configuration layers and apply at different stages.
AsIs
The routing stage does not proactively resolve domains to IP addresses; IP rules can still match when the target is already an IP. Whether the connection is resolved later depends on subsequent stages, such as the outbound.
Best for: configurations built around domain rules that should not trigger additional DNS lookups during routing.
IPIfNonMatch
RecommendedTry matching with the information already available first. If no rule matches, resolve the domain and use the resulting IP for another round of rule matching. Avoid catch-all rules that match too early.
Best for: routing setups like this guide, where domain exceptions take priority and IP-based location matching is a fallback.
IPOnDemand
Resolve on demand when a rule requires IP information during matching. An earlier matching IP rule may select the outbound before a later domain exception is reached.
Best for: configurations that explicitly require IP rules to be evaluated early and allow control over DNS results and rule order.
For example, suppose the first rule is geoip:private and the second is a proxy exception for a domain. If that domain resolves to a private address, IPOnDemand may match the first rule before reaching the exception. With IPIfNonMatch, a match on the second rule means no additional lookup is needed. After changing the strategy, check which outbound is actually selected—not just whether the site loads.
- When DNS returns multiple addresses, understand IP matching in terms of how the core handles the results. Do not infer the outcome from only the first address shown by a command-line tool.
- When an app connects directly to an IP, domain rules usually lack a hostname to match. Sniffing may recover the target domain for some protocols, but it is not guaranteed to work for every connection.
- Adding a rule to “block all UDP 53” does not block all DNS. Encrypted DNS may use other ports and transport protocols.
6. Add and Verify Custom Rules in v2rayN
These steps refer to common interfaces in the v2rayN 7.x desktop release; button locations may vary between minor versions, so use the fields shown in your current window. Rules managed through the interface are not entered the same way as a full JSON configuration. Do not paste an entire routing object into a single domain field.
-
Check the core
Open “Settings” → “Parameter Settings” and check the Core type options and which core the current configuration uses. After startup, verify the core name in the logs. The syntax in this guide is for classic V2Ray and Xray routing configurations; other formats need to be converted.
-
Open routing settings
Go to “Settings” → “Routing Settings,” back up the current profile, then edit the rule set you intend to enable. Make sure you are editing the route selected on the main screen, not an inactive copy.
-
Add an exception
In the rule editor, choose the target outbound and enter
full:api.example.comor the domain you need to handle in the domain field. Start without port or network restrictions. Once it works, narrow the conditions as needed. -
Reorder the rules
Move the exception above any domain-set rules that could match first, then save and select the routing profile. The client will usually reload the relevant configuration. If the logs do not show the change, restart the service before testing again.
-
Check the connection
Close existing test tabs and create a new connection. Check the connection log for the target and outbound tag. For more detail, temporarily increase the log level under “Settings” → “Parameter Settings,” then restore your usual level when finished.
Routing changes typically apply to new connections; browser connection reuse and long-lived connections may continue using the previous outbound. One refresh is not enough to prove a rule is ineffective. Also test the main domain, a subdomain and a direct IP. Checking only the homepage may miss page resources served from other domains.
| Test | What to check | If the result is unexpected |
|---|---|---|
| Exact domain | Whether the full hostname uses the specified tag | Earlier rules; the active routing profile |
| Subdomain | Coverage differences between full and domain | Whether a plain string was used by mistake or a subdomain was missed |
| Direct IP | Whether the IP rule works independently | CIDR syntax; IPv4 and IPv6 address families |
| Unmatched target | Whether it reaches the intended default outbound | First outbound; catch-all rules; resolution strategy |
7. Common Questions About Custom Routing
Why does traffic still go direct when my full rule is at the end?
Greater specificity does not mean higher priority. Open “Settings” → “Routing Settings,” move the rule above any geosite or plain-domain rules that could match first, save, and create a new connection. Then check the outbound tag.
Why doesn't geoip:cn route domain traffic by region?
First check routing.domainStrategy. With AsIs, routing does not resolve domains for IP rules. With IPIfNonMatch, check whether an earlier domain rule already matches or a catch-all rule at the end stops evaluation before the additional lookup.
What should I do if the core fails to start after adding GeoSite?
Remove the new entry to restore the connection, then check the startup log. Confirm that the core can read the data file and that the category you entered exists. Do not keep guessing category names; first verify the routing path with a single domain: rule.
Why do the homepage, login and images use different exits?
Check the actual hostnames used by those requests. Login APIs, static assets and download services often use different domains; a full: rule for the main hostname will not cover them. For hosts within the same controlled domain, use domain:; add separate rules for unrelated domains instead of broadening the match with a vague keyword.
Can I reuse existing rules after switching cores?
First confirm the target core's configuration format, datasets and outbound tags, then test each rule. V2Ray and Xray share some classic fields, but clients may generate configuration differently and support different extensions. A successful import only confirms that the configuration was accepted; it does not prove every connection uses the expected outbound.