Sep 9, 2026 · Advanced Config · About 12 min

DNS Routing and Resolution

Learn how dns.servers, domains, and expectIPs work with geosite rules to route DNS queries for domains in and outside mainland China, and reduce incorrect DNS results.

At a Glance

This guide is for users who can already connect to a node and want to adjust DNS and routing rules. The examples use the traditional Xray JSON configuration and separate resolver selection, response filtering, and outbound routing. You'll also find configuration snippets you can merge and steps to verify the results. Check field support against your actual V2Fly core version; client names alone don't guarantee compatibility.

Separate DNS Resolution from Outbound Routing

DNS routing determines which resolver receives a domain lookup; routing rules determine which outbound handles the connection. They’re related, but they aren’t the same setting. Assigning a domestic resolver to a domain doesn’t mean the connection will go direct. Likewise, routing a connection through a proxy doesn’t mean the app’s earlier DNS lookup went through that proxy.

Domain enters the core Domain rules are checked Resolve an address if needed IP rules are checked Select an outbound

This flow matches the routing.domainStrategy: "IPIfNonMatch" used in this guide: the core checks domain rules first and resolves the address for IP rules only if no domain rule matches. If a domain rule matches, the routing stage may not query the built-in DNS at all. Whether resolution happens later depends on how the selected outbound handles the domain. So, no DNS log entry doesn’t necessarily mean the configuration isn’t working.

servers, domains, and expectIPs

dns.servers is a resolver list. It can contain server addresses or objects with matching conditions. An object’s domains field selects queries by domain, while expectIPs checks whether the server’s response contains addresses in the expected range. One selects the query target; the other filters the result. They aren’t interchangeable.

Resolver for Domains in Mainland China

address
223.5.5.5
domains
geosite:cn
expectIPs
geoip:cn
skipFallback
true

Prioritize domains in mainland China and exclude this resolver from the general fallback pool for other domains.

General Fallback Resolver

address
https://1.1.1.1/dns-query
Transport
HTTPS
domains
Any
Query Type
Inherit global UseIPv4

Handles queries that don’t match a dedicated rule, or whose dedicated resolver response fails the filter.

skipFallback: true means “exclude this server from general fallback,” not “never fall back after this server is tried.” If a response for a domain in mainland China fails expectIPs, the example can still try another fallback resolver. Confusing these two behaviors can lead to incorrect assumptions about where a query goes.

geosite:cn uses domain classification data; geoip:cn uses IP classification data. Neither is a real-time geolocation service, and neither determines a region from a website’s display language. If a service in mainland China uses an overseas IP, a cross-region CDN, or outdated classification data, strict address filtering may reject valid results.

Combine DNS and Routing Snippets

The JSON below is not a complete node configuration. It assumes your existing outbounds already contain the direct and proxy tags, for direct and verified proxy connections respectively. When merging, update the existing dns and routing objects. Don’t declare the same keys twice in one object.

{
  "dns": {
    "queryStrategy": "UseIPv4",
    "servers": [
      {
        "address": "223.5.5.5",
        "domains": [
          "geosite:cn"
        ],
        "expectIPs": [
          "geoip:cn"
        ],
        "skipFallback": true
      },
      "https://1.1.1.1/dns-query"
    ]
  },
  "routing": {
    "domainStrategy": "IPIfNonMatch",
    "rules": [
      {
        "type": "field",
        "ip": [
          "223.5.5.5/32"
        ],
        "port": "53",
        "outboundTag": "direct"
      },
      {
        "type": "field",
        "ip": [
          "1.1.1.1/32"
        ],
        "port": "443",
        "outboundTag": "proxy"
      },
      {
        "type": "field",
        "domain": [
          "geosite:cn"
        ],
        "outboundTag": "direct"
      },
      {
        "type": "field",
        "ip": [
          "geoip:private",
          "geoip:cn"
        ],
        "outboundTag": "direct"
      },
      {
        "type": "field",
        "network": "tcp,udp",
        "outboundTag": "proxy"
      }
    ]
  }
}

The first two rules control how resolver connections are routed: queries to 223.5.5.5:53 go direct, while connections to 1.1.1.1:443 use the proxy. Standard https:// DoH endpoints use the core’s routing system; don’t replace them directly with the +local form, which follows a different connection path. These IP-and-port rules also match other traffic to the same destinations, not just DNS traffic.

53
UDP destination port for the mainland China resolver
443
HTTPS destination port for the DoH resolver
2
Upstream resolvers used in this example

Keep the rules in order. Resolver outbound rules come first to prevent the DoH address from being rerouted unexpectedly by later IP classification rules. The final rule explicitly sends remaining TCP and UDP traffic through proxy, rather than relying on the default order of the outbound array. If you have existing LAN, blocking, or application-specific rules, integrate them individually. Replacing the whole section won’t preserve their behavior.

Check: Set Resolver Routes Before Comparing Responses

