At a glance

For users who have imported a server profile and want to understand the core config. Follow a request through the four top-level fields, then examine a VMess + WebSocket + TLS example and check tag references, rule order, and DNS. By the end, you’ll know whether to adjust an inbound, server settings, or routing—instead of rewriting the entire JSON every time a connection fails.

1. Config files vs. client settings: know the difference

v2rayN and v2rayNG are clients for managing servers, updating subscriptions, and generating runtime configs for the core. v2rayNG uses the Xray core; v2flyNG uses the v2fly core. v2rayN can manage different cores, but this guide covers the traditional V2Ray/Xray JSON format—not a universal field reference for every core.

Here, config.json means the config actually read by the core—not a client settings file that stores things like window position or subscription groups. The filename may vary: check which file the launch command specifies and whether its contents include fields such as inbounds and outbounds. A client may overwrite a generated file when it updates servers or restarts the core.

App requestLocal inboundRouting matchSelected outboundDestination

2. Complete JSON example: two inbounds, three outbounds

Here’s a complete config to explore. node.example.invalid is a deliberately fake domain, and the UUID is for demonstration only, so this config can’t connect to a real server. The example uses the traditional VMess vnext structure. Use the server’s actual address, port, UUID, transport path, and TLS name in a real config.

{
  "log": {
    "loglevel": "warning"
  },
  "inbounds": [
    {
      "tag": "socks-in",
      "listen": "127.0.0.1",
      "port": 10808,
      "protocol": "socks",
      "settings": {
        "auth": "noauth",
        "udp": true
      },
      "sniffing": {
        "enabled": true,
        "destOverride": ["http", "tls"],
        "routeOnly": true
      }
    },
    {
      "tag": "http-in",
      "listen": "127.0.0.1",
      "port": 10809,
      "protocol": "http",
      "settings": {}
    }
  ],
  "outbounds": [
    {
      "tag": "proxy",
      "protocol": "vmess",
      "settings": {
        "vnext": [
          {
            "address": "node.example.invalid",
            "port": 443,
            "users": [
              {
                "id": "00000000-0000-4000-8000-000000000001",
                "alterId": 0,
                "security": "auto"
              }
            ]
          }
        ]
      },
      "streamSettings": {
        "network": "ws",
        "security": "tls",
        "tlsSettings": {
          "serverName": "node.example.invalid"
        },
        "wsSettings": {
          "path": "/ws",
          "headers": {
            "Host": "node.example.invalid"
          }
        }
      }
    },
    {
      "tag": "direct",
      "protocol": "freedom",
      "settings": {}
    },
    {
      "tag": "block",
      "protocol": "blackhole",
      "settings": {}
    }
  ],
  "routing": {
    "domainStrategy": "AsIs",
    "rules": [
      {
        "type": "field",
        "domain": ["full:blocked.example.invalid"],
        "outboundTag": "block"
      },
      {
        "type": "field",
        "ip": ["geoip:private"],
        "outboundTag": "direct"
      },
      {
        "type": "field",
        "domain": ["full:localhost"],
        "outboundTag": "direct"
      }
    ]
  },
  "dns": {
    "servers": ["223.5.5.5"]
  }
}
10808
Example SOCKS inbound port
10809
Example HTTP inbound port
443
Example remote TLS port

These are example ports, not fixed defaults across all versions. In JSON, a port is a number and true is a Boolean—neither should be quoted as a string. Don’t add a trailing comma after the last item. Comments are omitted so the example can be validated as-is.

3. inbounds: listen address, protocol, and sniffing

listen sets the local address that accepts connections, port sets the port, and protocol tells the app which proxy protocol to use. A SOCKS client can’t connect directly to an HTTP inbound; an open port doesn’t mean the protocol is right. For manual app setup, this example uses SOCKS5 at 127.0.0.1:10808 or an HTTP proxy at 127.0.0.1:10809.

SOCKS inbound

tag
socks-in
listen
127.0.0.1
port
10808
protocol
socks
settings.udp
true

Listens on localhost only. For UDP, the app must also use SOCKS5 UDP relay.

HTTP inbound

tag
http-in
listen
127.0.0.1
port
10809
protocol
http
settings
Empty object

For apps that support HTTP proxies; HTTPS requests typically use a CONNECT tunnel.

127.0.0.1 restricts the listener to the local machine. Changing it to 0.0.0.0 makes it listen on all IPv4 interfaces. The example SOCKS inbound also uses auth: "noauth", so don’t expose it to a network without changes. If you need to share the proxy, review authentication, firewall rules, and allowed source addresses together.

sniffing can identify a destination domain from an HTTP Host header or the server name in a TLS handshake, helping domain-based routing rules match. routeOnly: true means the sniffed name is used only for routing; it does not replace the original connection target. Sniffing doesn’t decrypt HTTPS or guarantee a domain will be available for every connection. In this example, it’s enabled only on the SOCKS inbound.

4. outbounds: separate server authentication from transport

For a proxy outbound, protocol: "vmess" specifies the server protocol. settings.vnext holds the server list, while users contains authentication parameters. id is the user’s UUID; alterId: 0 is the common setting for modern VMess—don’t try nonzero values from outdated guides. users[].security: "auto" is a VMess encryption option, not a TLS toggle.

