Configuration Reference / JSON Fields

V2Ray Configuration Reference

Follow the traffic flow: local app → inbound → routing → outbound. Then check DNS, transport and policies.

New to V2Ray? Start with the Getting Started guide. This page is a reference for reviewing configs after importing them: where fields belong, which settings interact, and how to confirm a change took effect. For desktop, we recommend v2rayN; see the download center to get started.

1. JSON Structure and Configuration Boundaries

Separate client data from core configuration

Subscription groups, UI options and server records saved by v2rayN are client data. When a connection starts, the client generates a core config from the selected server, routing preset and other settings. The core reads that generated file, not the UI database. A temporary config in the runtime directory may be overwritten when you switch servers or restart, so first identify what needs changing: a UI option, a routing template or a standalone custom config.

v2rayNG also handles imports, server selection and core startup; v2flyNG is an Android alternative that uses the V2Fly core. Similar menu labels do not mean the three clients export identical configs. When reviewing logs, note which core actually started. When inspecting a config, make sure it belongs to the current session, not an older export. Config paths vary by platform and client settings, so don't assume another machine's directory applies to yours.

What each top-level field does

Traditional JSON uses an object as its root. inbounds and outbounds are arrays that hold one or more entry points and exits; routing, dns and policy are usually objects. Each field must appear at the correct level: for example, an outbound's settings does not belong at the root. Braces denote objects and square brackets denote arrays. Keys and strings use double quotes; numbers and Boolean values do not.

Top-level fieldWhat it controlsCommon misconception
logAccess logs, errors and log levelsTreating the log level as a connection policy
inboundsLocal listener and inbound protocolUsing the local port as the remote port
outboundsOutbound connection and remote parametersMissing a tag or using the wrong protocol structure
routingTraffic matching and outbound selectionIgnoring rule order
dns / policyDNS resolution and connection behaviorAssuming it automatically takes over system traffic

A complete baseline for isolating local issues

This config accepts SOCKS traffic only from the local machine and connects directly to destinations over the local network. It does not connect to a remote proxy or configure the system proxy. Use it to check whether the core starts and the local port accepts requests. Save standalone files as UTF-8; don't include notes, code fences or line numbers. If the client already has a core using the same port, stop that process first.

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

This baseline deliberately omits routing and remote servers to keep variables to a minimum. A successful request confirms that the local listener and direct connection path basically work; it does not prove that a subscription server is available. When adding outbounds, put the intended default first, then add rules incrementally. Keep the last working file and revert each change that causes an error instead of piling on more settings.

Standard JSON does not allow trailing commas or comments. Some core input methods may accept extended syntax, but avoid relying on it when moving configs between tools. Remove duplicate keys too, since parsers may handle them differently. An editor's syntax check can catch formatting issues such as mismatched brackets or quotes, but it cannot tell you whether the current core supports a protocol field or whether a server address is reachable.

2. inbounds: Local Listeners and Traffic Entry Points

What the listen address and port control

An inbound determines how an app hands traffic to the core. listen is the local address to bind to, port is the local port that accepts connections, and protocol specifies the inbound protocol. Listening on 127.0.0.1 makes the proxy available only to local apps; 0.0.0.0 binds all IPv4 interfaces and may expose it to devices on your LAN. If you bind to a wider address, consider firewall rules, authentication and whether the network is trusted.

The local listening port does not have to match the server's remote port. The browser connects to the local port, then the core connects to the remote server port; these are separate connections. If a port is already in use, startup logs usually report a bind error. Identify the process using it, then stop that process or choose another port. Changing only the port in a command won't help if the client is still listening on a different one.

Choosing a SOCKS or HTTP inbound

Use a SOCKS inbound with apps that support SOCKS proxies, and an HTTP inbound with programs that accept an HTTP proxy address. They are different protocols, so a reachable port does not make them interchangeable. The example below listens on separate ports and uses distinct tags, making the source easier to identify in routing rules and logs. This is a standalone two-port example; your client's generated config may use a different layout.

