# Xray routing JSON: ordinary custom rules

Source: https://onexray.com/docs/configuration/custom-routing/
Description: Write importable OneXray routes with empty server slots, ordered domain/IP rules, local DNS, FakeDNS and Geodata dependencies.


Import through Connect → traffic method → Custom Routing → Normal configuration, not ordinary Servers import. Select the imported profile and choose existing servers on Connect. This chapter describes the ordinary form-backed format only. For user-owned DNS, sniffing and additional inbounds, use the separate [Advanced JSON template](https://onexray.com/docs/configuration/advanced-routing/). Both types share three saved profiles; editing does not convert between them.

## Accepted document

| Field | Contract |
| --- | --- |
| `name` | Include a unique, nonempty display name, at most 32 characters |
| `outbounds` | Required: 1–3 empty objects; their count is the entry-node count |
| `routing.domainStrategy` | Write `IPIfNonMatch`; the App normalizes this value |
| `routing.rules` | Ordered list of rules below; an empty list uses the default outbound behavior |
| `dns` | Optional; the tagged Local DNS form below with an optional FakeDNS server, not arbitrary Xray DNS |
| `geodata.assets` | Optional import-only custom file dependencies, each with `file` and HTTPS `url` |

No other root fields belong to Custom. Do not define real nodes, `direct`, `block`, `dnsOut`, balancers, inbounds, logs, metrics, observatory, root FakeDNS pools or UI flags in this file.

## Rule fields and actions

Supported conditions: `domain` (string array), `ip` (string array), `port` (for example `"443"`, `"8000-8080"`, `"80,443"`), `network` (`"tcp"`, `"udp"` or `"tcp,udp"`), `protocol` (string array) and `localOS` (string array). `ruleTag` names the rule. The editor groups protocol and OS under “More matching conditions”.

Select **exactly one** action:

| Desired action | JSON |
| --- | --- |
| VPN | `"balancerTag": "proxy"` |
| Direct | `"outboundTag": "direct"` |
| Block | `"outboundTag": "block"` |

Do not write `outboundTag: proxy`. Do not write `type`, `enabled`, `sourceIP`, `sourcePort`, `attrs`, `inboundTag` or process conditions. Unsupported fields are rejected rather than silently removed.

Different condition types in one rule are AND. Multiple ordinary values within one condition are alternatives. Prefer separate rules when the intent is OR across domain and IP. Core-specific negated IP lists retain Core semantics; do not apply the simple OR explanation to a negated list without checking it.

Domain forms: `full:host.example.com` for one hostname, `domain:example.com` for that domain and subdomains, `geosite:CN` for a verified default category, `ext:other.dat:category` for a verified custom category. A bare string is a keyword match, not an exact-host match. Do not silently convert user intent to a broad substring or regular expression.

IP forms: an address, CIDR, `geoip:CN`, or `ext:other.dat:category`. Use [verified Geodata](https://onexray.com/docs/configuration/geodata/).

## Order and fallback

Rules are checked in order. With `IPIfNonMatch`, a domain destination is resolved for another IP-aware matching pass only when the first pass fails. Avoid an unconditional final proxy rule: it can prevent the IP pass.

OneXray generates a round-robin balancer `proxy`, even with one entry, with fallback `direct`. **Unmatched traffic uses the first outbound, not the balancer.** Add explicit proxy rules for destinations that should use balancing. Direct fallback is not a fail-closed guarantee.

Normal mode supplies the real nodes and system outbounds. Their tags are implementation details; only the three action references above belong in a Custom file.

## Local DNS

If present, include exactly one `{"tag":"app-dns-direct","address":"8.8.8.8"}` server. Change its address, not its tag. To enable FakeDNS, also include the `app-dns-fake` server described below. Do not add `domains`, `queryStrategy`, `skipFallback`, a port property or arbitrary DNS fields. A nondefault port belongs in a Core-supported address string, such as `tcp://192.168.50.53:5353`.

Only pure direct-domain rules contribute domains to this resolver. Combining a domain with IP, port, network, protocol or OS conditions prevents that rule from contributing DNS domains. IP-only direct rules do not resolve internal names. Proxy DNS remains `8.8.8.8`. See [DNS behavior](https://onexray.com/docs/configuration/dns/).

## Complete example

This recipe includes ad blocking, GitHub through VPN, and combined Apple/Microsoft/Bing/China/private direct rules. It needs both default DAT files and two eligible entry nodes. It has no credentials or unresolved placeholders.

[JSON: custom-cn.json](https://onexray.com/examples/custom-cn.json)

```json
{
  "name": "Mainland China direct",
  "outbounds": [
    {},
    {}
  ],
  "dns": {
    "servers": [
      {
        "tag": "app-dns-direct",
        "address": "8.8.8.8"
      }
    ]
  },
  "routing": {
    "domainStrategy": "IPIfNonMatch",
    "rules": [
      {
        "ruleTag": "Block ads",
        "domain": [
          "geosite:CATEGORY-ADS-ALL"
        ],
        "outboundTag": "block"
      },
      {
        "ruleTag": "GitHub through VPN",
        "domain": [
          "geosite:GITHUB"
        ],
        "balancerTag": "proxy"
      },
      {
        "ruleTag": "Direct domains",
        "domain": [
          "geosite:PRIVATE",
          "geosite:APPLE",
          "geosite:MICROSOFT",
          "geosite:BING",
          "geosite:CN"
        ],
        "outboundTag": "direct"
      },
      {
        "ruleTag": "Direct IPs",
        "ip": [
          "geoip:PRIVATE",
          "geoip:CN"
        ],
        "outboundTag": "direct"
      }
    ]
  }
}
```


## Protocol and operating-system conditions

Use `protocol` (`http`, `tls`, `quic`, `bittorrent`) and `localOS` (`ios`, `android`, `darwin`, `windows`, `linux`), both arrays. Protocol means sniffed application traffic, **not** VLESS/VMess. OS means the OS running Xray, not a remote server or an individual Android app.

[JSON: custom-protocol.json](https://onexray.com/examples/custom-protocol.json)

```json
{
  "name": "Block QUIC on mobile",
  "outbounds": [
    {}
  ],
  "dns": {
    "servers": [
      {
        "tag": "app-dns-direct",
        "address": "8.8.8.8"
      }
    ]
  },
  "routing": {
    "domainStrategy": "IPIfNonMatch",
    "rules": [
      {
        "ruleTag": "Mobile QUIC",
        "protocol": [
          "quic"
        ],
        "localOS": [
          "ios",
          "android"
        ],
        "outboundTag": "block"
      }
    ]
  }
}
```


## FakeDNS

FakeDNS is off by default and is represented by an additional tagged DNS server, not a boolean root field. Include the direct server too; do not export generated pools or sniffing.

[JSON: custom-fakedns.json](https://onexray.com/examples/custom-fakedns.json)

```json
{
  "name": "FakeDNS routing",
  "outbounds": [
    {}
  ],
  "dns": {
    "servers": [
      {
        "tag": "app-dns-direct",
        "address": "8.8.8.8"
      },
      {
        "tag": "app-dns-fake",
        "address": "fakedns"
      }
    ]
  },
  "routing": {
    "domainStrategy": "IPIfNonMatch",
    "rules": [
      {
        "ruleTag": "Private networks",
        "ip": [
          "geoip:PRIVATE"
        ],
        "outboundTag": "direct"
      }
    ]
  }
}
```


[Compatibility](https://onexray.com/docs/configuration/compatibility/) · [FakeDNS limitations](https://onexray.com/docs/configuration/dns/) · [import dependencies](https://onexray.com/docs/configuration/geodata/)


