Xray Routing Rules: Advanced JSON Configuration Guide

Build a maintainable Xray routing design from the JSON level. We explain rule precedence, domain matching, DNS-aware routing, and custom geosite or geoip sets with complete examples you can adapt for real deployments.

Xray routing is the layer that turns a working proxy node into a predictable network design. Instead of sending every connection through one outbound, you can classify traffic by domain, IP, port, protocol, application, or source, then send each class to a direct, proxy, or blocking outbound. The difficult part is not writing a long JSON file; it is defining rule precedence, choosing a domain strategy, understanding when DNS is involved, and keeping custom rule sets maintainable as the configuration grows.

Article overview

This guide builds an advanced Xray routing design from the JSON level. It explains how rules are evaluated, how domain and IP matching interact with DNS, how to create custom geosite and geoip sets, and how to test changes safely in v2rayN, v2rayNG, or another Xray-based client.

Understand the routing architecture and rule flow

An Xray routing configuration normally connects four parts: an inbound receives traffic, DNS optionally resolves a domain, the routing engine evaluates rules, and an outbound performs the selected action. A connection may enter through a local SOCKS port such as 10808, an HTTP port such as 10809, or a TUN interface. The inbound determines what metadata is available, while the routing rules determine what happens next.

Application requestInbound captureDomain or IP matchOutbound selectionRemote connection

The basic routing object looks like this:

{
  "routing": {
    "domainStrategy": "IPIfNonMatch",
    "rules": [
      {
        "type": "field",
        "domain": ["geosite:category-ads-all"],
        "outboundTag": "block"
      },
      {
        "type": "field",
        "domain": ["geosite:cn"],
        "outboundTag": "direct"
      },
      {
        "type": "field",
        "ip": ["geoip:cn", "geoip:private"],
        "outboundTag": "direct"
      },
      {
        "type": "field",
        "network": "tcp,udp",
        "outboundTag": "proxy"
      }
    ]
  }
}

Rules are evaluated from top to bottom. In the common field-rule model, the first matching rule wins, so broad rules must come after specific exceptions. A final rule that sends TCP and UDP to proxy acts as a default route. Without a deliberate final rule, unmatched traffic may use the core's default behavior, which can make testing confusing.

Practical conclusion: write exceptions before categories

Place private networks, local services, and explicit block rules before broad mainland or proxy rules. A rule that is technically correct can still be ineffective when an earlier general rule has already selected an outbound.

What a field rule can match

The field rule is the most frequently used form for client-side routing. Depending on the Xray version and inbound metadata, it can match domain, ip, port, source, sourcePort, network, protocol, inboundTag, and user. Conditions inside one rule are normally combined as requirements: for example, a rule with both domain and port matches only when both conditions are satisfied.

Field Typical value Useful purpose
domain full:example.com Match an exact name, suffix, keyword, or geosite set
ip geoip:cn Classify a resolved address or CIDR range
network tcp,udp Apply a route to one or both transport networks
port 80-443 Route traffic by destination port
protocol http,dns Handle recognizable application protocols

Design rule precedence before writing JSON

A maintainable configuration starts with a policy rather than a list of domains. A practical policy may be: block unwanted categories first, keep local and private destinations direct, send domestic services direct, proxy foreign services, and reject anything that should never leave the device. Each policy should have one clearly named outbound tag. Tags such as direct, proxy, block, and dns-out are easier to audit than anonymous labels such as outbound-1.

Direct outbound

Tag
direct
Protocol
freedom
Use
LAN, private IPs, local domains

Use this for traffic that should leave through the normal network connection.

Proxy outbound

Tag
proxy
Protocol
Configured proxy
Use
Unmatched or external services

The tag must exactly match the outbound supplied by the selected node.

Block outbound

Tag
block
Protocol
blackhole
Use
Ads, telemetry, unwanted ports

Return an intentional failure instead of silently sending the request elsewhere.

DNS outbound

Tag
dns-out
Protocol
dns
Port
53

Keep DNS handling explicit when the configuration uses a DNS outbound.