{
  "inbounds": [
    {
      "tag": "local-socks",
      "listen": "127.0.0.1",
      "port": 10808,
      "protocol": "socks",
      "settings": {
        "auth": "noauth",
        "udp": true
      },
      "sniffing": {
        "enabled": true,
        "destOverride": ["http", "tls"]
      }
    },
    {
      "tag": "local-http",
      "listen": "127.0.0.1",
      "port": 10809,
      "protocol": "http",
      "settings": {}
    }
  ]
}

To test the HTTP inbound with this config, use http://127.0.0.1:10809; for SOCKS, use the corresponding SOCKS address. Some clients generate a mixed inbound that recognizes multiple proxy protocols on one port, but support depends on the core and how the config is generated. When troubleshooting, check the actual config instead of inferring the protocol from a screenshot in a guide.

What UDP and traffic sniffing can—and can't—do

Enabling udp for SOCKS only allows that inbound to handle valid UDP forwarding requests. The app must also support SOCKS UDP, and the outbound and remote path must support it too. This does not automatically capture all UDP traffic on the system. Regular browser requests through an HTTP proxy may take a different path from UDP traffic in other apps, so test them separately.

sniffing tries to identify destination details from traffic that has already entered the core, such as an HTTP hostname or the domain in a TLS handshake, to help with routing. It does not decrypt traffic, and it cannot identify a domain in every request. Encrypted handshakes, IP-only services and nonstandard protocols may provide no clues, so enabling sniffing is no substitute for sound DNS and routing configuration.

destOverride uses sniffed data to handle the destination address; it does more than add a name to the logs. If the original destination is a specific IP but sniffing finds a domain, subsequent resolution and connection behavior may change. If a site only fails with sniffing enabled, compare the same request with the option on and off, and check whether it matches a different rule. Don't change sniffing, DNS and the server all at once—you'll lose a clear basis for comparison.

Sharing with your LAN is not the same as taking over system traffic

To share a computer's proxy with a phone or TV, enter the computer's LAN address on the other device—not the loopback address, which refers to the other device itself. Allow the relevant inbound connection and restrict which local networks can reach it; don't expose an unauthenticated listener on a public network. See how to allow LAN connections for the UI steps. Test from another device after changing the setting, not just from the host computer.

Apps must explicitly use a regular SOCKS or HTTP inbound. The system proxy toggle only publishes proxy settings to apps that support it. TUN involves a virtual network interface, routing and permissions; adding a regular inbound does not provide the same behavior. If a browser works but terminal commands don't use the proxy, check whether the program reads system settings or environment variables. If there are no inbound logs at all, investigate the traffic entry point before changing the remote protocol.

3. outbounds: Exits, Tags and Remote Connections

Outbound objects and the default outbound

An outbound describes how the core connects to a destination after receiving a request. Each object's protocol determines which fields are allowed in settings; don't copy settings between protocols just because the field names look similar. A tag is a local identifier used by routing rules. It is not sent to the server and is not the display name in a subscription. Use short, stable tags, and manage them separately from UI labels.

In traditional configs, traffic that matches no rule usually uses the first outbound in the array. Changing the array order can therefore affect a large amount of traffic. Putting a proxy outbound first and routing private addresses directly is a different default from putting direct first and proxying only selected domains. When migrating a config, decide which default behavior you want, then check the rules; don't just check that all the tags are present.

Direct and block are two basic outbound actions

freedom connects to a destination over the local network. It doesn't mean DNS resolution is unnecessary or guarantee that the destination is reachable. blackhole rejects traffic matched to it; if it is first in the array, unmatched requests may be rejected too. This snippet provides basic direct and block actions for routing rules; it does not include a remote proxy connection.

{
  "outbounds": [
    {
      "tag": "direct",
      "protocol": "freedom",
      "settings": {}
    },
    {
      "tag": "block",
      "protocol": "blackhole",
      "settings": {}
    }
  ]
}

The routing field outboundTag must match a tag here exactly, including letter case and hyphens. If you rename a tag, update every reference. Reusing the same tag across multiple outbounds makes configs harder to read and troubleshoot; tags should not be treated as repeatable category names. Some missing references cause config-loading errors, while other combinations may fail only when a request is handled.

