CONFIGURATION REFERENCE

V2Ray Configuration Reference

JSON structure · Inbounds and outbounds · Routing and DNS · Policy and validation

This page is organized by configuration field and is intended for readers who have installed a client and need to understand parameters or explain connection behavior. New users should start with the Getting Started Guide: import a subscription, choose a node, set up the proxy and test the connection, then return here for details. If you haven't installed a client yet, visit Download Clients and choose your platform. For desktop setup, start with the v2rayN settings.

JSON structure: objects, arrays and references

Follow the data flow before digging into fields

A configuration isn't a flat list of node settings; it describes how requests are handled. An application sends a request to an inbound, the routing module selects an outbound based on the request's attributes, and the outbound connects to the destination or proxy server. DNS provides addresses wherever names need to be resolved, while policy governs connection lifetimes and traffic statistics. Trace this flow first, then look at each module. It's easier to see how the pieces fit than to guess at fields line by line.

The top level is usually an object, with modules named inside curly braces. inbounds and outbounds are arrays: a process can listen on multiple inbounds and have multiple outbounds. routing, dns and policy are usually objects. Separate array items with commas, but don't add a comma after the last item. Write Boolean values as true or false, and ports as numbers in most cases. Protocol names and tags are strings. Don't put every label from the UI into the config as-is and wrap it in quotes.

This complete configuration is for validating the structure on your own machine. It exposes a SOCKS inbound on the loopback address only and has no remote proxy node; all requests use your local network. Use it to check that the core loads the config, an application can connect to the listening port, and the logs record requests. It does not establish a remote proxy, and a successful direct connection doesn't confirm that subscription settings are correct. After testing, choose which client configuration to use.

{
  "log": {
    "loglevel": "warning"
  },
  "inbounds": [
    {
      "tag": "socks-in",
      "listen": "127.0.0.1",
      "port": 10808,
      "protocol": "socks",
      "settings": {
        "auth": "noauth",
        "udp": true
      }
    }
  ],
  "outbounds": [
    {
      "tag": "direct",
      "protocol": "freedom",
      "settings": {}
    }
  ],
  "routing": {
    "domainStrategy": "AsIs",
    "rules": []
  }
}

Tags are internal references

tag is an internal identifier, not a required display name. A routing rule's outboundTag must exactly match the target outbound's tag, including capitalization. inboundTag restricts a rule to a particular inbound. Keep tags short, descriptive and unique within their object type. If you rename an outbound tag, update every routing rule that references it. Otherwise, the file may parse successfully but fail at runtime or when forwarding a request because the target can't be found.

An omitted field is not the same as explicitly setting it to an empty value. When an optional field is left out, the core usually applies its own default behavior; setting it to null, an empty string or an empty object isn't guaranteed to do the same. When copying an example, check where each field belongs. Don't put streamSettings inside a protocol account object or place rules at the top level. Passing a JSON syntax check only confirms that the brackets and data types are broadly valid; it doesn't prove the core recognizes the fields.

Where client-generated and hand-written configs differ

Graphical clients often regenerate the runtime config based on the selected node, routing templates and DNS options. Direct edits to a temporary file may be overwritten the next time you switch nodes or restart. For settings you need to keep, change them in the corresponding client settings when possible. Treat a complete JSON file as the main configuration only when you're deliberately using a custom-config import workflow. Exported configs may contain server addresses and account details; remove them before sharing troubleshooting materials, keeping only the field structure and the context needed to understand the error.

Xray and V2Fly share many legacy structures, but new features, aliases and defaults aren't always consistent. This guide's classic structure helps explain data flow; it isn't a promise of cross-core compatibility. If you see an “unknown field” or “unknown transport type” error, first check which core is running, then verify its config format. In particular, don't mix field names from another config format into the same file. See the glossary to distinguish protocols, transports, security layers and clients, and find settings at the right layer.

Inbounds: listen address, port and traffic sniffing

The listen address determines who can connect

An inbound defines where the client receives application requests. A common desktop SOCKS inbound accepts traffic from apps that support a SOCKS proxy; an HTTP inbound accepts requests sent using the HTTP proxy protocol. This is the protocol between a local app and the client, not the VMess or VLESS protocol used by a remote node. The app's proxy type must match the inbound. Entering a SOCKS port in a setting that only supports HTTP proxies won't make the protocols compatible just because the port is reachable.