The following order is suitable as a starting framework:

  1. Block explicit unwanted domains and IP ranges.
  2. Send private, loopback, and local network destinations to direct.
  3. Handle DNS protocol traffic before broad port or network rules.
  4. Match custom exceptions that must use a particular outbound.
  5. Match domestic domain and IP sets if your policy requires direct access.
  6. Send all remaining supported traffic to proxy or another intentional default.

Do not place a rule matching every TCP and UDP connection near the beginning. It will capture traffic before later domain, IP, or port rules are evaluated. Likewise, do not assume that a rule using geoip:cn can classify a domain before an address exists. That depends on domainStrategy, DNS behavior, and whether the application supplied a domain or a literal IP.

Use domain matching and DNS-aware routing correctly

Xray can receive a hostname such as news.example.com or a literal address such as 203.0.113.20. Domain rules are more precise because they preserve the requested name. IP rules are useful for applications that connect directly by address, for services whose domain classification is unavailable, and for enforcing private-network or regional policies. These two types should complement one another rather than compete randomly.

Common domain expressions include full:example.com for an exact hostname, domain:example.com for the domain and its subdomains, keyword:video for a name containing a string, and regexp: for a regular expression. A bare domain entry may be interpreted according to the core's domain-matching rules, but explicit prefixes make the intention easier to review. Avoid using keyword: for sensitive production rules when an exact or suffix match is available, because it can match unrelated names.

The domainStrategy setting determines whether Xray should resolve a domain while routing. AsIs prefers the original domain and avoids proactive resolution for IP rules. IPIfNonMatch keeps checking after domain rules fail and is often a practical choice for split routing. IPOnDemand resolves when an IP classification is needed earlier, but it can create more DNS activity. The exact behavior should be verified against the Xray version bundled with the client.

{
  "routing": {
    "domainStrategy": "IPIfNonMatch",
    "rules": [
      {
        "type": "field",
        "protocol": ["dns"],
        "outboundTag": "dns-out"
      },
      {
        "type": "field",
        "domain": ["geosite:category-ads-all"],
        "outboundTag": "block"
      },
      {
        "type": "field",
        "ip": ["geoip:private", "geoip:cn"],
        "outboundTag": "direct"
      },
      {
        "type": "field",
        "domain": ["geosite:cn"],
        "outboundTag": "direct"
      },
      {
        "type": "field",
        "network": "tcp,udp",
        "outboundTag": "proxy"
      }
    ]
  }
}

There is an important distinction between routing a DNS request and resolving a destination for routing. A rule with "protocol": ["dns"] handles recognizable DNS traffic, while domainStrategy controls whether Xray resolves a destination to test IP rules. If DNS requests are sent directly but the resulting connection is proxied, the resolver and the application route are still separate decisions. When investigating a leak or a failed regional match, inspect both the DNS section and the routing section.

Avoid DNS loops and unexpected resolution

A DNS outbound can be useful, but it must not route back into itself through a general proxy rule. Give it a dedicated tag and place the DNS protocol rule before broad rules. If the DNS server name itself needs resolution, provide a reachable bootstrap path or use an address that does not require the same unresolved route. Also check whether the client rewrites DNS settings when TUN mode is enabled; v2rayNG and desktop clients can expose different controls even when the underlying Xray fields are similar.

Build and test the configuration safely

Manual routing edits are easiest to debug when you change one policy group at a time. Export or copy the active profile before editing. In v2rayN, inspect the selected profile and its JSON-related advanced settings; in v2rayNG, review the current configuration and routing options before replacing a generated profile. Subscription updates may overwrite local modifications, so keep custom routing in a reusable template or client-side routing section when the application supports it.

  1. Map outbound tags

    Confirm the active profile contains direct, proxy, and, if needed, block and dns-out. Record the local SOCKS port, commonly 10808.

  2. Write narrow rules

    Add private IP, DNS, explicit block, and exception rules first. Use one purpose per rule so a later failure has a clear scope.

  3. Add regional sets

    Place geosite and geoip rules after explicit exceptions, then add one final default route for unmatched traffic.

  4. Validate the JSON

    Check commas, array brackets, duplicate keys, valid outbound tags, and supported fields for the installed Xray core version.

  5. Test one class

    Restart or reload the core, test a local domain, an external domain, a private address, a blocked name, and a literal IP separately.