First confirm that 1.1.1.1:443 actually matches proxy, then inspect the domain’s resolution result. Changing the DNS address without checking the outbound route can’t confirm that the remote query was sent as intended.

Check the Direct Outbound’s DNS Strategy Too

When a domain in mainland China matches a domain rule, its connection goes through direct. If that outbound uses the Freedom protocol with its default resolution strategy, the final connection may still use the system resolver instead of this guide’s built-in DNS. To make the direct outbound explicitly use the built-in resolver, check the existing outbound and adjust settings.domainStrategy.

{
  "tag": "direct",
  "protocol": "freedom",
  "settings": {
    "domainStrategy": "UseIPv4"
  }
}

This is one object in the outbounds array. Update the existing direct outbound instead of adding another with the same tag. Here, UseIPv4 and the DNS query strategy above provide an IPv4 baseline that’s easier to troubleshoot; they don’t mean IPv6 must be disabled.

Verify the Effective Configuration in Your Client

v2rayN is a desktop client, v2rayNG uses the Xray core, and v2flyNG uses the V2Fly core. The client interface generates the runtime configuration, so a field appearing in an editor doesn’t mean the active core is using it. This guide isn’t tied to a particular interface version: always verify the active node, running core, and final generated JSON.

  1. Save a baseline: Back up the working configuration and note the node, core version, system proxy status, and whether a virtual network adapter is enabled. Change only DNS and related routing settings at a time; don’t also switch nodes or transport options.
  2. Inspect the runtime file: Confirm the final JSON contains both dns.servers entries, the expected outbound tags, and the rules in the correct order. If the client generates the configuration, edit its persistent configuration source—not a temporary file that will be overwritten at the next launch.
  3. Enable diagnostic logging: Temporarily set log.loglevel to debug, restart the core, and make the request again. Log details vary by version; look for the resolver destination, matched outbound, and any errors.
  4. Test separately: Choose one domain confirmed to be in geosite:cn and another that isn’t. Close duplicate connections and restart the app if needed, so reused connections don’t hide new DNS behavior.
  5. Restore normal logging: When testing is done, set the log level back to warning or its original value. Before sharing logs, remove node addresses, subscription links, visited domains, and other sensitive information.

If the relevant core command is available on your device, run a syntax and load check from the configuration file’s directory. The commands below are common command-line forms for Xray and V2Fly; run only the one for your core. A successful configuration test doesn’t mean the upstream resolver is reachable.

xray run -test -config config.json
v2ray test -c config.json

Don’t use the result of nslookup example.com 223.5.5.5 as proof that the core’s DNS routing works. This command queries the specified server directly. It’s useful for checking whether that query path is available, but it doesn’t use this guide’s domain classification, fallback, or proxy routing rules. Test and record system DNS and core-generated queries separately.

DNS Spoofing Protection: Limits and Common Issues

Here, “DNS spoofing protection” means reducing incorrect results by encrypting remote queries, controlling the outbound route, and filtering unexpected addresses. No single field guarantees this. DoH protects queries in transit to the resolver, but the resolver’s trustworthiness, queries made separately by apps, and system DNS caching can all affect the final connection.

Mainland China websites are suddenly slow too. What should I change first?

Temporarily remove expectIPs from the mainland China resolver and retest without changing anything else. If latency improves, check whether the domain’s actual response matches the geoip:cn classification. Don’t add more fallback resolvers to mask the cause.

Why am I still seeing local DNS queries with remote DNS configured?

First identify whether the query comes from the browser, the operating system, node-domain resolution, or the core. Check the app’s secure DNS settings, the scope of the system proxy, and Freedom’s domainStrategy. The core’s DNS settings don’t automatically stop other processes from sending queries.

The core won’t start after I save the configuration. How do I troubleshoot it?

Check whether the first error points to invalid JSON, an unknown field, or a problem loading classification data. Confirm the core can read geosite.dat and geoip.dat, and that direct and proxy are existing outbound tags.

DoH keeps timing out, but the proxy node connects. What should I check?

Confirm that requests to 1.1.1.1:443 use the proxy outbound, then check connectivity from the node to the resolver. Check the system clock and look for certificate errors; don’t bypass the failure by disabling certificate validation. If needed, test a different DoH endpoint that you can verify.

Why are websites still connecting to the old IP after I changed the settings?

Exit and reopen the app, restart the core, and test a new connection. The browser, operating system, core, and remote resolver may all cache results. Refreshing a page may reuse an existing connection, so it doesn’t prove the new rules failed.

Keep the Configuration Minimal and Easy to Explain

A maintainable split-DNS setup should answer three questions: Why did the domain select this resolver? Why did the resolver connection use this outbound? Why did the final destination go direct or through a proxy? Answer these with logs and the runtime configuration before adding more rules; it’s easier to troubleshoot than simply stacking resolver addresses.

Clients and Downloads