For users who have imported a subscription and want to route specific websites or IP ranges separately. Start by distinguishing domain conditions from IP conditions, then review rule order and when DNS resolution occurs. By the end, you’ll be able to write a narrowly scoped exception and use logs to see why it did—or didn’t—match.
Routing rule priority: the first match wins
In standard V2Ray and Xray routing, rules in the routing.rules array are checked from top to bottom. Once a rule matches, its specified outbound is used, and no later, “more specific” rule is considered. So an exact domain placed after a broad category rule won’t override it automatically—you need to move the exception higher in the list.
Within a rule, multiple entries in the domain array usually work as an OR condition: any one can match. Different condition fields usually work as AND conditions. For example, specifying both domain and port means the domain and destination port must both match. Putting a domain and an IP in the same rule does not mean either one can match on its own.
Here’s a core routing rule that sends only port 443 traffic to a specified host through the proxy. proxy is an outbound tag, not a built-in keyword; the full configuration must include an outbound with that exact tag. It is not the node’s display name, so don’t replace it with a subscription node name without checking first.
{
"type": "field",
"domain": ["full:portal.example.com"],
"port": "443",
"outboundTag": "proxy"
}
domain, full, regexp: Domain syntax and matching boundaries
domain is a rule field; domain: is a matching prefix used in a field value. They belong to different levels. The complete field expression is "domain": ["domain:example.com"]. Don’t treat full or regexp as rule fields alongside domain.
| Entry syntax | What it matches | Boundary notes |
|---|---|---|
full:example.com | Matches this exact hostname | Does not include www.example.com |
domain:example.com | Matches the root domain and its subdomains | Includes a.example.com, but not notexample.com |
regexp:^api[0-9]+\.example\.com$ | Matches using a regular expression | The expression is shown here; escape backslashes again when writing JSON |
example.com | A string without a prefix matches as a substring | May match more than expected; not suitable for precise exceptions |
geosite:cn | Uses a category from the domain database | The category contents depend on the data file currently loaded |
For a single-site exception, use full:. To route the root domain and all its subdomains through the same outbound, use domain:. Use a regular expression only when the hostname follows a consistent pattern. Don’t include https://, paths, or query parameters in domain entries: routing checks the destination host, not the full URL in the browser’s address bar.
{
"type": "field",
"domain": [
"full:portal.example.com",
"domain:static.example.net",
"regexp:^api[0-9]+\\.example\\.com$"
],
"outboundTag": "proxy"
}
The three entries in this array are alternatives. A visit to api12.example.com can match the regular expression; a visit to img.static.example.net can match the domain suffix. In JSON, \\. becomes \. in the regular expression after parsing. One missing escape layer can prevent the configuration from loading.
ip, geoip, geosite: Keep IP ranges and category data separate
The ip array specifies destination IP conditions. It accepts individual addresses, CIDR ranges, and geoip: categories. geosite: is a domain condition and belongs in the domain array. Swapping these category prefixes won’t produce the expected combined routing by domain and address.
{
"type": "field",
"ip": [
"192.168.10.0/24",
"10.0.0.8/32",
"geoip:private"
],
"outboundTag": "direct"
}
- Single device:
10.0.0.8/32matches only this IPv4 address. For a single IPv6 address, use/128. - Entire subnet:
192.168.10.0/24covers this IPv4 range, making it suitable for a specific LAN destination. - Address category:
geoip:cnrelies on an IP database and may not reflect a website’s business location. - Domain category:
geosite:cnrelies on a domain database. A newly added domain won’t match automatically just because the site uses Chinese.
A website may use multiple content delivery networks, and the same domain can resolve to IPs in different regions depending on the network. Different routes for domain-based and IP-based categories don’t necessarily mean a rule is broken. First decide whether you want “this website to always use the proxy” or “these IP ranges to connect directly,” then choose the matching condition.
domainStrategy: When does routing resolve domain names?
routing.domainStrategy determines how routing handles domain names in relation to IP rules. It is not a DNS server address or the address selection strategy used for outbound connections. These three values are common in V2Ray/Xray routing configurations; behavior can vary by core version, so check the version you’re running.
AsIs
RecommendedFor destinations passed in as domain names, routing does not proactively resolve IPs for rule matching. Destinations passed in as IPs can still match IP rules. This is useful for getting domain exceptions and rule order working first, with fewer variables to troubleshoot.
Best for: prioritizing domain rules and initially testing custom routing
IPIfNonMatch
If a domain doesn’t match any existing rule, routing then tries to resolve it to an IP for subsequent rule matching. If a broad rule earlier in the list already catches the request, the presence of a later geoip entry won’t necessarily trigger resolution.
Best for: handling most traffic with domain rules and using IP categories as a fallback
IPOnDemand
When a rule requires a destination IP during matching, routing tries to resolve the domain. An IP rule near the top may trigger a DNS lookup earlier and may determine the outbound before a later domain exception is reached.
Best for: cases where IP conditions must be considered first
Bottom line: fix rule order before changing the resolution strategy
First use AsIs to verify exact-domain exceptions. If you need geoip to provide additional matching for domain requests, test IPIfNonMatch and record the destination address and outbound tag in the logs before and after the change.
If an app resolves a domain itself and then sends only the IP to the proxy, the core may no longer see the original domain. Traffic sniffing can provide domain information for some protocols, but it doesn’t work for every connection. When enabling it, distinguish between using sniffed data for routing and rewriting the destination. Sniffing does not guarantee that domain rules will match.
Complete routing example: put exact exceptions before categories
The snippet below shows only the routing section to embed in a complete configuration; it is not a standalone configuration file. The example assumes the configuration already has outbound tags named direct and proxy. Rules are ordered as follows: LAN addresses, exact domain exceptions, domain categories, IP categories, and finally a TCP/UDP fallback.
{
"routing": {
"domainStrategy": "IPIfNonMatch",
"rules": [
{
"type": "field",
"ip": ["geoip:private"],
"outboundTag": "direct"
},
{
"type": "field",
"domain": ["full:portal.example.com"],
"outboundTag": "proxy"
},
{
"type": "field",
"domain": ["geosite:cn"],
"outboundTag": "direct"
},
{
"type": "field",
"ip": ["geoip:cn"],
"outboundTag": "direct"
},
{
"type": "field",
"network": "tcp,udp",
"outboundTag": "proxy"
}
]
}
}
One easy-to-miss detail: the final network rule catches typical TCP/UDP requests. With IPIfNonMatch, a domain request that matches this rule won’t take the “resolve only after no match” path just because the earlier IP category didn’t match. Here, geoip:cn mainly handles requests that already include a destination IP; it does not classify the addresses of every remaining domain.
- Keep this explicit fallback if you want domain requests routed mainly by domain rules and IP requests mainly by IP rules.
- To apply IP categories only when a domain doesn’t match, remove the rule that catches all connections early and check the default outbound. The usual default is the first outbound.
- To resolve a domain whenever an IP condition is encountered, consider
IPOnDemand, but retest how earlier IP rules interact with exact domain exceptions.
Check the default outbound in the complete configuration; don’t infer it from the node currently selected in the client interface. v2rayN generates the runtime configuration, and manual edits to a temporary generated file may be overwritten when switching nodes or restarting. Save long-term rules in the client’s routing configuration.
v2rayN custom routing: test one rule at a time
v2rayN is a desktop client; the selected core handles the actual traffic. The steps below refer to desktop versions with a Settings → Routing Settings entry. Button locations and field labels may vary between versions. Before editing, note the name of the active routing configuration and keep a copy of the existing rules. This helps avoid confusing subscription node issues with routing issues.
-
Open Routing Settings
Go to Settings → Routing Settings and note the active routing configuration. Export or copy the existing rules before adding an exception.
-
Enter a domain condition
In the rule editor’s domain condition field, enter
full:portal.example.com. The GUI field typically accepts individual entries, not an entire routing JSON object. -
Choose the outbound
Choose proxy or direct routing according to your needs. If you’re editing core JSON, check that
outboundTagexactly matches a tag inoutbounds. -
Move it up and enable it
Move the exception above domain categories and broad fallback rules. After saving, select this configuration in the main interface’s routing options. Don’t just save it while continuing to use the original configuration.
-
Start a fresh test connection
Close the old test page and existing connections, then make a new request. Check the core logs for the destination host, destination port, and outbound tag. Change only one condition at a time.
For verification, start with a full: exception for the actual test hostname. Once it matches, broaden it to domain: or a category. Browsers reuse connections, so an existing connection may keep using its original outbound; simply refreshing the page may not open a new one. When testing with command-line tools, explicitly use the local proxy port shown in the client interface instead of assuming every machine uses the same port.
Routing rules not working? Diagnose by symptom
The domain is correct, so why is traffic still going direct?
Move the exact rule above category rules, then verify that the modified routing configuration is active. If the log shows only an IP for the destination, check whether the app resolved the domain first and whether the core can obtain domain information.
Why does the core fail to start after I add geosite?
Check the logs for errors saying the domain data file is missing or a category failed to load. Verify the path to geosite.dat and the category name. Test the basic configuration with a single full-domain rule first, then restore the category entry.
The main website uses the proxy, but why don’t its images?
Check the actual hostname of each image request in the browser’s network panel. Images may come from a different root domain, which won’t be covered by the main site’s domain entry. Confirm the resource’s purpose, then add a separate rule for its domain.
Port 443 matches, so why is the download still failing?
The port condition determines routing; it doesn’t guarantee the destination is reachable. Check that the log shows the correct outbound tag, then troubleshoot node connectivity, DNS results, and UDP support separately. Port 443 can carry both TCP and UDP traffic.
Why did my custom rules disappear after restarting the client?
Check whether your changes were made only to a temporary generated core configuration. Go back to Settings → Routing Settings to save the rules and enable the right configuration. If you use a separate custom configuration, maintain the source file that’s actually loaded.
Finish by answering three questions: Did the connection reach the core? Did the core see a domain or an IP? Which outbound did the first matching rule select? Answering these usually narrows things down faster than changing DNS, switching cores, and rebuilding the subscription all at once. For field details, see the Protocol and Core Reference; for unfamiliar terms, consult the Glossary.