With listen set to 127.0.0.1, the inbound accepts IPv4 loopback connections from the same machine only. Setting it to 0.0.0.0 listens on all local IPv4 interfaces; it does not restrict access to your home network. If the device is connected to other networks, the service may also be reachable through those interfaces. For LAN sharing, consider the system firewall, allowed source addresses and inbound authentication. Don't assume that toggling one setting in the client limits who can connect.

port is the local listening port, independent of the node's server port. After changing it, update any browser, developer tool or other app where you've entered the proxy settings manually. If the port is already in use, check whether another client instance or program is listening instead of repeatedly switching nodes. A bind error in the logs occurs while creating the local inbound, before any remote protocol negotiation. Changing the server UUID, TLS settings or routing won't fix a local port conflict.

{
  "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
      }
    }
  ]
}

SOCKS, UDP and the system proxy are separate settings

In this example, udp lets the SOCKS inbound handle UDP requests. It doesn't route apps that aren't using SOCKS through the proxy, and it doesn't guarantee that the outbound or remote service supports that traffic. The full path also depends on whether the app initiates a SOCKS UDP association, whether the local network allows the traffic and whether the remote protocol combination can carry it. Test with an app that explicitly supports this mode. A web page loading successfully doesn't prove that every UDP use case works.

The system proxy usually affects only apps that honor the system proxy settings; it does not capture every connection on the device. An app that ignores those settings or connects directly to a destination won't necessarily use the inbound just because the client is running. A client's virtual adapter or transparent proxy is a separate access method that also requires routing, permissions and DNS configuration. This guide focuses on explicit proxies. Confirm which inbound is actually in use, then check the corresponding logs to see whether the request reached the core.

What sniffing is used for

sniffing attempts to extract destination details from protocol features in incoming traffic, such as an HTTP hostname or the server name in a TLS handshake. It doesn't decrypt all application traffic or recover a domain from every connection. If an app supplies only a destination IP and the traffic doesn't reveal a name, domain-based routing may have no hostname to match. Sniffing can add routing information in some cases, but it can't replace correct app proxy settings or a complete DNS design.

destOverride specifies which types to try to identify; routeOnly expresses the intent to use the identified value for routing while retaining the original destination. Check that the current core supports this option. An option with the same name in a client UI doesn't guarantee identical behavior across implementations. To troubleshoot domain rules, compare destination records and outbound selection with sniffing enabled and disabled. This helps determine whether no hostname was extracted, the name didn't match, or an earlier rule took precedence.

If you need to share an inbound, first verify a basic configuration that listens only on the local machine, then expand the listen address and access permissions one step at a time. Don't change the listen address, authentication, system proxy and node parameters together; if the connection fails, you won't know whether local access was blocked or the upstream handshake failed. Once configured, test the allowed access path from another trusted device and check that unintended interfaces aren't exposed. For the graphical setup steps, see the Getting Started Guide.

Outbounds: protocol accounts, default route and tags

An outbound defines the next hop

An outbound determines how the core connects after receiving a request. freedom connects directly to the destination; blackhole blocks traffic; VMess and VLESS outbounds send requests to a specified proxy server. A remote node's address is the next hop, not the website the user ultimately visits. When reading a config, keep the server address, user's destination and DNS server separate. All three can include a hostname and port, but they serve different roles in the data flow.

In the classic config, requests that don't match a routing rule usually go to the first outbound in the outbounds array. That makes array order meaningful, not just a display choice. Moving direct to the first position changes the path for unmatched traffic. To make the behavior clear, define an explicit default outbound and, if appropriate, add a fallback rule at the end of routing. Test with a new connection after changing the config; existing connections may not switch paths immediately.

This VLESS outbound snippet shows only the server and account fields. It isn't a working node with transport security configured. example.com is a documentation address, and the all-zero UUID is a sample value; use the parameters supplied by your server in an actual deployment. Don't assume TLS is enabled just because the port is 443. The port specifies where to connect; the transport settings and server determine whether a security layer is established.