Use logs to confirm the result rather than judging only by whether a page opens. Enable an appropriate log level temporarily and look for the destination, matched rule, and selected outbound. A useful test matrix includes a local service on 192.168.1.1, a private range such as 10.0.0.0/8, a known blocked domain, a domestic domain set, and an external service. Repeat the tests over both TCP and UDP if your client and applications use both.

Test Expected result If it fails
Private address direct Check the geoip:private or CIDR rule position
Blocked domain block Move the exact domain rule above broad proxy rules
Unmatched external domain proxy Verify the final rule and the proxy outbound tag
DNS request dns-out or selected DNS path Check protocol detection and avoid a DNS routing loop

Create maintainable custom geosite and geoip sets

Built-in geosite and geoip data is convenient, but a real deployment often needs local exceptions. A service may use a domain that is missing from a regional list, or a company network may require a private CIDR that is not represented by a standard set. Put these exceptions in a small, clearly named custom group rather than scattering dozens of individual entries through the main routing array.

For domain sets, use entries that express the matching scope precisely. An exact hostname can use full:api.example.com; a service family can use domain:example.com. For IP sets, use CIDR notation and keep address ranges documented by purpose. Do not add a hostname to an IP list or assume that a domain rule automatically covers connections made directly to its resolved address.

{
  "routing": {
    "domainStrategy": "IPIfNonMatch",
    "rules": [
      {
        "type": "field",
        "domain": [
          "full:updates.example.net",
          "domain:corp.example.net"
        ],
        "outboundTag": "direct"
      },
      {
        "type": "field",
        "ip": [
          "192.168.0.0/16",
          "172.16.0.0/12",
          "10.20.0.0/16"
        ],
        "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",
        "network": "tcp,udp",
        "outboundTag": "proxy"
      }
    ]
  }
}

When a client supports custom geosite or geoip files, the file location and syntax are core-version dependent. Some deployments use a geosite database with named categories, while others use plain domain or CIDR lists generated during deployment. Treat the data file as part of the Xray core installation, not as a universal feature shared identically by every client. After updating Xray-core, verify that the custom file is still in the expected directory and that the category name resolves successfully.

Keep custom data small and reviewable. Use a change record with the domain, reason, intended outbound, date, and source of the address range. Remove temporary test entries after validation. A large keyword list can increase false positives and make future troubleshooting difficult, while a narrow exact-domain rule usually remains understandable months later.

Troubleshoot common routing failures

Most routing problems come from rule order, unavailable metadata, an incorrect outbound tag, or a mismatch between the JSON fields supported by the installed core and the fields copied from another configuration. Start with the active core version rather than the client name alone. v2rayN, v2rayNG, NekoBox, and other clients may expose different interfaces, but the decisive behavior comes from the Xray or sing-box core actually running.

Why does my block rule have no effect?

Check whether a previous domain, geoip, or catch-all network rule already matches the connection. Move the block rule above that broader rule, then reload the profile and inspect the core log.

Why does a domain still use the proxy after I added geoip:cn?

The connection may still be classified by its hostname, or the address may not belong to the current geoip data. Add a precise domain:example.com rule and use IPIfNonMatch only when address-based fallback is actually required.

Why does the configuration fail immediately after editing JSON?

Validate JSON syntax and then verify every outboundTag, domain strategy value, and custom category. A valid JSON document can still be rejected when an Xray field is unsupported by the installed core.

Why do local devices stop opening when proxy mode is enabled?

Add private CIDR ranges and the local gateway range before the final proxy rule. If the application uses a hostname for the local device, add an exact domain rule or ensure local DNS resolution remains available.

When a rule change produces unexpected behavior, revert to the saved profile and reintroduce only one rule group. Test the same destinations with the same client, core, network, and proxy node. This controlled comparison is more reliable than changing DNS, transport, routing, and node selection simultaneously.

Download v2rayN