Understand outbound structure with a local upstream

This outbound forwards requests to another SOCKS service on the local machine. It needs no remote credentials and illustrates how a server list is nested; it works only if a service is actually listening on 127.0.0.1:2080. Don't point the upstream port back to the current core's own inbound, or traffic may loop. Before testing, confirm that the upstream service works on its own.

{
  "outbounds": [
    {
      "tag": "upstream",
      "protocol": "socks",
      "settings": {
        "servers": [
          {
            "address": "127.0.0.1",
            "port": 2080
          }
        ]
      }
    },
    {
      "tag": "direct",
      "protocol": "freedom",
      "settings": {}
    }
  ]
}

VMess, VLESS, Trojan and Shadowsocks use different authentication structures. The server address, port, user ID or password, and encryption method must come from the server's configuration. Changing the protocol name alone does not convert one protocol to another. Transport and TLS requirements may apply in addition to protocol parameters. Successfully importing a client link only means it could be parsed; it does not prove that remote authentication and transport settings match.

Which layer should you check when a connection fails?

When the remote address is a domain, it usually needs to be resolved before the proxy connection can be established. If that resolution itself depends on a proxy connection that hasn't been established yet, the setup has a circular dependency. Resolution errors, connection timeouts, refused connections, authentication failures and TLS handshake failures point to different stages in the logs. Troubleshooting by stage is more effective than changing several outbound settings at once.

For multi-hop forwarding, check which chaining fields the current core supports and where they apply. Don't merge an example written for a different core. The local upstream listener, intermediate outbound and final outbound can each fail independently, so verify every hop in sequence. For a typical subscription setup, keep the client-generated single-outbound config unless you know exactly what an additional hop is meant to solve.

Updating a subscription may replace server records, but it won't necessarily update old addresses, credentials or tags in a custom config. If the UI shows updated data but connections still use old values, check the selected config type and startup logs. Before sharing a config, remove credentials, subscription URLs and fields that could identify your account. The structure, protocol names and failure stage are usually enough to discuss a config issue.

4. routing: Rule Order and Outbound Selection

Match first, then select an outbound

Routing is not a server list; it evaluates requests that have already entered the core. Rules are usually checked in array order. Once a rule matches, the request goes to its selected outbound, and later rules are not checked—even if they seem more specific. A broad rule at the top can therefore hide more precise rules below it. Read the config from the first rule down and trace a request through it; don't review domain and IP rules separately and guess the result.

Conditions from different categories in one rule generally all need to match; multiple values within the same category are alternatives. For example, setting both inboundTag and domain means the inbound must match and the destination domain must also qualify—not that either condition is enough. To express two independent cases, use two rules. Inbound tags are especially useful for distinguishing browser traffic from other apps.

A rule snippet that's easy to follow

This snippet requires the direct and block outbounds from Chapter 3. It handles an example blocked domain, private addresses and a documentation domain, in that order. geoip:private requires the runtime to have the relevant data resources. If the logs report a missing data file, fix the resource path or use an explicit CIDR list appropriate for your network; don't ignore the startup error.

{
  "routing": {
    "domainStrategy": "AsIs",
    "rules": [
      {
        "type": "field",
        "domain": ["full:blocked.example.com"],
        "outboundTag": "block"
      },
      {
        "type": "field",
        "ip": ["geoip:private"],
        "outboundTag": "direct"
      },
      {
        "type": "field",
        "domain": ["domain:example.com"],
        "outboundTag": "direct"
      }
    ]
  }
}

Use full: for an exact domain match and domain: for a domain and its subdomains. Neither accepts a URL path, query parameters or a protocol prefix. To match a site's requests, don't put the full URL in the domain array. Regular expressions are useful when you genuinely need a pattern, but they are harder to review. Prefer explicit domain entries when they work.

Domain rules are not a DNS master switch

