July 19, 2026 · Troubleshooting · 9 min read

V2Ray DNS Leak Test and Prevention: Symptoms, Checks, and Config Options

Whether a DNS query goes through the proxy is not decided by a switch in the UI — it is decided by which outbound handles that query in the log. Below we start with how to tell the two apart, then give reproducible checks, and finish with the DNS and routing settings in v2rayN and v2rayNG.

At a glance

This guide is for users who can already connect to a node but are not sure which side resolves their domains. After reading it you will be able to tell intentional direct resolution from an accidental leak, trace where a query goes with access logs and port 53 filtering, and tighten the dns outbound tag, routing rules, domain sniffing, and per-app proxy settings one by one.

How to tell: where the query actually went

A DNS leak is not simply resolution that skipped the proxy. It means the query was handed to a resolver you never intended to use, and it happened outside the proxy tunnel. In V2Ray, a domain can be sent to the remote node as-is and resolved there, or the client can resolve it to an IP first and connect by IP. Both are normal; the difference is that the first never exposes the queried name, while the second leaves the query and its source on the local network.

One thing decides it: which outbound finally handles that port 53 query. If the outbound tag in the access log is a proxy, resolution happened on the node; if the tag is direct, it happened locally. The second case is not a fault — under Bypass Mainland China routing, domains in mainland China are resolved with a local DNS, which is fast and accurate.

The case that actually needs fixing is the third one: a domain that should have been resolved remotely ends up at a local resolver because sniffing is off, the per-app proxy list is misconfigured, or the app ships its own encrypted DNS. This kind of leak never raises an error — it just happens quietly, so you have to hunt it down with logs and packet captures.

App starts resolutionInbound captures queryRouting rules matchDNS outbound resolvesResult written back to connection

That is the normal path. If any link is bypassed — the app never reaches the core, a rule sends the query to direct, or the core does not sniff — the query lands on a local resolver early, and no later proxying of the connection can undo it.

Four typical cases and the exact log lines

The four entries below are the log lines and errors you will see most often while troubleshooting, and they cover most cases of a query going the wrong way. Compare them with your own log to pin down which layer is at fault.

Log: accepted udp:223.5.5.5:53 [socks-in -> direct]

Cause and fix: 223.5.5.5 is an IP in mainland China, so it matches direct in Bypass Mainland China style rules and the query stays local. That is by design for domains in mainland China; if you want every query to leave from the node side, give the dns section its own tag and point it at a proxy outbound with a routing rule.

Symptom: no :53 entries at all in the log

Cause and fix: the query never reached the core — the per-app proxy list excludes that app, or the browser's own secure DNS is using an HTTPS address. Check the proxy list first, then turn off secure DNS in the browser and reproduce once to see whether the log records anything.

Error: failed to find an available destination

Cause and fix: the outbound address resolved to nothing usable, which usually means the DNS query itself was sent into an outbound that does not work. Switch back to local resolution temporarily to confirm the node is reachable, then check where the dns_inbound tag points in your routing rules and how the node address is spelled.

Error: context deadline exceeded

Cause and fix: the query did not come back before the timeout, usually because UDP 53 is dropped upstream or the query went into an outbound that cannot reach anything. Switch the resolver address to an encrypted DNS endpoint starting with https and reproduce again — that tells a link problem apart from a rule problem.

Of these four, the one involving a direct outbound should first be confirmed as intentional; the two error entries mean checking whether the rules and resolver addresses actually work. Keep intentional direct resolution and accidental leaks in separate buckets, or the direct tag in your log will lead you astray.

Checks: reproducible steps to run

None of this needs an online tool. The client's own log plus one port 53 filter is enough to see where resolution goes. The steps work on both v2rayN and v2rayNG, with one extra packet-capture comparison on desktop.

  1. Turn on the access log

    In v2rayN, go to Settings → Parameter Settings → Basic Settings, set Log level to info, and restart the core; in v2rayNG, open the Log page from the menu in the top right of the main screen.

  2. Reproduce a resolution

    Open an ordinary site in the browser that came with your system, not one with secure DNS enabled — otherwise the query target becomes an HTTPS address and a port 53 filter will not show it.

  3. Filter port 53

    Search the log for :53 and check the outbound tag on each line; on desktop, filter udp.port == 53 in Wireshark at the same time to see whether plaintext queries still leave the physical adapter.

  4. Check the proxy list

    Open Per-app proxy and confirm the app that starts the resolution is on the list; on Android, also check the system Private DNS value and whether secure DNS is on in the browser.

The evidence has to line up: the query uses a proxy outbound in the log, no plaintext port 53 query appears on the physical adapter, and the app list has no gaps. If only one of these holds, fix the ones that do not and test again. After switching the resolver address to an encrypted DNS, run everything once more — if the outbound tag in the log and the capture result both change, the change took effect.

On v2rayN: DNS outbound and routing rules