{
  "outbounds": [
    {
      "tag": "proxy",
      "protocol": "vless",
      "settings": {
        "vnext": [
          {
            "address": "example.com",
            "port": 443,
            "users": [
              {
                "id": "00000000-0000-0000-0000-000000000000",
                "encryption": "none"
              }
            ]
          }
        ]
      }
    },
    {
      "tag": "direct",
      "protocol": "freedom",
      "settings": {}
    },
    {
      "tag": "block",
      "protocol": "blackhole",
      "settings": {}
    }
  ]
}

Account fields aren't interchangeable across protocols

For VLESS, encryption set to none describes a setting at the protocol layer; it doesn't mean the entire connection is unprotected. Check streamSettings for the actual security layer. VMess uses different account fields and negotiation logic, so its user object can't be swapped directly with VLESS's. Parameters in shared node links may also use abbreviations or different names. After importing, check the protocol type parsed by the client rather than copying link query parameters into JSON one by one.

The account identity, transport and server capabilities must all match. A valid UUID with the wrong transport may disconnect before account authentication; a successful security handshake still won't complete a proxy request if the identity fields are wrong. A log showing that the connection reached the server doesn't mean the account was authenticated. For a comparison of the two protocols, see VMess vs. VLESS: Key Differences, then check the fields for your specific node.

Name direct and blocking outbounds explicitly

direct, proxy and block are tags used in this guide, not built-in outbounds created automatically by the core. If a rule says outboundTag: "block", an outbound with that tag must exist. A direct connection may still depend on DNS, IPv4 or IPv6 selection, system routing and the local firewall; it doesn't bypass all network settings. If a destination fails over direct, that alone doesn't prove the proxy outbound is broken. Test each path separately.

A client supporting multiple nodes doesn't mean its runtime config will always list every outbound the same way. Some modes generate only the current node; others add outbounds or internal chains. After switching nodes, a hand-written rule may stop working if it references a tag that no longer exists. For long-term use, prefer fixed routing targets supported by the client and check the effective config instead of relying on temporary names in a one-time export. Avoid duplicate outbound tags, which make references ambiguous.

After a subscription update, server addresses, ports, transport parameters and node identifiers may all change. Don't keep an old address and splice in new account fields, or mix security settings from different nodes. Save a working client config first, import the update and test one node on its own. To understand how share links map to native config, see Subscription Formats and Share Links. Distinguish the subscription container, node description and final runtime config.

Routing: rule order, match conditions and fallback

Check rules from top to bottom; the first match wins

The routing module selects an outbound based on attributes of requests that have reached the core; it doesn't make app traffic appear if it never entered the core. In the classic config, rules are generally evaluated in array order, and the first matching rule determines the result. Earlier rules have higher priority. So the first question isn't whether a rule exists, but whether another rule catches the request before it gets there. Put broad catch-all rules last, or more specific rules won't get a chance to match.

Different types of conditions in the same rule usually all need to match; multiple candidate values in the same array usually mean any one of them can match. For example, a rule with both inboundTag and domain applies to the specified inbound and a matching domain, not to either condition on its own. If you want to describe two separate cases, use two rules. That's clearer than packing every field into one rule and makes troubleshooting easier.

{
  "routing": {
    "domainStrategy": "IPIfNonMatch",
    "rules": [
      {
        "type": "field",
        "inboundTag": ["socks-in"],
        "domain": ["full:example.com"],
        "outboundTag": "proxy"
      },
      {
        "type": "field",
        "ip": [
          "10.0.0.0/8",
          "172.16.0.0/12",
          "192.168.0.0/16",
          "127.0.0.0/8",
          "::1/128",
          "fc00::/7",
          "fe80::/10"
        ],
        "outboundTag": "direct"
      },
      {
        "type": "field",
        "network": "tcp,udp",
        "outboundTag": "proxy"
      }
    ]
  }
}

Where domain and IP rules differ

full:example.com matches the exact domain; domain:example.com matches that domain and its subdomains. A bare string and a regular expression have different semantics too. Similar-looking text doesn't mean the match covers the same set of domains. For an exception affecting one site, start with an exact match you can test, then broaden it if needed. Regular expressions also need escaping as JSON strings. An incorrect backslash can change what a rule means even when the file still loads.