AsIs tells routing to use the destination information already available where possible, without resolving a domain just to check IP rules. IPIfNonMatch tries to resolve the domain and continue with IP matching if domain matching finds no result. IPOnDemand triggers resolution when a rule requires an IP match. The actual behavior also depends on whether the request arrives with a domain or an IP, so the strategy name alone doesn't tell the whole story.

If an app resolves a domain to an IP before connecting to the local proxy, the core may not have the original domain to match. Sniffing can sometimes provide that information, but it doesn't work for every protocol. If a domain rule isn't matching, first confirm what kind of destination the inbound receives, then check sniffing results, and only then review the domain list. Otherwise, missing information can easily be mistaken for a rule syntax error.

Ports, network types and private addresses

port matches the destination service port, not the local proxy listener port. Use network to distinguish TCP from UDP. Proxying all web ports does not mean all browser traffic uses the proxy: DNS, real-time communication and other protocols may use different destination ports. You also can't infer per-process outbound selection from ordinary routing fields.

Private address rules are often used to keep router admin pages, LAN services and local resources on a direct path. However, whether an IP rule matches a domain that resolves to a private address depends on whether routing has access to the resolution result. With a synthetic IP scheme, follow the core's requirements for mapping and traffic capture. Don't simply treat synthetic addresses as ordinary private IPs and send them directly, or requests may bypass the required address restoration process.

To validate a rule, pick a specific destination, temporarily enable logs that show the outbound tag, make one request, then check the inbound, destination and outbound. Retries can produce multiple log entries, and background browser connections can make results harder to read; a single command-line request is easier to isolate. For connection testing, see how to verify your first connection and proxy. Page load speed alone does not tell you which route was used.

5. dns: Resolution Sources and Circular Dependencies

Start by distinguishing the three kinds of DNS resolution

Before configuring DNS, distinguish app-side resolution, the core's built-in resolver and resolution by the remote server. An app may get an IP from the system and pass that IP to the proxy, or send a domain through SOCKS for the downstream connection to handle. The core's dns object affects only paths that use its built-in resolver. It does not replace the system resolver or change encrypted DNS configured separately in a browser.

The server's own domain may need to be resolved too; this is a different task from resolving the domain of the site you want to visit. Resolving the server is a prerequisite for establishing the proxy connection, while the destination domain may be handled remotely depending on the protocol and outbound. Track these two domains separately when diagnosing DNS issues. If the server address cannot be resolved, every site may appear unreachable; that does not mean all destination lookups are failing.

Basic fields and conditional resolution

servers can contain a simple server address or an object with matching conditions; hosts defines explicit mappings for the built-in resolver. The example shows the structure for a static mapping and a conditional DNS server. It is not a one-size-fits-all setup: assess server reachability, network policies and privacy requirements for your environment. A public resolver is not guaranteed to work everywhere.

{
  "dns": {
    "hosts": {
      "lab.example.com": "192.168.50.10"
    },
    "servers": [
      {
        "address": "1.1.1.1",
        "port": 53,
        "domains": ["domain:example.com"]
      },
      "8.8.8.8"
    ]
  }
}

The LAN address in the example illustrates a static mapping and is useful only if a corresponding service actually exists on your network. The core's hosts setting is not the same as the operating system's hosts file. If a request never uses the built-in resolver, the mapping may not be consulted at all. Static mappings also need to account for changing service addresses, region-specific endpoints and certificate names; a fixed IP is not a general long-term substitute for normal DNS.

How queries are sent and how fallback works

Domain conditions in DNS server entries select a resolver; they do not select a proxy outbound for application traffic. Business traffic is still routed by routing. Whether a failed conditional DNS server falls back to another server, whether queries are sent concurrently and which returned addresses are accepted depend on the relevant fields supported by your core. Fallback options vary between cores, so verify both the field name and its actual behavior before changing anything.

Queries from the built-in DNS resolver also have a network path. Some types follow the core's routing rules, while others with local semantics use the local network stack. Using encrypted DNS alone does not tell you which outbound carries the query. If the resolver address is a domain, you also need a way to resolve that domain first. If the bootstrap lookup depends on the same resolver, it can create a loop.

