Before reading a config, confirm which core and config format it uses. Then trace the flow in order: inbound, routing rules, outbound. This guide is for anyone inspecting a config generated by v2rayN, troubleshooting routing, or learning to write configs by hand. A complete example with a local SOCKS inbound, direct and block outbounds shows how the fields work together. It contains no remote proxy node, so it cannot verify whether a proxy service is working.
First, tell apart client settings, subscriptions, and core config
v2rayN is a desktop client for managing nodes, subscriptions, routing, and the core process. The core is what actually accepts traffic and makes connections. The interface preferences, subscription URLs, and group records saved by the client are not the same as the JSON the core loads at startup. Changes made directly to a temporary config generated by the client may be overwritten when you switch nodes, update a subscription, or restart the core.
This guide uses the traditional JSON config format commonly found in V2Ray. Different V2Ray config formats and Xray extensions cannot be mixed just because they use the same file extension. For VLESS, transport, and security settings in particular, refer to the documentation for the core and version you’re running. Being JSON does not mean every field is compatible.
| Type | What it contains | What to check |
|---|---|---|
| Client settings | Subscription groups, active node, system proxy toggle | Is the right node selected? Is the app using the client? |
| Subscription or share link | Node address, port, protocol, and transport settings | Is the node info complete? Did the subscription update successfully? |
| Core config JSON | log、dns、inbounds、outbounds、routing | Are the inbound listener, outbound references, and rule order correct? |
In v2rayN, start by checking local listener settings under Settings → Parameter Settings, then review routing rules under Settings → Routing Settings. Menu names may differ between versions, so use the options available in your installation. Check the startup log first for the core name, launch arguments, and config file path. Don’t mistake a subscription file for the complete runtime config.
Traffic flow: inbounds receive, routing selects, outbounds connect
inbounds is an array that describes where and how the core accepts requests. For example, a request enters an inbound when an app connects to the SOCKS service at 127.0.0.1:10808. Starting the core alone does not route every app through it. System proxy settings, app-specific proxies, and TUN are separate ways to send traffic to the core.
outbounds is an array of outbound routes, such as remote proxies, direct connections, and blocks. routing selects an outbound based on conditions like domain, IP, port, or inbound tag. A tag is an internal config identifier, not a protocol name, and it does not create an outbound with the same name.
inboundTag: filters traffic by inbound, using the inbound’stag.outboundTag: selects the outbound for a matched rule. It must exactly match an outbound’stag.- Rules are checked in order, and the first match takes effect. If no rule matches, the first outbound in the
outboundsarray is typically used. - Within a rule, multiple domain entries usually mean “match any.” Different conditions, such as domain and port, must all match.
Takeaway: check tag references before troubleshooting the network
A misspelled outbound tag or a misplaced fallback outbound can make rules behave differently than expected. First map each outboundTag to an actual outbound, then test node connectivity.
Complete example: local SOCKS, direct connection, and domain blocking
Here’s a complete example using the traditional JSON format. It listens for SOCKS connections on the local machine, blocks the exact domain example.com, and sends other requests received through this inbound directly to their destinations. It includes no VMess or VLESS server settings, so even if a webpage loads, that only confirms the local inbound and direct connection path work.
{
"log": {
"loglevel": "warning"
},
"dns": {
"servers": [
"1.1.1.1"
]
},
"inbounds": [
{
"tag": "socks-local",
"listen": "127.0.0.1",
"port": 10808,
"protocol": "socks",
"settings": {
"auth": "noauth",
"udp": true
}
}
],
"outbounds": [
{
"tag": "direct",
"protocol": "freedom",
"settings": {}
},
{
"tag": "block",
"protocol": "blackhole",
"settings": {}
}
],
"routing": {
"domainStrategy": "AsIs",
"rules": [
{
"type": "field",
"domain": [
"full:example.com"
],
"outboundTag": "block"
}
]
}
}
In JSON, objects use curly braces, arrays use square brackets, and strings use double quotes. To avoid parser differences, this example has no comments or trailing commas. Valid syntax is only the first step: the core still needs to validate field names, types, and protocol settings.
Inbounds and outbounds: the listening address is not the server address
An inbound’s listen value is the address bound on the local machine. A remote proxy server’s address belongs in the protocol settings for the relevant outbound. Entering the server domain as a local listening address will not establish a proxy connection and may prevent the inbound from binding. 127.0.0.1 allows access only from the local machine. Changing it to 0.0.0.0 exposes it on all IPv4 interfaces, so consider authentication, firewall rules, and LAN access before doing so.
Local SOCKS inbound
- tag
- socks-local
- listen
- 127.0.0.1
- port
- 10808
- protocol
- socks
- auth
- noauth
For local testing only. Set the app to use SOCKS on the same port.
Direct outbound
- tag
- direct
- protocol
- freedom
- settings
- Empty object
- Array position
- First outbound
- Purpose
- Connects directly to the destination
In this example, connections that don’t match the block rule fall back to this outbound.
udp: true allows the SOCKS inbound to forward UDP; it does not mean all app UDP traffic will be captured. The app must support SOCKS5 UDP associations, and the outbound and network path must also support UDP. Setting an HTTP proxy in the system does not automatically send UDP requests to this SOCKS inbound.
To add a VMess or VLESS outbound, you’ll also need the server address, port, user ID, and matching transport settings. VMess configs commonly use settings.vnext. VLESS has a similar user-and-server structure, but its authentication semantics differ, so changing only the protocol string is not enough. If you enable TLS, check the server name as well. A server port of 443 does not necessarily mean TLS is enabled.
- Get remote node details from the service provider or a valid subscription. Don’t guess the user ID based on connection errors.
- A core supporting a protocol does not necessarily support every new transport or security extension for that protocol.
- v2rayNG uses the Xray core, while v2flyNG uses the v2fly core. After migrating a node, check that its settings are compatible.
Routing rules: order, domain matching, and fallback outbounds
In this example, full:example.com matches only that exact domain; it does not automatically include www.example.com. To match a domain and its subdomains, traditional routing rules can use domain:example.com. Don’t confuse domain matching with URL matching: routing generally cannot distinguish pages on the same site by their path, such as /account.
| Field or syntax | Meaning | What to check |
|---|---|---|
full:example.com | Exact domain match | Does not include subdomains or URL paths |
domain:example.com | Domain and subdomain match | Covers more than an exact domain match |
ip: ["192.168.0.0/16"] | Destination IP range match | Whether a domain target is resolved depends on the routing strategy |
port: "443" | Destination port match | A port does not identify the application-layer protocol or encryption status |
inboundTag: ["socks-local"] | Limit rules by inbound | The inbound tag must exist and match exactly |
domainStrategy: "AsIs" means routing does not proactively resolve domains for IP-based rules. It does not mean DNS is never used during the connection: the outbound may still need to resolve the destination when connecting. With IPIfNonMatch, the core can resolve the IP and try matching again if no domain rule matches. With IPOnDemand, a rule that requires an IP may trigger resolution.
Order rules from specific to broad. If the first rule sends all traffic from an inbound directly, a later domain block rule for that same inbound will never run. For geoip: and geosite: rules, also confirm the required data files are available. Missing data and network connectivity problems are different issues.
Takeaway: design a reproducible request for each rule
Test the block rule with the exact domain, then use a different domain to verify the fallback outbound. Change one rule at a time, and confirm the app passes the domain—not an IP resolved locally—to the inbound.
DNS and logs: defining a resolver does not capture every query
dns.servers defines upstream resolvers for the core’s built-in DNS. This example uses 1.1.1.1. A plain IP address usually means traditional DNS, not encrypted DNS. Whether the resolver is reachable and how queries are routed also depend on the network and related settings; this field alone does not tell you the full resolution path.
An app may also use the operating system’s DNS to resolve a domain first, then pass the resulting IP to SOCKS. In that case, the core receives an IP rather than the original domain, so domain rules may not match. Sniffing can help identify domains for supported protocols, but it cannot recover the original domain for every request—and it is not automatically enabled just because built-in DNS is configured.
- Check what was sent. Do the request logs show a domain or an IP? Is the SOCKS app configured to resolve domains through the proxy?
- Then identify where it failed. DNS resolution failures, connection timeouts, and TLS handshake failures occur at different stages; don’t treat them all as DNS problems.
- Raise the log level briefly. Change
loglevelfromwarningtoinfoordebug, reproduce the issue once, then restore the original setting to avoid logging excessive connection details. - Configure log files only if needed. Set
accessanderrorto specify access and error log paths. The target directory must exist, and the process account needs write permission.
How to test: check syntax first, then real traffic
Save the example as config.json and test it in an isolated environment. Don’t overwrite the config currently used by the client. If your V2Ray build supports the command, use the method below to test the config. Command-line options vary by release; if you see an unknown command, run v2ray help to check which options your installation supports.
v2ray test -c config.json
A config passing validation does not guarantee that its port can be bound, a remote server can be reached, or routing will behave as expected. After starting the core, test with a request that explicitly specifies the proxy protocol. The commands below require curl; in a Windows terminal, use curl.exe to avoid command alias differences.
curl --socks5-hostname 127.0.0.1:10808 --connect-timeout 10 https://example.com/
--socks5-hostname sends the destination domain to the SOCKS proxy, making it suitable for testing this example’s domain rule. The request should fail to retrieve the webpage, and the logs should show that it reached the core. The exact error depends on the core and curl versions, so a single error message is not enough to tell which rule took effect.
- Test the block rule. Keep the
full:example.comrule in the complete config, run the command above, and check the logs. - Test direct access. In a test copy, set
rulesto an empty array[], restart the core, and run the same request. If the destination is reachable, the first direct outbound should handle the connection. - Check the inbound. If no request appears in the logs, check the listening address, port, and app settings. Don’t keep changing routing rules.
- Restore your setup. Stop the standalone test core, restore the original config, and then start the client to avoid multiple processes competing for the same port.
Troubleshooting: routing doesn’t change even though the core starts
Why do my JSON changes disappear when I restart the client?
v2rayN often generates configs dynamically from node and routing settings. Make changes in the relevant client settings, or use the custom config method supported by your version to manage a separate file. First check the startup log to see which file was actually loaded, then verify whether your changes took effect.
Why does the core report an error even though the JSON is valid?
A generic JSON parser checks syntax such as brackets, commas, and strings, but it does not understand protocol fields. Invalid field types, references to nonexistent outbounds, missing rule data files, or settings unsupported by the current core can all cause config validation to fail.
Why don’t some apps show up in the logs when the system proxy is enabled?
Some apps ignore the system proxy or use their own proxy settings. First test the inbound with a request that explicitly uses SOCKS, then check the app in question. If you need to test TUN, record the results with system proxy mode first so you don’t change the traffic entry method and routing rules at the same time.
How do I change the example to use a proxy by default?
First add a remote proxy outbound with complete settings that are compatible with your core. Then set the default route with an explicit fallback rule or outbound order. Renaming the direct tag to proxy does not change the protocol behavior; an outbound using freedom still connects directly.
When reading a config, ask three questions: Does the app reach the intended inbound? Does the request match the intended rule? Does the selected outbound have the right settings? Test DNS resolution, routing, and connectivity separately; this usually makes the problem easier to isolate than changing several settings at once.
Download v2rayN
Choose a client for your operating system and processor architecture. For your first setup, follow the guide to import a node and verify the local proxy before changing routing rules or custom JSON.
View download options Read the guide