IP rules match a destination address or an address obtained during routing; they don't compare an IP directly with the domain in the browser's address bar. If an app submits only an IP and no domain information is available, domain rules may not apply. When an app submits a domain, whether an additional lookup occurs depends on domainStrategy. Even a complete domain list can't fix a request that never reached the inbound, missing destination information or rules in the wrong order.

StrategyWhat it does during routingWhat to check
AsIsDoesn't resolve domains to IPs for routing.Whether domain rules are sufficient.
IPIfNonMatchResolves the domain and tries IP rules only if no domain rule matches.Whether a catch-all rule ends matching too early.
IPOnDemandResolves the domain when an IP condition needs to be checked.DNS dependencies and rule order.

IPIfNonMatch doesn't mean every request is resolved before rules are checked. If a broad rule matches during the domain stage, routing may never reach the IP matching you expect. The final network rule in the example is an explicit proxy fallback. Consider how it interacts with the DNS strategy: if domain requests should be checked against private IP ranges after resolution, redesign the rules or choose a suitable resolution strategy. Don't assume the private IP list will always be checked first for every domain.

Validate rule logic separately from rule data files

geosite: and geoip: entries depend on the client or core being able to read the corresponding classification data. Verify that the category exists, the data is available and its update behavior is understood in the actual environment. Missing resources are a configuration dependency issue; unexpected domain classifications are a data coverage issue. Don't confuse the two. When debugging, start with explicit domains and IP ranges. Add category sets after the routing logic works, so you aren't troubleshooting too many unknowns at once.

To validate a rule, first record the inbound tag, request destination, transport and final outbound, then change one condition at a time. Existing web connections, background connection pools and app caches may keep using the old path, so close the connection and make a new request. A rule match only means the core selected an outbound; it doesn't guarantee the remote destination is reachable. If the outbound is correct but the request fails, check the outbound handshake, DNS or destination service instead of reshuffling rules that have already been shown to work.

DNS configuration: resolvers, match scope and connection dependencies

First, find out which component resolves the domain

A request may involve the app's own DNS, the system resolver, the core's built-in DNS and resolution on the remote proxy side. Adding a top-level dns section doesn't mean every app on the device has its DNS queries intercepted. With an explicit proxy, an app may pass the domain to the proxy or resolve it first and pass only the IP. Those request types leave different information for routing. Before troubleshooting, determine what form of destination the app submits instead of looking only at the DNS server in the client's settings.

Built-in DNS mainly serves core operations that need to resolve addresses, such as routing strategies that require an IP, outbounds that need to resolve their destinations and some domain connection flows. Whether it is used also depends on the relevant module and options. System proxy settings usually don't redirect system DNS. If web browsing works but another app doesn't, it may be using a different resolution path. Check how the app connects and where resolution happens rather than blaming every failure on an unreliable DNS server.

This partial config uses public resolvers to illustrate server lists and static mappings. The test name in hosts points to a documentation-only address and is included only to demonstrate mapping behavior; it doesn't host a real website. Remove this sample mapping for actual use, then choose resolvers based on network reachability, resolution needs and privacy requirements. The addresses in this example aren't mandatory for every environment and don't guarantee suitable results on your network.

{
  "dns": {
    "hosts": {
      "full:resolver-test.example": "192.0.2.10"
    },
    "servers": [
      "1.1.1.1",
      "8.8.8.8"
    ],
    "queryStrategy": "UseIP"
  }
}

Consider domain scope and fallback behavior together

A server entry can include fields such as address, domains and expectIPs to describe the resolver address, preferred match scope and expected results. Domain scope isn't simply an access rule: sending a lookup to a particular resolver doesn't mean the website connection will use the same outbound. Configure and check DNS and application traffic paths separately. Don't treat DNS server groups as if they were proxy and direct groups in routing.

In classic configs that support these fields, expectIPs checks whether a DNS result is expected, while skipFallback and related fallback options affect what happens when no server matches or the result doesn't meet expectations. Check the actual core for fallback details. Don't assume multiple servers are always tried one by one in the order written, or that adding a backup server handles every failure. Use logs to see which queries were sent and what they returned, then decide whether to narrow the match scope.

queryStrategy controls which address families built-in DNS queries use. Common values include UseIP, UseIPv4 and UseIPv6. Returning IPv4 only may work around some IPv6 routing problems, but it doesn't disable IPv6 system-wide or provide a universal fix for connection failures. If the system's IPv6 route is unavailable, first identify where the problem lies. An app's own resolver may not follow this query strategy.