The v2rayN GUI covers most situations, but pinning DNS queries to a proxy outbound exactly requires adding a tag to the dns section in the config. Recent Xray cores (1.8 and later) accept a tag in the dns section, and that tag takes part in routing as an inbound tag; put encrypted resolver addresses first in servers so that even local resolution goes out encrypted.

{
  "dns": {
    "tag": "dns_inbound",
    "queryStrategy": "UseIPv4",
    "servers": [
      { "address": "https://1.1.1.1/dns-query", "domains": ["geosite:geolocation-!cn"] },
      { "address": "223.5.5.5", "domains": ["geosite:cn"] }
    ]
  },
  "routing": {
    "domainStrategy": "IPIfNonMatch",
    "rules": [
      { "type": "field", "inboundTag": ["dns_inbound"], "outboundTag": "proxy" },
      { "type": "field", "domain": ["geosite:cn"], "outboundTag": "direct" }
    ]
  }
}

This config does two things: domains outside mainland China are resolved through encrypted DNS, domains in mainland China through local resolution; and DNS queries themselves carry the dns_inbound tag, which the first rule sends to a proxy outbound. Order matters — the dns_inbound rule must come before the domain rules, or a rule such as geosite:cn will grab the query first and the tag will do nothing.

SettingWhereRecommendedEffect
Routing modeSettings → Parameter Settings → Routing SettingsBypass Mainland ChinaDomains in mainland China go direct, everything else through the proxy
Domain resolution strategySettings → Parameter Settings → Routing SettingsIPIfNonMatchSend domains to the remote side first, fall back to IP matching
Domain sniffingSettings → Parameter Settings → Routing SettingsOn, with HTTP and TLS selectedRecovers the domain from the handshake so IP-based rules do not misfire
DNS serversSettings → Parameter Settings → DNS SettingsEncrypted resolvers firstWhen local resolution is needed, the query still leaves encrypted
Outbound domain strategysockopt.domainStrategy in the outbound configAsIsDomains go to the node as-is and are resolved there

Takeaway: give DNS its own route first

Encrypted DNS only hides what you query; it does not decide which outbound carries the query. The right order is to add the dns tag, point dns_inbound at a proxy outbound with a routing rule, and only then switch to encrypted resolver addresses. Do it the other way round and an encrypted query can still leave through a direct outbound.

On v2rayNG for Android: routing mode, sniffing, and per-app proxy

v2rayNG runs the Xray core, and once connected the system VpnService takes over traffic, so app connections enter the core before being routed. On Android the leak points cluster in three places: the per-app proxy list, the domain sniffing switch, and the system Private DNS. The steps below follow v2rayNG; v2flyNG uses similar setting names, so you can look them up side by side.

One more thing that is easy to miss: some browsers and apps ship their own secure DNS, so the query target is the resolver's HTTPS address rather than port 53, and searching the log for :53 finds nothing. Either add the resolver's domain to your proxy rules, or turn secure DNS off in the browser settings so resolution goes back to the dns section of the core.

Takeaway: on Android, check the app list before sniffing

A gap in the per-app proxy list lets a whole app and its DNS bypass the core, which has a far wider impact than the sniffing switch. Check in this order: the app list, sniffing, Private DNS, and only then the dns section of the config.

Common questions

With sniffing off, routing still works by IP. What does that actually change?

The domain is resolved locally to an IP first, the connection is made by IP, and direct and proxy rules can only match on IP. CDN domains in mainland China are easily sent through the proxy by mistake, and the resolution request stays on the local network. Turning sniffing on fixes both at once.

Under Bypass Mainland China, domains in mainland China use a local DNS. Does that count as a leak?

No. That is intentional direct resolution, and the query goes to a local resolver you chose yourself. To have domains in mainland China resolved on the node side too, switch routing mode to Global and accept the change in resolution latency that comes with it.

The resolver shown in the test results is not the DNS you configured. Why?

First check whether secure DNS is on in the browser, then whether Private DNS is set in the system. Both hand resolution to another resolver at the app layer, where the client config has no say. Turn them off one at a time and test again.

Subscription updates keep timing out. Is DNS involved?

Yes. The subscription domain may be resolved over a direct connection by default, and if that resolution is poisoned or times out, the update fails. Tick Update subscription via proxy in the subscription settings so both the request and the resolution go through the node.

An encrypted DNS address was set but has no effect?

First confirm the core version supports that syntax, then check that the tag in the dns section is referenced by a routing rule, and restart the core after editing the config. It has only taken effect once the outbound tag on DNS queries changes in the log.

Treat DNS as ordinary traffic and the picture gets clear: see which outbound handles it, give it its own tag and rules, then decide which domains keep local resolution. The GUI options in v2rayNG and v2rayN cover the vast majority of cases; the remaining precision comes from the dns and routing sections of the config.

Download v2rayNG for Android and v2rayN for desktop Xray core, with VMess / VLESS / Trojan / SS subscription import; the download page lists the right build and install method for each platform.
Go to the download page Browse the guides
Download v2rayNG