V2Ray DNS Configuration Guide: Split DNS Routing & Anti-Pollution Setup

Configure split DNS in Xray: mainland China domains use local resolvers, global domains use encrypted DoH, with expectIPs validation and DoH server setup.

In this article

For users who already have a working proxy connection and want to improve DNS resolution quality. After reading, you'll be able to write a split dns section for domestic and foreign domains, understand what expectIPs validation does, and map it to v2rayN's settings.

Two Problems Split DNS Solves

On mainland China networks, direct queries for foreign domains are frequently poisoned: the returned IP doesn't belong to the target site, so connections time out or land on an error page. Encrypted remote resolution wraps the query in a TLS tunnel, so the poisoning source can't tamper with the response — that's DoH's core role in split DNS.

Domestic domains are the opposite. Remote resolution still returns a result, but the query path adds an extra proxy hop, and some Chinese CDNs return different nodes depending on where the query comes from, so remote resolution often gets out-of-region nodes. Handing domestic domains to the local DNS resolves them nearby and makes CDN routing more accurate.

App sends requestDomain rule matchesDomestic: local DNSForeign: DoHConnect by IP
ComparisonLocal DNSRemote DoH
Query pathDirect to ISP or public DNSEncrypted query via proxy outbound
DomainsDomestic domains (geosite:cn)Foreign domains (geosite:geolocation-!cn)
Anti-pollutionWeak — plaintext queriesStrong — tamper-proof inside TLS
LatencyLow — resolves nearbyHigher — extra proxy hop

The goal of split DNS isn't to mechanically split queries, but to give each domain type its own resolution channel while making sure the routing rules and the dns section use the same domain set.

Basic Structure of the dns Section and Split DNS Syntax

Xray's dns section is a servers array matched in order: when a domain matches an entry's domains list, that entry is used; entries without domains match everything and go last as a fallback. Just paste the snippet below into the root of your config.

"dns": {
  "servers": [
    {
      "address": "https://1.1.1.1/dns-query",
      "domains": ["geosite:geolocation-!cn"],
      "tag": "proxy"
    },
    {
      "address": "223.5.5.5",
      "domains": ["geosite:cn"],
      "expectIPs": ["geoip:cn"]
    },
    "223.5.5.5"
  ]
}

The first entry handles foreign domains, sending queries through the proxy outbound; the second handles domestic domains and uses expectIPs to validate the response; the third is a bare-address fallback covering domains not in the geosite data. In v2rayN-generated configs, the proxy outbound tag is proxy and direct is direct — keep them consistent when referencing. Use the proxy server's IP address directly instead of its hostname to skip an extra bootstrap resolution step.

Local DNS

Address
223.5.5.5 or 119.29.29.29
Match
geosite:cn
Validation
expectIPs: geoip:cn
Outbound
direct

Domestic domains resolve nearby, keeping CDN routing accurate.

Remote DoH

Address
https://1.1.1.1/dns-query
Match
geosite:geolocation-!cn
Outbound
tag: proxy
Validation
Optional: geoip:!cn

Queries go through the proxy, so responses can't be poisoned.

Domains that need fixed resolution can go into dns.hosts, which takes priority over the servers array and never sends queries outbound. Internal hostnames and self-hosted service domains belong here.

Pairing routing Rules with domainStrategy

The dns section decides who resolves a domain; routing decides which outbound the connection takes. Configuring only the dns section without routing can still send all traffic through the proxy; configuring only routing without dns leaves domestic domains exposed to remote-resolution poisoning. The two must be set up together.

"routing": {
  "domainStrategy": "IPIfNonMatch",
  "rules": [
    {
      "type": "field",
      "domain": ["geosite:cn"],
      "outboundTag": "direct"
    },
    {
      "type": "field",
      "domain": ["geosite:geolocation-!cn"],
      "outboundTag": "proxy"
    }
  ]
}

When to Use Each of the Three domainStrategy Values

Conclusion: Share One Domain Set Between dns and routing

geosite:cn appears on both sides, and the foreign side uses geosite:geolocation-!cn as its complement, leaving no gap between them — otherwise you get mismatches where resolution goes through DoH but the connection goes direct.

expectIPs Anti-Pollution Semantics and Verification

expectIPs isn't a routing rule — it only validates the resolution result: if the result isn't in the given geoip list, it's discarded and the next server is tried in order. The most common form for domestic entries is expectIPs: ["geoip:cn"], which ensures the local DNS only accepts IPs from mainland China.

{
  "address": "223.5.5.5",
  "domains": ["geosite:cn"],
  "expectIPs": ["geoip:cn", "geoip:private"]
}

expectIPs supports negation: foreign entries can add geoip:!cn to reject results that resolve to Chinese CDN nodes. If you'd rather have a validation failure raise an error than fall back, add skipFallback: true to the entry. geoip:private allows private address ranges so internal domains aren't wrongly discarded.

Verify That Split DNS Is Working

  1. Go to Settings → Parameter Settings → Basic Settings, set the log level to debug, and reconnect the node.
  2. Visit a foreign site and confirm in the log that the domain is resolved by the first DoH entry and the query went through the proxy.
  3. Visit a domestic site and confirm the query hits 223.5.5.5 and the response passes the geoip:cn check.
  4. Run nslookup www.qq.com 223.5.5.5 in the terminal to confirm the local DNS itself is working.

Conclusion: expectIPs Is a Validator, Not a Router

It only discards unacceptable resolution results; it doesn't change traffic direction. Which outbound the traffic takes is decided by routing rules — don't expect expectIPs to do split DNS for you.

Common Issues and Troubleshooting

If you run into problems after configuring split DNS, work through these four checks in order.

Domestic sites got slower after enabling split DNS?

Check the log first to see which server handled the query. The usual cause is outdated geosite:cn data — new domains aren't included and fall through to remote DoH. Update the geosite and geoip data files, or add the domain to dns.hosts to pin it to local resolution.

Remote DoH keeps timing out?

Make sure the DoH entry's tag points to the proxy outbound, and in v2rayN check the corresponding 'Remote DNS via proxy' option. If 1.1.1.1 isn't reachable directly, switch the address to https://dns.google/dns-query or https://8.8.8.8/dns-query.

Foreign domains still resolve to poisoned IPs?

The domain probably isn't covered by geosite:geolocation-!cn. Check that the first dns entry's domains and the routing rules for foreign traffic use the same domain set, then set the log level to debug and confirm the query actually lands on the DoH entry.

Manually edited config.json gets reset when selecting a node?

v2rayN regenerates the config from its built-in template whenever you switch servers, overwriting manual dns edits. Configure split DNS under Settings → Parameter Settings → DNS Settings instead of editing the generated config file directly.

v2rayN Download