The resolver itself needs a reachable connection path

With encrypted DNS, consider not only the query contents but also how the resolver connection is established and how its own hostname is resolved. If the proxy server address needs DNS and the DNS connection depends on that same proxy before it is connected, startup can get stuck in a loop. Keep an explicit path for any required bootstrap lookups and check how the current core supports the DNS protocol in use. Adding an HTTPS URL to the server list alone doesn't resolve connection dependencies.

hosts can provide explicit mappings for a small number of names, but pinning a website's address long term may conflict with load balancing, address changes or certificate use. When using a mapping to troubleshoot, record the original result, what changed and how to revert it. The system, app and core may all cache DNS results, so test with a new request and clear caches or restart relevant processes using the method provided by the client if needed. Refreshing a page doesn't guarantee a new DNS lookup.

When comparing results, don't record only whether a site “opens.” Distinguish a query timeout, an empty result, an unreachable address family and a failure later in the connection. If the domain resolves but the destination port refuses the connection, the problem is now at the connection layer. If the IP works but a domain request fails, also check whether the security layer requires a specific server name. See the routing and DNS glossary, then use this page's validation section to narrow down the failing step.

Policy: user levels, timeouts and statistics

Policy doesn't select outbounds

policy controls connection handling and statistics; it doesn't select a proxy outbound based on the domain. Routing's domainStrategy, DNS query strategy and user-level policy operate at different layers. They may all be called “strategy” in a translated UI, but they aren't interchangeable. To change whether a site uses a direct or proxy connection, check routing. To understand why a connection ends after being idle, investigate timeouts, app behavior and server limits.

levels is an object keyed by level number. In JSON, the key is still a string, such as "0", while level in an account object is usually a number. A level associates a user or related processing step with a set of policies. It isn't a node speed rating or a priority for sorting nodes in the UI. Many configs use a default level when none is set explicitly, but check which modules it applies to and how the current core handles defaults.

{
  "policy": {
    "levels": {
      "0": {
        "handshake": 8,
        "connIdle": 300,
        "uplinkOnly": 2,
        "downlinkOnly": 5,
        "statsUserUplink": false,
        "statsUserDownlink": false
      }
    },
    "system": {
      "statsInboundUplink": false,
      "statsInboundDownlink": false,
      "statsOutboundUplink": false,
      "statsOutboundDownlink": false
    }
  }
}

Know when each timeout takes effect

handshake sets how long protocol handling that supports this policy waits for a handshake to complete. It isn't a single timeout shared by every phase of a network connection. Connecting to the server, TLS negotiation, proxy protocol authentication and the application request all have their own contexts. The example value illustrates the unit and structure; it isn't an optimal setting for every network. A longer timeout may help on a weak connection, but it won't fix incorrect authentication parameters.

connIdle sets how long a connection can remain open without data activity. If it's too short, sessions may be interrupted after a long idle period. If it's very long, idle connections may use more resources. Whether a connection stays open also depends on the app, remote service and network intermediaries, so one interrupted long-lived connection doesn't prove this field is taking effect. Check whether there was actual traffic before the disconnect, then compare client and server logs to see which side closed it.

uplinkOnly and downlinkOnly set the wait period after one side of a connection closes while the other side can continue transmitting. Uplink and downlink here refer to connection direction, not bandwidth limits. These policies may affect how a connection winds down after a download completes or an app half-closes a connection. They don't control download speed and shouldn't be set to zero arbitrarily to “speed things up.” First determine whether the problem occurs during connection setup, data transfer or teardown.

Field groupControlsDoes not mean
handshakeWait time for the relevant protocol handshake.One timeout for every connection phase.
connIdleHow long an idle connection is kept open.Network throughput or node priority.
statsUserUplinkWhether user traffic is counted.An automatically generated statistics dashboard.

Configure statistics collection separately from its display

Enabling user statistics usually also requires the top-level stats module, identifiable user information and an API or client feature that can read the data. Changing statsUserUplink to true on its own won't create a traffic graph. Supported user identifiers and statistics may also vary by protocol. If statistics are empty, first confirm which inbound and account handled the traffic, then check whether the relevant counters are enabled. Don't immediately assume the proxy connection wasn't established.