A typical loop looks like this: establishing the proxy requires resolving the server's domain, but the DNS query is configured to go through that proxy before it has been established. To fix it, map out the dependency order. Keep a working bootstrap resolution path for the server address, then handle application domains. Sending every DNS query through the same proxy outbound may look simple, but can prevent the proxy from starting at all.

IPv4, IPv6 and caching

Getting an AAAA record does not mean your network has a reliable IPv6 route; being able to connect only over IPv4 does not mean the domain lacks IPv6 service. Changing the query strategy affects which address families are returned, but won't fix a router, ISP path or missing remote listener. If some destinations time out, compare the DNS results with the address actually used for the connection. Then, if needed, temporarily restrict the address family to isolate the issue.

DNS caches may exist in the browser, operating system, client and remote services. If the same domain behaves as before after a config change, that does not necessarily mean the new setting was ignored. Restart the component that actually handles resolution or test with a different, known domain, then check the logs. Don't delete all client data just to clear a cache; that adds new variables such as subscriptions, routing and server selection.

When comparing commands, note that curl handles domains differently with socks5:// and socks5h://; the latter sends the destination domain to the SOCKS proxy for resolution. This distinction helps determine whether local DNS is involved, but success only confirms that this particular request path works. For DNS errors while updating a subscription, see troubleshooting subscription update failures, since subscription downloads may not use the current proxy path.

6. policy: Timeouts, User Levels and Statistics

Policies control behavior, not outbound selection

policy sets connection-handling behavior, such as handshake timeouts, idle timeouts and statistics. It is not a routing rule and does not decide whether a domain goes direct or through a proxy. When a connection fails, identify the stage first. Adjust a policy only if the logs show that a wait or idle timeout is actually involved. Increasing every timeout often just makes the error take longer to appear.

Keys in levels are user levels, represented as strings in JSON. They are not speed tiers or subscription plan levels; they are policy indexes referenced by protocol user settings. Whether a user level can be referenced depends on the relevant inbound and protocol. If no custom level is configured, check the default behavior. Adding a level object does not automatically apply it to all traffic.

What the connection timeouts mean

The snippet below shows the structure for level zero and system statistics toggles. Time fields are measured in seconds. The values illustrate the syntax, not recommended settings for every network. To retain the core's defaults, you can leave the relevant fields out. Before changing a value, record the original behavior and test one change at a time, especially with long-lived connections.

{
  "policy": {
    "levels": {
      "0": {
        "handshake": 4,
        "connIdle": 300,
        "uplinkOnly": 2,
        "downlinkOnly": 5,
        "statsUserUplink": false,
        "statsUserDownlink": false
      }
    },
    "system": {
      "statsInboundUplink": true,
      "statsInboundDownlink": true
    }
  },
  "stats": {}
}

handshake sets the wait time for protocol handshakes covered by this policy; it is not a universal timeout for every remote dial, TLS negotiation or web response. connIdle sets how long a connection can remain idle without useful data. Real-time communication, remote sessions and push notifications may have long periods without application data. A short timeout can trigger more reconnects; a long one may leave dead connections using resources for longer.

uplinkOnly and downlinkOnly control the wait period when one side of a connection has finished but the other may still have data to send. They are not upload and download speed limits. A shorter value may close connections sooner, but can also truncate half-closed sessions that still need to transmit data. Pay particular attention to this during large downloads, streaming responses and long-lived requests; testing one short web page is not enough.

How statistics settings relate to the UI

Statistics usually require both a stats object and the corresponding policy settings. System-level statistics apply to objects such as inbounds; user-level statistics require protocol users with identifiable settings. Enabling user statistics does not automatically give every anonymous SOCKS request its own report. Statistic names and retrieval methods also depend on the core's service interface and the client's implementation.

Allowing counters to accumulate in the config does not guarantee that the UI will display them. A program needs the right interface and method to retrieve the statistics, and the interface should listen only on a trusted address. For everyday use, prefer v2rayN's own statistics implementation rather than exposing a control interface just to display a particular number. If no statistics appear, first confirm that traffic passed through the relevant object.

Log levels and resource usage

