This guide is for Mac users who can already connect to a server with v2rayN and want to adjust routing. First, understand the difference between domain lists and IP databases. Then set up rules to send private addresses and mainland China domains and IPs directly, while routing everything else through a proxy. The examples show a readable routing snippet and explain how the default outbound, DNS, and rule order affect the result.
geosite vs. geoip: Different Match Targets
Routing does not determine where traffic goes based on a webpage's language or your device's location. When V2Ray core receives a request, it reads available information such as the destination domain, destination IP, and port, then checks for a match in routing.rules from top to bottom. When a rule matches, traffic is sent to the outboundTag specified by that rule. The tag must correspond to a tag that actually exists in outbounds.
| Syntax | Matches | Key limitation |
|---|---|---|
geosite:cn | Domain category list | Depends on the geosite data file in use; it does not include every website in mainland China |
geoip:cn | IP ranges classified as China in the database | Does not indicate where a website's company is based or what language its content uses |
geoip:private | Private and special-use address ranges | Check the data file in use for the exact ranges |
domain:example.com | A specified domain and its subdomains | Does not cover other domains with similar names |
full:example.com | Exact domain | Does not include subdomains |
geosite:cn is a category entry, not a guess based on a domain suffix. A .com domain may be on the list, while a site using an overseas CDN may have a domain category that differs from its IP's location. When domain information is available, routing by domain is usually easier to maintain than routing only by the resolved address.
v2rayN Routing Setup: Back Up Before Editing
v2rayN manages configuration and connections; the selected core actually applies the rules. Configurations in the Project V, V2Fly, and Xray ecosystems share some fields, but also have core-specific extensions. This guide uses only common field rules and does not transplant extensions from one core to another.
-
Note your current configuration
On the main screen, note the current server, routing mode, and system proxy status. Open Settings → Parameter Settings and note the core in use and local listening ports. The interface may vary between versions, so follow what you see. Do not switch to a core that does not support your current server's protocol just to use this example.
-
Back up your routing rules
Go to Settings → Routing Settings and back up the existing rules before creating or editing a routing profile for testing. Make sure this profile is selected on the main screen, rather than Global Proxy or Global Direct mode.
-
Check the outbound tags
Check the configuration actually loaded by the core and verify the
tagvalues for the proxy and direct outbounds. This guide usesproxyanddirectas example tags. If your tags differ, update the rules'outboundTagvalues to match. -
Apply the rules and check the logs
Save the rules and have the client reload the core configuration, then check the startup results in the logs. If you see an unknown outbound, a data file loading failure, or a JSON parsing error, restore your backup before making further changes.
The client usually regenerates its runtime configuration. Direct edits to the temporary config.json may be overwritten when you switch servers or restart. For everyday use, save rules through Routing Settings. If you manage the configuration manually, maintain the snippet below in your own configuration source.
Routing Example: Direct in Mainland China, Proxy Everywhere Else
This is a standalone routing snippet, not a complete client configuration. Before using it, make sure you have a working proxy outbound and a direct outbound with protocol set to freedom; their tags should be proxy and direct, respectively. Also confirm that the core can read geosite and geoip data files containing the relevant categories.
{
"routing": {
"domainStrategy": "IPIfNonMatch",
"rules": [
{
"type": "field",
"ip": ["geoip:private"],
"outboundTag": "direct"
},
{
"type": "field",
"domain": ["geosite:cn"],
"outboundTag": "direct"
},
{
"type": "field",
"ip": ["geoip:cn"],
"outboundTag": "direct"
},
{
"type": "field",
"network": "tcp,udp",
"outboundTag": "proxy"
}
]
}
}
- Rule 1 handles private addresses: Local network destinations such as
192.168.1.1and10.0.0.1should not go through a remote proxy just to reach a router or internal service. - Rule 2 handles mainland China domains: Matches for
geosite:cngo direct immediately, without waiting for later IP conditions. - Rule 3 handles mainland China IPs: This applies to destinations that are already IP addresses, as well as mainland China addresses obtained through resolution when the routing strategy allows it.
- Rule 4 is an explicit fallback: Send all remaining TCP and UDP requests to
proxy, rather than relying on the default outbound order.
When no rule matches, the core typically uses the first outbound in outbounds. So you can send everything else through a proxy either with an explicit rule or by making the proxy the default outbound. These approaches affect IPIfNonMatch differently, so do not copy the rules without checking the outbound order.
domainStrategy: When to Resolve Domains for Routing
routing.domainStrategy determines whether routing tries to resolve a domain to an IP address for matching. It is not a system DNS setting, nor is it the same as the domain handling strategy on a direct outbound. Changing this field does not automatically take over DNS requests, encrypt them, or select an upstream resolver.
IPIfNonMatch
RecommendedTry matching with the information already available. If a domain does not match any rule, try resolving it and match again using the result. This is a good starting point, but check whether a catch-all rule already covers every request. If you need resolution as a fallback, using the proxy as the first outbound makes the default behavior clearer.
Best for: Configurations that prioritize domain rules and explicitly manage the default outbound
AsIs
The routing stage does not actively resolve domains for matching. Domain requests rely mainly on domain rules, while requests that already use an IP address can still match IP rules. This does not guarantee that no DNS queries occur anywhere in the connection process.
Best for: Configurations with a comprehensive domain list that aim to minimize routing-time DNS lookups
IPOnDemand
Try resolving domain requests when a matching rule requires IP information. If geoip:private is the first rule, resolution may happen early, making routing more directly dependent on DNS availability and the addresses returned.
Best for: Configurations that need to make decisions based on resolved IPs and have a verified DNS path
Bottom line: Set the fallback before choosing a resolution strategy
To check IPs when a domain is not in your domain list, make the proxy the first outbound, remove the final catch-all, and test with IPIfNonMatch. If you keep an explicit fallback, be clear that unknown domains will go straight to the proxy, or test the resolution results with IPOnDemand separately.
A domain can resolve to multiple IPv4 and IPv6 addresses, and changes to the DNS upstream can also change its CDN endpoint. A match for geoip:cn once does not guarantee that future connections will match. For a stable egress route, add a dedicated rule for the specific domain instead of permanently pinning a CDN IP that happened to appear.
Rule Order: First Match Wins
Rules are checked from top to bottom, and the matching rule determines the outbound. The core does not compare rules to see which is more specific. Put specific exceptions before broad categories and any catch-all rule last. This is the basic order for maintainable rules.
{
"type": "field",
"domain": ["domain:example.com"],
"outboundTag": "proxy"
}
For example, if a domain is classified for direct routing by the domain database but your use case requires it to go through a proxy, put a dedicated rule for it above geosite:cn. example.com is a sample domain for demonstrating the syntax; replace it with your actual destination. To match only the exact domain and not its subdomains, use full:.
- Multiple values in one field usually mean “OR”: If the
domainarray contains two domains, matching either one satisfies that field. - Different conditions in one rule usually mean “AND”: If you specify both
domainandport, the destination must match both the domain and the port. Do not putdomainandipin the same rule expecting either one to match. - Account for the actual protocol in port rules: A rule that matches only TCP does not cover UDP. Browsers often use UDP 443 for HTTP/3, so matching TCP 443 alone does not account for all web traffic.
- Rules only apply to connections that reach the core: A local address excluded by the system proxy bypass list may be accessed directly by the app. In that case, it is normal for the core logs to show no record.
Test and Troubleshoot: Check the Destination, Rule, and Outbound
A webpage loading only proves that this request completed; it does not prove that it used the intended egress. First confirm that the core started successfully, then make a consistent test request and check the destination and outbound tag in the access logs. Log formats vary between cores, and some show the inbound-to-outbound mapping. If the log level is too low, temporarily increase it for testing, then restore it afterward.
curl --proxy http://127.0.0.1:10809 --connect-timeout 10 --max-time 20 -I https://www.baidu.com/
curl --proxy http://127.0.0.1:10809 --connect-timeout 10 --max-time 20 -I https://example.com/
10809 is an example HTTP proxy port, not a claim about the current v2rayN default. Replace it with the active HTTP or mixed listening port shown under Settings → Parameter Settings. The commands explicitly specify a local proxy to rule out whether the terminal follows system proxy settings. These requests mainly test the TCP path, not browser behavior over UDP.
Why Is a Mainland China Website Still Going Through the Proxy?
First, check the logs to see whether the destination is a domain or an IP, then check whether that domain appears in the current geosite data. If you use IPIfNonMatch and have a catch-all proxy rule at the end, the domain will not be resolved for another match. Try adding an exact-domain direct rule first; do not immediately switch to direct routing for an entire IP range.
Why Do Some Sites Stop Working After Adding geoip:cn?
Check the DNS response and the actual outbound shown in the logs. If the destination IP is classified as being in mainland China but the service cannot be reached directly on your current network, add a proxy exception for its domain and place it before the mainland China direct rules. Change one rule at a time, and keep a record of the failed destination for comparison.
What if the geosite category cannot be found?
Check the category name in the rule, the data file path actually loaded by the core, and the startup logs. Different data releases may not contain the same categories, and updating a subscription does not necessarily update the rule database. Use the data update option provided by your client, then restart the core. If the category is still unavailable, restore a category you have confirmed exists.
Rules had no effect. Do I need to reinstall?
First, make sure the new routing profile is selected, reload the core, and close any existing connections in the test app before making a new request. Existing long-lived connections will not switch outbounds automatically when rules change. Also confirm that you edited the configuration source, not a runtime file the client may overwrite.
Keep three records: the core in use, the routing rules, and the outbound tag for each test request. Before sharing logs publicly, remove subscription URLs, server credentials, and private browsing records. The goal of validating routing is to confirm that specific requests consistently use the intended outbound—not to judge every rule by a single speed test.