System-level inbound and outbound statistics are aggregated by their respective objects, while user statistics are tracked per account. Their readings may differ due to where they're collected, reconnects, connection reuse and counter resets; they aren't guaranteed to match exactly at every moment. A client's displayed speed may also be calculated from periodic samples rather than read from a fixed config value. Don't change routing or nodes while adjusting statistics, or differences in data collection could be mistaken for a change in traffic flow.

For most personal desktop setups, leave the client-generated policy as-is until you can reproduce a specific idle disconnect, handshake delay or statistics issue. Record the original value, why you changed it and how you'll verify the result; keep a way to revert. Core policy can't override OS sleep, mobile network changes, remote timeouts or app connection pool behavior. Increasing every timeout may only delay errors without improving connection quality. Evidence-based changes are more reliable than applying a generic set of “optimized” values.

Transport and security: streamSettings, TLS and flow

Check protocol, transport and security separately

A node config has at least three dimensions: proxy protocol, transport and connection security. VLESS or VMess specifies the proxy protocol; TCP or WebSocket specifies the transport; TLS or REALITY specifies a security mechanism. A UI may show them together, but that doesn't mean they belong in the same field. When a connection fails, compare each dimension with the server details separately, then check additional parameters. This is more likely to reveal a mismatch than repeatedly changing the protocol type.

streamSettings is usually a sibling of the outbound's settings. The former describes how to establish the transport connection; the latter contains the proxy protocol's server and account parameters. This classic WebSocket and TLS snippet shows where the fields go. Merge it into the relevant outbound, and make sure the server provides the corresponding service. Network names and transport settings may have aliases or change as cores evolve, so check the implementation you're running before copying.

{
  "streamSettings": {
    "network": "ws",
    "security": "tls",
    "tlsSettings": {
      "serverName": "example.com",
      "allowInsecure": false
    },
    "wsSettings": {
      "path": "/proxy",
      "headers": {
        "Host": "example.com"
      }
    }
  }
}

Address, serverName and Host serve different purposes

The outbound's address is the actual next-hop address to connect to. serverName is used for TLS server-name handling, while WebSocket's Host is part of the HTTP request. These can differ in a valid deployment, but don't swap them just because they all look like hostnames. If the server routes sites by server name or HTTP Host, an incorrect field may connect to a different service even when the address and port are reachable.

path must match the WebSocket path configured on the server, including capitalization, the leading slash and any query string. An HTTP 404 or failed upgrade may indicate a path or reverse-proxy forwarding mismatch, not an account identity problem. First confirm the request reaches the expected service, then check proxy protocol authentication. A TLS handshake failure happens earlier; changing the WebSocket path repeatedly won't help if the HTTP layer hasn't started.

Keep allowInsecure set to false. If certificate validation fails, first check the system clock, server name, certificate validity and deployment. Disabling validation hides important errors and doesn't fix a connection to the wrong service. Some environments require additional trusted certificates; configure the actual certificate chain instead of disabling validation globally. Keep the specific error type in troubleshooting notes rather than reducing it to an uninformative “TLS failed.”

REALITY and Vision have specific requirements

REALITY is a security feature in the Xray ecosystem; don't assume V2Fly or every importer supports the same fields. The server-provided name, public parameters, short ID and client fingerprint must match as a set. Use field names supported by the current core and client. Don't combine snippets copied from different tutorials or enter private material meant for another purpose. For a deeper look at how it works, see How REALITY and XTLS Vision Work.

flow belongs in the relevant protocol account settings. xtls-rprx-vision is only for server and transport combinations that support it. It's not a general-purpose speed setting, and it shouldn't be added to every VLESS node. If the server isn't configured for the flow, setting it on the client alone may cause the connection to fail. The fingerprint is also specific to a security layer; it can't replace the correct server name, and its name doesn't tell you everything about the browser's actual network behavior.

Multiplexing (Mux) is a separate concept from the security layers above. It attempts to share a transport connection across multiple logical requests; whether that's appropriate depends on the protocol, transport combination and traffic. Enabling it by default doesn't guarantee better performance, and some combinations may not support it. If failures occur only with concurrent requests or long-lived connections, compare results with Mux changed while leaving other settings untouched. Don't disable Mux, change flow, replace the fingerprint and adjust TLS all at once, or you won't know what made a difference.