For everyday use, keep logs at a restrained level such as warning. Temporarily increase verbosity when troubleshooting, reproduce the issue once, then restore the previous level to avoid unbounded log growth. Access logs show where requests go; error logs help identify the stage where a connection failed. Log paths must be writable. A bad path can leave you with no logs, which does not prove that no requests occurred.

If file logging is enabled, a desktop client running as a regular user should not write logs to a system directory that only an administrator can modify. A full disk, read-only directory or security restriction can also affect what you see. If the client opens and immediately exits, check process startup and directory permissions before connection policies. See how to troubleshoot v2rayN startup crashes.

Policy fields do not control operating system process priority, global bandwidth limits or power management. Android background restrictions, Linux sleep and resume, or a network interface change can all invalidate existing connections; increasing an idle timeout alone won't fix them. If connections fail to recover after standby, compare foreground and background behavior and network changes, then check the client's background permissions, system proxy restoration and core logs separately.

7. Protocols, Transport and Core Compatibility

Break a connection into three layers

When describing a server, distinguish the proxy protocol, the transport and the security layer. VMess, VLESS, Trojan and Shadowsocks handle proxy data and authentication; TCP and WebSocket describe how the connection is carried; TLS and other security mechanisms protect the connection. All three layers must match the server. A correct domain and port are not enough if the transport type, path or certificate name is wrong.

In a config, settings belongs to the proxy protocol and streamSettings belongs to the connection transport. Putting a WebSocket path in a protocol user object usually won't work; putting authentication fields in the transport object won't authenticate the protocol. Check the field's level before checking its value. That's more useful than repeatedly importing the same malformed link.

Reading WebSocket and TLS snippets

This is a transport snippet inside a compatible outbound object; it cannot run on its own and must be merged with a real server's protocol settings. The example domain and path show where fields go; they are not a working server. If your client generates different field names, follow the structure supported by the selected core. In particular, don't treat names from different generations of transport as interchangeable aliases.

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

The outbound server address determines the initial connection destination. TLS serverName is used for server name indication and certificate verification; the WebSocket Host is the host in the HTTP upgrade request. They may be the same or differ in a valid reverse-proxy deployment, but don't guess. Even if you connect to the right IP, a front-end service may reject a mismatched name.

The WebSocket path must match the server's routing. A leading slash, letter case, trailing slash or query string may affect how the request is handled. If you get an unexpected HTTP response, check that the reverse proxy forwards the request to the correct backend. Opening the domain's home page in a browser only proves that the website is reachable; it does not confirm that the upgrade path or proxy backend works.

Don't hide certificate errors by disabling verification

Keep allowInsecure disabled. For certificate errors, check the system clock, certificate validity dates, name match, certificate chain and system trust store. If your organization uses an authorized internal certificate authority, manage trust according to its policies rather than skipping verification for every connection. Disabling verification makes it harder to detect an impostor server and can conceal real configuration problems.

A TLS handshake can also fail when the security layers do not match: the client starts TLS but the service on that server port accepts plain TCP, or vice versa. Some errors occur before authentication, so changing the user ID won't help. Confirm what service is actually listening on the port, then check the security layer, transport upgrade and proxy authentication, in that order. A clear sequence avoids unproductive trial and error.

How V2Fly, Xray and clients fit together

Project V provides background on the related modular proxy design. V2Fly and Xray are different core families. They share parts of the basic JSON structure, but newer protocol capabilities, security-layer fields and some routing behavior have diverged. REALITY requires a core that explicitly supports it; don't add its settings to an arbitrary V2Ray config. Get parameters from a source that matches the server.

With v2rayN on desktop, first check which core the current config actually invokes. On Android, account for Xray capabilities in v2rayNG and V2Fly capabilities in v2flyNG. A client name does not imply a fixed set of config fields, and a similar-looking UI does not prove compatibility. For help choosing, see a comparison of three clients and their cores before migrating.

When migrating a config, check the protocol, authentication, security layer, transport, routing and DNS one layer at a time. Keep one working server first, then move over additional rules. Don't delete an unfamiliar field and carry on; it may be essential to the connection. A config that loads is not necessarily a config that does what you intended. Verify its behavior with a specific request and logs.

