V2Ray Routing Rules: Domain, IP, GeoSite Matching and Rule Priority

Learn V2Ray domain match prefixes, IP and GeoIP rules, GeoSite categories, first-match rule order, and how domainStrategy affects routing.

At a glance

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.

10808
Local SOCKS port used in this guide's examples
443
Common HTTPS destination port
/32
IPv4 single-address CIDR prefix

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.

SyntaxWhat it matchesNotes
example.comA string contained in the domainMay match notexample.com; broader than a root-domain match
domain:example.comexample.com and its subdomainsMatches www.example.com, but not notexample.com
full:api.example.comOnly the exact domain api.example.comDoes not include cdn.api.example.com
regexp:^api[0-9]+\.example\.com$The entire string must match the regular expressionEscape 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.

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.

  1. Put specific exceptions first: to proxy one exact hostname, place its full: rule near the top.
  2. Follow with category rules: add domain categories to proxy or block, then general categories to send direct.
  3. 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.
  4. Put the fallback last: if no rule matches, the first outbound in the outbounds array 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

Recommended

The 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.

  1. 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.

  2. 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.

  3. Check the strategy

    Check domainStrategy in 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.

  4. 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.

SymptomCheck firstWhat to do
An exact-domain exception still goes directDid 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 destinationResolution behavior with AsIs or IPIfNonMatchCheck the strategy and whether a fallback matched on the first pass
An error appears immediately on startupJSON syntax, category names, data files, and outbound tagsRemove the new category reference first, then isolate the issue with one full rule
Browser and command-line results differDo 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 rulesActive rule set, core restart, and connection reuseConfirm 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/

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.

Download v2rayN