A successful node import only shows that its fields were parsed; it doesn't verify every transport feature. A client may discard extension fields from another core or regenerate the config when you save it in the editor. Check key parameters after import, then test a new connection. If you need a client that supports a particular feature, choose the right platform on the download page and keep the original config for comparison while troubleshooting.

Config validation: load checks, logs and rollback

Test syntax, loading and real requests separately

Validate a config at three levels. First, check JSON syntax: brackets, commas, strings and data types must be valid. Second, check that the core loads it and accepts its fields, protocols, resources and references. Third, test a real request: confirm the app reaches the right inbound, routing selects the expected outbound, the outbound handshake succeeds and the destination responds. Passing one level doesn't replace the next. After merging a partial example, validate the entire file again, not just the snippet you copied.

For a graphical client, use its built-in config checks, logs and core restart features when available; where to find them varies by client. Keep the original config or an exported copy before editing. After making changes, confirm they were saved and reload the core. If the client generates the config automatically, check the saved UI options rather than editing a temporary file in the runtime directory. A running process doesn't prove that the new config loaded successfully: the old process may still be serving, or the new one may exit immediately. Check the logs.

Temporarily increase log verbosity when investigating a problem. This partial logging config can help you observe destinations, connection steps and routing decisions. Detailed logs may contain visited domains, server addresses and account context, so don't collect them longer than necessary or share them unredacted. Once you have enough evidence, restore a lower log level and handle unneeded records according to your local file management practices. When sharing troubleshooting materials, include the relevant error and only the context needed to understand it.

{
  "log": {
    "loglevel": "info"
  }
}

Trace the data flow one step at a time

First, check that the request reaches the inbound. Send a new request from an app that explicitly honors proxy settings and look for its destination in the logs. If there's no entry, check the app's proxy type, listen address, port and whether the system proxy applies. Second, check the routing decision. If the logged outbound isn't the expected one, inspect tag references, rule order and destination details; don't change account credentials yet. Only then check the remote connection, security layer and proxy protocol handshake, followed by the destination's response.

SymptomCheck firstHold off on changing
Exits on startupSyntax, fields, ports and resource dependencies.Website domain rules.
No logs for app requestsApp proxy method and inbound listener.Remote account and security layer.
Unexpected outbound selectedTags, rule order and destination details.A working server address.
Fails after connecting to the serverSecurity layer, transport, identity and server status.Unrelated local listening port.

Interpret error messages in context. “Connection refused” usually means the address and port didn't accept the connection; that could be a local inbound or a remote server. A timeout may occur during DNS resolution, connection setup or while waiting for a response. A certificate error needs to be understood in the context of the security layer. Don't draw conclusions from the last word alone. Record the destination, port and outbound along with the preceding error so you can identify which part of the connection failed. Repeating a test without keeping its context usually adds no useful information.

Make changes one at a time and keep a rollback path

Change one independent variable at a time: fix the listening port, verify it, then adjust domain rules. If several node parameters must change together, treat them as one change set and keep the full previous set for rollback. Use the same app and destination for each test, and start a new connection. Don't switch from a browser to a tool using a different protocol between tests. If the problem affects only one app, check whether it uses its own DNS, reuses connections or bypasses the proxy.

A latency test proves only the parts of the connection it actually tests. A successful TCP connection doesn't confirm proxy authentication; a successful test request doesn't prove large-file throughput is stable; and a good download speed doesn't show that every domain routing rule works as intended. See v2rayN latency testing methods to understand what each test covers. For routing issues, check the outbound; for DNS issues, check the queries; and for throughput issues, run a sustained transfer. No single number summarizes the whole configuration.

When moving to another device or core, don't just copy the final JSON and call it done. System permissions, port conflicts, classification data, certificate trust and client-generated rules may all differ. On the new setup, first verify a minimal inbound and a connection to one node, then restore custom DNS, routing and policy gradually. Routers and gateway devices also need system-level forwarding. See Running Xray on OpenWrt: Deployment Overview; a desktop explicit-proxy config isn't a transparent proxy setup.