8. Merging Configs, Validation and Layered Troubleshooting

Apply changes to the actual source of the config

Before making changes, export or back up the client's current data and save a separate copy of any working custom config. Edit ordinary server records in the v2rayN UI, routing presets in the routing manager, and full custom configs separately. Don't edit a temporary client-generated file while switching servers in the UI; it becomes difficult to tell which version is actually running.

Before merging snippets from this guide, make a checklist: keep only one instance of each top-level key; avoid inbound port conflicts; keep outbound tags unique; make sure every rule references an existing tag; and confirm that any required data resources are present. The private addresses and documentation domains in examples are there for demonstration. Remove rules you don't need instead of leaving tutorial settings in your live config.

Test the inbound before the outbound

After restoring the complete baseline from Chapter 1 and starting it, run the SOCKS test below. It requires a SOCKS listener on 10808 on the local machine. The destination is a public documentation domain, so the test does not depend on private server details. If that site is unavailable on your network, use a known reachable HTTPS address instead, and keep the destination the same throughout your comparisons.

curl -I --connect-timeout 10 --max-time 20 \
  --proxy socks5h://127.0.0.1:10808 \
  https://example.com

If you use the two-inbound config from Chapter 2, you can test the HTTP inbound separately. The command below is useful only if that port is enabled. The server may return a successful response, a redirect or another valid HTTP status. A response confirms that the request reached an HTTP service; it does not prove that it went through a remote proxy. The baseline uses a direct outbound, so its result cannot confirm server connectivity.

curl -I --connect-timeout 10 --max-time 20 \
  --proxy http://127.0.0.1:10809 \
  https://example.com

Explicitly specifying the proxy address is more useful for troubleshooting than relying only on system proxy settings, since it reduces the impact of environment variables, desktop settings and app compatibility. To test the system proxy, make a request from the app in question and check the inbound logs. If a command works but a browser doesn't, check the browser's settings; if the browser works but another program doesn't, check that program's proxy support and DNS behavior.

Narrow down the issue by failure stage

Observed symptomCheck firstNext step
Core fails to startJSON structure, supported fields, resource filesRestore the minimal baseline, then add config sections one at a time
Local connection refusedListen address, port and process statusMake sure the request protocol matches the inbound
Name resolution failedServer domain and DNS bootstrap pathDistinguish server resolution from destination resolution
Remote connection timed outAddress, port and network reachabilityConfirm the remote service is available before checking transport
TLS or authentication failedName, time, transport and authentication fieldsCompare each layer with the server config
Only some destinations failMatched rule, address family and outbound tagRepeat the comparison with a single destination

If the core starts but no requests appear in the logs, first confirm that the app is using the proxy; don't immediately blame the remote server. If requests appear but use the wrong outbound tag, check rule order and the default outbound. If the outbound is correct but the connection fails, check server DNS resolution, network connectivity, TLS and authentication. Follow the evidence one layer at a time.

Don't treat a ping result as a proxy protocol test. A destination may block ICMP but accept normal application connections; it may also respond to ICMP without listening on the expected port. Likewise, a successful TCP connection only shows that something accepted the connection, not that it speaks the expected protocol. Record the inbound test, port reachability, protocol handshake and actual request separately; one result cannot stand in for the whole path.

Restore and verify again

After a change passes validation, restart the client and repeat the same test to make sure regenerated config hasn't overwritten the setting. Then test LAN resources, destinations that need a proxy and long-lived connections. Confirm that fixing one path didn't break another. Also check what happens after a network change or resume from sleep, especially on desktop setups using the system proxy or TUN.

When reporting an issue, include the core name, operating system, failure stage and relevant snippets with sensitive information removed. Never share subscription URLs, complete credentials or logs containing account parameters. If you can't identify the problem after a change, restore your backup and repeat the minimal tests in this chapter. For common symptoms, see the FAQ; to review importing and connecting from the beginning, return to the Getting Started guide. Reinstalling repeatedly is no substitute for diagnosing the config.