Xray Routing And DNS Leak Prevention: Advanced JSON Guide
Why routing and DNS must be designed together
Xray routing and DNS settings are often configured in separate sections, but they solve two parts of the same decision. DNS answers a basic question—what address belongs to a domain—while routing decides whether the resulting connection should go through the proxy, bypass it, or be rejected. If DNS is resolved by the local network before Xray evaluates the request, a configuration can appear to work while still exposing domain lookups to an ISP, hotel network, or corporate resolver.
A DNS leak is therefore not limited to a browser showing the wrong IP address. It can also mean that the domain name was sent to an unintended resolver, that a direct DNS query revealed the destination before routing took effect, or that an application used its own resolver outside Xray. The practical goal is not to force every packet through one remote server. The goal is to make the resolution path deliberate, match DNS behavior to routing policy, and verify the result from outside the client.
This guide focuses on Xray-core JSON concepts used by desktop and Android clients such as v2rayN and v2rayNG. Exact menu names vary between client versions, but the relationships among dns, inbounds, routing, sniffing, FakeDNS, geosite, and geoip remain the important parts. Build a small working configuration first, then add policies one at a time. A large ruleset that has never been tested is difficult to trust.
How Xray resolves and routes a domain
When an application connects to a hostname, several things may happen before the connection reaches its destination. The application may perform its own DNS lookup, send a DNS request to the operating system, or pass a domain directly to a proxy-aware client. Xray may receive an IP address, a domain name, or a synthetic address generated by FakeDNS. These cases affect which routing rules can match.
With a domain-aware request, Xray can evaluate rules such as domain:example.com, domainSuffix:example.com, geosite:category-ads-all, or a user-defined domain list. With an IP-only request, domain rules may not be available unless sniffing recovers the hostname from the protocol. This is why enabling sniffing can improve policy accuracy for HTTP, TLS, and QUIC traffic, but it should not be treated as a universal solution.
After a routing rule matches, its outboundTag selects an outbound. A common design uses one outbound for the proxy, one for direct access, and one for blocking. The order of rules matters because Xray evaluates them from top to bottom. Put narrow exceptions before broad geosite or geoip rules. If a general rule appears first, the later exception will never be reached, even if the exception itself is written correctly.
DNS resolution can happen before or during routing, depending on the request type and configuration. The domainStrategy value tells the router how to handle domains when IP-based rules are involved. A policy such as AsIs keeps the original domain-oriented behavior, while values such as IPIfNonMatch or IPOnDemand allow Xray to resolve a domain when a domain rule did not match or when an IP rule requires an address. Choose this deliberately: resolving every domain can add latency and create unexpected direct queries, while never resolving domains can make geoip rules ineffective.
A safe JSON baseline for domain handling
Start with a minimal policy that separates resolution from connection routing. The following values are useful building blocks to enter in the client’s JSON configuration rather than a complete universal profile: an Xray dns object with a remote resolver, a routing object with domainStrategy, and outbounds tagged consistently as proxy, direct, and block. Keep the names stable. A rule referring to proxy will fail operationally if the actual outbound is called Proxy or streaming.
A compact DNS policy can use an object shaped like "dns": {"servers": ["https+local://1.1.1.1/dns-query"], "queryStrategy": "UseIP"}. The exact resolver is a policy choice, not a magic value. Some environments need a provider reachable through the proxy; others need a local resolver for domestic domains. If you use a remote DoH endpoint, make sure the endpoint itself is reachable under your intended bootstrap method. Otherwise Xray may be unable to resolve the resolver hostname before the proxy is available.
For routing, a useful starting point is "routing": {"domainStrategy": "IPIfNonMatch", "rules": [...]}. This means Xray tries domain rules first and resolves the name when no domain rule matches but an IP-based decision may be needed. It is usually easier to reason about than immediately selecting an aggressive strategy. After testing, you can move to IPOnDemand if your rules genuinely need IP information during routing.
Place a private-network bypass near the top when local resources must remain reachable. A representative rule is {"type": "field", "ip": ["geoip:private"], "outboundTag": "direct"}. Follow it with explicit blocked domains, advertising lists, or provider-specific exceptions. A final catch-all rule such as {"type": "field", "network": "tcp,udp", "outboundTag": "proxy"} creates a predictable default. Without a final policy, unmatched traffic may use an unexpected outbound or produce confusing behavior during testing.
Do not copy a JSON fragment without checking whether the surrounding client already supplies a DNS section, routing section, or outbound with the same tag. Duplicate top-level keys can overwrite one another in some editors, and duplicate outbound tags make logs difficult to interpret. Treat each fragment as a component to merge, not as permission to paste two complete profiles together.
Sniffing, routeOnly, and domain recovery
Sniffing helps Xray discover the hostname associated with traffic that arrives as an IP address. A typical inbound setting uses "sniffing": {"enabled": true, "destOverride": ["http", "tls", "quic"], "routeOnly": true}. HTTP Host headers, TLS server names, and some QUIC metadata can then participate in routing. This is particularly useful when an application connects to a CDN address but the policy should be based on the requested domain.
routeOnly is an important safety control. When it is enabled, the sniffed domain is used for route evaluation without necessarily replacing the original destination used to establish the connection. That separation reduces surprising destination rewrites and makes the feature easier to introduce into an existing profile. It does not mean that every application will reveal its hostname. Encrypted ClientHello, unusual protocols, pinned connections, and applications that connect directly to known IPs may still hide useful information.
Sniffing should also be scoped sensibly. Enabling every possible protocol handler can increase complexity and may produce misleading matches. Start with http, tls, and quic when those protocols are relevant. If a particular application breaks after sniffing, compare behavior with sniffing disabled, inspect the actual destination in the Xray log, and add only the protocol support you need.
There is another common mistake: assuming sniffing prevents DNS leaks by itself. It does not. Sniffing observes connection metadata after traffic reaches Xray. It cannot undo a DNS query that the application already sent directly to the operating system. To reduce that exposure, combine routing with an appropriate DNS capture design, system VPN or TUN behavior, and application settings that do not bypass the client.
FakeDNS: useful for interception, not a universal cure
FakeDNS gives a domain a synthetic IP address from a configured pool. When an application resolves a hostname, it receives that synthetic address; Xray can later map the address back to the original domain and apply domain routing. This is valuable for transparent proxying, TUN mode, and applications that otherwise hand Xray only an IP address.
A FakeDNS configuration usually includes a pool under dns.fakeDns and a matching FakeDNS object in the relevant inbound settings. The pool must not overlap with real private networks used by your LAN, VPN, containers, or corporate routes. An overlap can make an ordinary internal address look like a synthetic address, causing failed connections or traffic being sent to the wrong outbound.
FakeDNS also requires cooperation from the client and operating system. It is not enough to paste a FakeDNS object into JSON while the device continues sending DNS requests outside the Xray tunnel. On desktop, confirm whether the selected TUN implementation captures DNS and whether another VPN or security product owns the virtual adapter. On Android, check VPN permission, per-app exclusions, and whether private DNS or an application-specific resolver is bypassing the client.
Use FakeDNS when you need reliable domain recovery for transparent traffic, not simply because it sounds more private. It adds state, pool management, and troubleshooting cases. Some software expects literal IP answers, performs certificate checks against unusual destinations, or maintains long-lived connections that behave differently when synthetic addresses are introduced. Test browsers, command-line tools, messaging applications, and local services separately before making FakeDNS the default for every device.
Using geosite and geoip without creating contradictions
Geosite rules classify domains, while geoip rules classify IP addresses. They answer different questions. A rule such as "domain": ["geosite:category-ads-all"] can block known advertising domains. A rule such as "ip": ["geoip:private"] can keep local networks direct. A regional policy may use a local geosite list for domains and a geoip list for destination addresses, but neither list is automatically correct for every provider or country.
Rule order should reflect your actual policy. A practical sequence is private and local exceptions first, explicit block lists second, essential services or trusted domains third, regional direct rules next, and the proxy catch-all last. If a streaming service must always use the proxy, place its domain rule before a broad direct geosite rule. If a local dashboard must never leave the LAN, place its private IP rule before any general proxy rule.
Be careful with domain matching. An exact domain entry is narrower than a suffix-style entry. A policy for example.com may not cover api.example.com unless the matching syntax or list format explicitly includes subdomains. Conversely, a broad suffix rule can unexpectedly catch unrelated services under the same parent domain. Review the generated or downloaded geosite list rather than trusting its label alone.
Geoip decisions can be affected by the resolution strategy. If Xray has only a domain and your rules never trigger DNS resolution, an IP rule cannot match. If Xray resolves through a resolver that returns a nearby CDN address, the result may not represent the organization’s nominal country. Use geoip for broad routing, not as a guarantee of content availability. For important services, an explicit domain rule is normally easier to maintain and explain.
Testing, logging, and maintaining custom rules
Test one question at a time. First confirm that a known node connects. Then test a domain that should use the proxy, a domain that should go direct, a blocked domain, and a private address. Record the expected outbound for each case before changing the configuration. This prevents a successful browser page from hiding a policy error elsewhere.
Enable an appropriate Xray log level temporarily and inspect whether the request arrived with a domain or only an IP, whether sniffing recovered a hostname, which routing rule matched, and which outbound tag was selected. Avoid leaving verbose logs enabled permanently on a busy phone or desktop because they can expose browsing metadata locally and consume storage. After testing, return to a quieter level and keep only the logs needed for future diagnosis.
Check DNS from outside the client as well as inside it. A browser-based DNS leak test can show which public resolvers are visible, while command-line tools such as nslookup or dig can reveal the resolver used by the operating system. Run tests with system proxy, TUN, and Android VPN modes separately. A clean result in system-proxy mode does not prove that an application ignoring system proxy is protected.
When a leak appears, simplify before adding more rules. Temporarily remove geosite and geoip policies, disable FakeDNS, and test a single remote DNS path. Then restore one feature at a time. Common causes include a DNS server listed as a plain local address, a direct resolver used for bootstrap, a TUN exclusion, an Android per-app bypass, or an application using encrypted DNS independently. Reinstalling the client rarely fixes these policy-level problems.
Finally, maintain the configuration like software. Keep a clean baseline, document why each rule exists, and separate custom domain lists from generated geosite or geoip data. After updating Xray-core or a client, retest sniffing, FakeDNS, DNS transport, and the most important routing exceptions. If a provider changes its domains or a list changes semantics, a previously correct rule may become too broad or too narrow. A small, ordered, tested JSON policy is safer than a large configuration copied from several unrelated examples.