FieldExample valueWhat it does
vnext[].addressnode.example.invalidServer address the core connects to
vnext[].port443Server listening port, not the local inbound port
streamSettings.networkwsUses WebSocket transport
streamSettings.securitytlsEnables TLS at the transport layer
tlsSettings.serverNamenode.example.invalidTLS server name, typically used for SNI and certificate name validation
wsSettings.path/wsWebSocket request path; must match the server
wsSettings.headers.Hostnode.example.invalidHTTP Host header for the WebSocket handshake

The example uses the same value for the address, SNI, and Host to keep things simple; these can differ in a real config. Even when connecting to an IP address, TLS may require a domain name. The path must also match exactly: /ws and /proxy aren’t interchangeable labels.

freedom is the direct outbound; blackhole is the blocking outbound. direct, block, and proxy are tags chosen for this example and can be renamed, but every outboundTag reference must be updated to match. VLESS user fields differ from VMess fields—you can’t migrate by changing protocol to vless alone.

Quick check: verify both layers when a connection fails

First compare the address, port, and UUID. Then check the network, TLS name, Host, and path. Don’t disable certificate verification to work around an incorrect serverName; skipping validation won’t make a bad config correct.

5. routing: rule order and the default outbound

Rules in rules are checked in array order, and the first match selects the outbound. This example blocks a demonstration domain first, then sends private addresses and localhost direct. If no rule matches, traditional V2Ray/Xray configs use the first outbound in the outbounds array, so this example defaults to proxy. The default doesn’t depend on the tag being named “proxy.”

  1. Check the tag: outboundTag: "direct" must match an outbound with tag: "direct".
  2. Check the order: Put specific exceptions before broad rules. A catch-all rule placed too early will shadow the rules after it.
  3. Check the conditions: Different fields in a rule generally all need to match. Multiple options in an array for one field generally match if any one of them does.
  4. Check the data: geoip:private requires the GeoIP data file to be available. If you use geosite entries, check that the corresponding domain data is available too.

Domain matching syntax varies: full:example.com matches only the exact domain, while domain:example.com matches that domain and its subdomains. A string without a prefix is generally treated as a keyword and may match more targets than expected. For precise exceptions, make the match scope explicit.

domainStrategyWhen resolution happensWhat to check
AsIsRouting does not resolve domains to IP addresses on its ownDon’t expect a domain request to match an IP rule automatically
IPIfNonMatchIf no domain rule matches, resolve the domain and try matching its IPThe resolved address can change the selected outbound
IPOnDemandResolution is triggered when matching requires an IP conditionCheck the cost of resolution and the DNS response

AsIs keeps the matching process simple in this example. A destination that is already a private IP can match a private-address rule. But a domain that resolves to a private IP isn’t guaranteed to match that rule. For internal domains, add an explicit direct-routing domain rule, or adjust the policy once you understand how resolution works.

6. dns: the built-in resolver is not system DNS

dns.servers specifies which servers the core’s built-in resolver uses. This example has a single upstream, 223.5.5.5, to illustrate the structure; it is not a complete DNS-routing setup. An app may resolve names itself before sending a request. Check the resolution paths used by the system, browser, and core separately.

So, adding dns alone doesn’t prove that all lookups are routed as intended. Check whether the app uses its own resolver, the routing domainStrategy, the outbound’s resolution strategy, and whether DNS queries can reach their server. For a server that requires a domain name, make sure its lookup doesn’t depend on connecting through that same server first.

7. From exported config to troubleshooting: change one variable at a time

Before troubleshooting, keep a working copy of the config, then check the core version and the file it actually reads. In v2rayN, go to “Settings” → “Parameter Settings” to view or adjust local listener options; page layouts and labels vary by version. Make sure the client’s server form, routing settings, and runtime JSON agree—don’t focus only on a file the client may regenerate.

  1. Check the syntax: Use a local editor to check brackets, commas, and value types. Don’t submit UUIDs, subscription URLs, or server details to a public online formatter.
  2. Check core compatibility: Confirm whether V2Ray or Xray is reading the config. Check that new fields and transport types are supported by your current core version.
  3. Check the listener: Confirm that the process is listening on the configured port. If you see address already in use, resolve the port conflict before changing the server UUID.
  4. Check the outbound: Temporarily reduce the number of routing variables and verify the address, authentication, and transport. Troubleshoot timeouts, authentication failures, and TLS errors separately.
  5. Check routing: Add one narrowly scoped test rule, confirm that the request selects the intended outbound, then restore the full rule set.
  6. Turn debug logging back down: If needed, temporarily set loglevel to debug. When you’re done, restore warning to avoid logging excessive connection details.

When running the core independently, use its config test command. The command below is for V2Ray/Xray executables that use the current subcommand format; replace the path with the actual executable location. Older command-line syntax may differ, so check the local help output first.

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

A successful test mainly confirms that the current core can parse and load the config. It doesn’t confirm that the server is online, the certificate is valid, or every rule behaves as intended. Finish by making a real request from an app known to support proxies. In logs, check the inbound tag, destination, and selected outbound. Remove authentication details and personal browsing records before sharing logs.

Key takeaway: verify the inbound, outbound, routing, and DNS in order

First confirm that requests reach the right port, then verify the proxy server, and only then add routing and DNS policies. Keep a restorable copy at each step. Otherwise, after changing all four layers at once, all you’re left with is “it still doesn’t work.”