一、JSON 结构与配置边界
先分清客户端与内核
v2rayN、v2rayNG 和 v2flyNG 是图形客户端,负责管理订阅、节点、系统代理及启动过程;真正按配置收发连接的是内核。Project V 提供了相关技术生态的基础,V2Fly 延续 V2Ray 的社区发展,Xray 则是另一条独立演进的内核分支。它们共享不少概念,但字段支持并不完全相同。看到一份能用的 JSON,首先确认它面向哪个内核,再看当前客户端实际启动了哪个程序。
本页以传统 JSON 配置结构为阅读主线,远端协议示例主要采用 Xray 常见写法。v2rayNG 通常围绕 Xray 使用,v2flyNG 是采用 V2Fly 内核的安卓备选;v2rayN 可以管理不同内核,具体能力取决于所选内核及客户端设置。不能因为菜单里都有“自定义配置”,就把针对某个分支的新字段直接搬到另一分支。遇到未知字段或不支持的协议,先核对内核,而不是反复更换订阅。
顶层对象怎样串起请求
配置的最外层是对象。inbounds 描述请求从哪里进来,outbounds 描述请求从哪里出去,routing 决定两者之间的选择关系。dns 提供内置解析器设置,policy 管理部分连接与统计策略,log 帮助观察运行过程。它们不是按书写先后依次执行的脚本;字段放在文件前面,并不意味着会比后面的模块更早处理流量。
下面是一份刻意缩小的本地测试配置:只监听本机 SOCKS 端口,收到请求后直接访问目标。它适合验证 JSON、端口和应用代理设置,不会建立远端代理连接。端口如果已被其他程序使用,需要同步修改配置和测试应用。这个起点越简单,后续加入远端出站、路由和 DNS 时就越容易判断是哪一步引入了问题。
{
"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": {}
}
]
}
语法正确不等于语义正确
JSON 的对象键和字符串使用英文双引号,数组使用方括号,布尔值写成 true 或 false,而不是带引号的文字。对象最后一项后面不加逗号。即使某些加载器容忍注释,也建议保存为严格 JSON,方便在不同客户端之间检查。不要在同一个对象里重复写同名键;有的解析器会覆盖前值,有的会拒绝,这会让配置的实际含义难以判断。
语法通过后,还要检查字段所在层级。例如 port 属于入站对象,远端服务器的端口则位于对应协议的服务器设置里;streamSettings 通常与出站的 settings 平级,而不是塞进服务器地址对象。结构错误不一定表现为无法读取文件,也可能让某项设置被忽略。阅读示例时先辨认外层括号,再复制字段,往往比对照单个关键词更可靠。
建议保留一份可运行的基线副本,并为每次调整记下目的,例如“仅增加内网直连规则”。节点凭据、订阅地址和日志中的访问目标都可能包含私人信息,分享配置前应替换。本文带远端地址的示例使用说明用途的域名与示例标识,展示结构而非可连接服务;只有上面的本地直连配置可以在端口空闲时直接用于测试。
二、inbounds:本地请求入口
监听地址、端口与协议
入站首先回答三个问题:在哪个地址等待连接、使用哪个端口、接受什么协议。listen 写成 127.0.0.1 时,仅本机能够访问这个 IPv4 监听地址;写成 0.0.0.0 则会监听所有 IPv4 接口,可能让局域网设备接入。后者不是“兼容性更好”的通用开关,需要同时考虑防火墙、网络环境和访问控制。只给本机浏览器使用时,保留回环监听更容易管理。
port 是本地服务端口,不是远端节点端口。把两个数字填成一样没有特殊意义。SOCKS 和 HTTP 是不同的入站协议,应用端也要选对类型;仅填写正确的地址与端口还不够。系统代理、浏览器扩展和终端变量可能各自指向不同入口,所以某个浏览器能打开页面,并不能证明所有应用都已经进入同一个入站。
增加一个 HTTP 入口
下面展示两个入站的组合,可以替换第一章配置的 inbounds 数组。SOCKS 用于支持该协议的应用,HTTP 用于普通 HTTP 代理及支持 CONNECT 的客户端。两个端口必须分别空闲。HTTP 入站不是通用 UDP 入口,SOCKS 允许 UDP 也不代表所有应用都会发送 SOCKS UDP 请求;它只是为具备相应能力的应用提供条件。
"inbounds": [
{
"tag": "local-socks",
"listen": "127.0.0.1",
"port": 10808,
"protocol": "socks",
"settings": {
"auth": "noauth",
"udp": true
}
},
{
"tag": "local-http",
"listen": "127.0.0.1",
"port": 10809,
"protocol": "http",
"settings": {}
}
]
tag 是配置内部的名字,后续路由可以通过 inboundTag 精确引用它。建议使用能表达用途的短名,并确保不重复。标签不会自动创建系统代理,也不会因为名字叫“全局”就接管所有流量。若希望不同入口采用不同出口,可以分别命名,再在路由规则里明确对应关系,避免依赖客户端显示名称的猜测。
嗅探补充的是目标信息
部分应用只交给内核一个目标 IP,域名规则因此缺少匹配依据。嗅探可以尝试从受支持的初始协议数据中识别 HTTP 主机名或 TLS 的服务器名称信息。常见配置放在入站对象的 sniffing 中;enabled 开启后,destOverride 指定尝试识别的类型。它不是解密工具,也不能保证每个连接都能得到域名,遇到加密握手信息或非目标协议时可能无法补充。
"sniffing": {
"enabled": true,
"destOverride": ["http", "tls"],
"routeOnly": true
}
这段是 Xray 常见的入站补充项。routeOnly 表示将嗅探所得信息用于路由判断,而不据此直接改写实际连接目标,具体支持情况仍需核对内核。排查时要区分“识别出域名”“规则匹配成功”和“目标连接成功”三件事。嗅探有结果只能证明第一步,不能证明 DNS、远端传输或目标服务都正常。
安卓 VPN 接管与普通代理不同
在 v2rayNG 中,系统 VPN 授权与流量接管由客户端配合完成。不能只复制一个 SOCKS 入站,就期待得到同样的全设备接管效果。分应用代理、绕过应用和后台运行限制也位于 JSON 之外。自定义配置导入后,应确认客户端如何接入该配置,以及哪些本地端口由客户端保留,避免和它自动生成的监听入口重复。
桌面端同样需要区分系统代理与 TUN。系统代理只影响遵循相关设置的程序,TUN 还涉及虚拟接口、权限和路由处理,并非单个入站字段就能完整描述。遇到“只有部分应用可用”,先查看应用是否进入内核,再看出站。针对浏览器与终端的区别,可以继续阅读系统代理分开排查记录,不要一开始就改远端协议参数。
三、outbounds:连接出口与协议参数
默认出口与标签引用
出站决定内核如何连接目标。一个配置可以同时包含远端代理、直接连接和阻断出口,但拥有这些出口并不意味着它们会自动分工。没有匹配路由时,传统配置通常使用出站列表的第一项作为默认出口。因此调整数组顺序可能改变未命中规则的请求行为。先选定默认策略,再添加例外,比把一组标签随意排列后观察结果更清楚。
路由通过 outboundTag 引用出站的 tag,二者需要完全一致。界面中显示的节点备注不一定等于运行配置里的标签。一个常见问题是复制规则后仍指向旧名称,导致内核无法找到目标出口。另一类问题是把默认出口放成直连,却以为未匹配流量都会经过代理。阅读配置时,应从出站数组第一项开始确认,再逐条追踪路由引用。
直连与阻断的基础写法
freedom 用于直接连接请求目标,blackhole 用于终止流量。下面是两个可独立引用的出口对象,可放进现有出站数组。直连仍然使用设备自身的网络,并不保证目标可达;阻断也不是一个能完成访问的备用节点。排查时可以临时将某个测试域名指向直连,比较代理链路与本地链路的差别,但应记录修改并在测试结束后恢复。
{
"tag": "direct",
"protocol": "freedom",
"settings": {}
},
{
"tag": "block",
"protocol": "blackhole",
"settings": {}
}
VLESS 示例的字段归属
下面展示一个 Xray VLESS 出站结构。示例域名和用户标识仅供阅读,需要换成服务端实际提供的参数才能连接。address 是建立远端连接所用的地址,port 是服务端监听端口,users 是该协议的用户参数集合。id 属于用户标识,encryption 的写法由协议定义;这里的 none 不代表传输层也没有安全保护。
{
"tag": "proxy",
"protocol": "vless",
"settings": {
"vnext": [
{
"address": "edge.example.org",
"port": 443,
"users": [
{
"id": "00000000-0000-4000-8000-000000000001",
"encryption": "none",
"level": 0
}
]
}
]
},
"streamSettings": {
"network": "ws",
"security": "tls",
"tlsSettings": {
"serverName": "edge.example.org"
},
"wsSettings": {
"path": "/tunnel"
}
}
}
这段的协议设置与传输设置是两个层次。VLESS 用户标识正确,而 WebSocket 路径不对,连接仍会失败;TLS 能握手,也不证明用户认证通过。服务端要求的流控选项、传输类型及其他参数必须逐项对应,不能看到协议名相同就认为节点等价。示例没有包含所有可选字段,未出现的参数也不应靠猜测补齐。
不同协议不要混用设置结构
VMess 同样常见 vnext 结构,但用户参数的含义与 VLESS 不同;Trojan 通常使用 servers 数组及密码字段。不要将 Trojan 密码放进 VLESS 的 id,也不要把一个协议的用户对象原样套给另一个协议。节点分享链接导入后,先在客户端界面核对协议和关键参数,比手动拼接长文件更不容易遗漏。
出站还可能包含域名解析、接口绑定、链式转发等进阶选项。这些能力跨内核差异较大,只有明确需要时再增加。所谓“备用出口”也不等于自动故障切换;仅多写一个出站,内核不会因此自动测速并选择更快的那个。自动选择通常还需要相应的选择机制、观测逻辑或客户端功能支持,应按当前内核文档分别配置。
如果导入后只有某个出站失败,保留其余可运行设置,只替换这一项的地址、认证或传输参数进行比较。避免同时改出口顺序与路由,因为这样即使连接恢复,也难以知道是节点修好了,还是流量实际上走到了另一条出口。
四、传输与安全层:分开核对参数
协议、承载方式和加密不是一回事
读一条节点配置时,可以把它拆成三层:应用代理协议负责身份和转发语义,传输方式负责怎样承载连接,安全层负责相应的握手与保护。例如 VLESS、WebSocket、TLS 分别属于不同层面。协议名字正确只是起点,服务端如果期待 WebSocket,客户端却使用普通流式连接,连接不能仅凭端口一致而成功。
streamSettings.network 选择传输方式,streamSettings.security 选择该层的安全机制,具体设置再分别放到相应对象中。不同内核对传输名称及新旧字段的支持存在变化,所以不能把某份面向新内核的示例当成跨版本通用模板。先保留导入时的原始参数,再围绕某个明确报错修改,尤其不要批量替换所有看起来类似的网络名称。
WebSocket 与 TLS 对照
下面是一个传输片段,应该放在对应出站对象中,而不是单独作为完整配置运行。路径要与服务端或前置反向代理匹配,包含开头斜杠;如果实际路径带查询参数,也需要保留其含义。serverName 用于 TLS 服务器名称相关处理,WebSocket 的 Host 则属于 HTTP 请求层。两者常常相同,但不能因此视作同一个字段。
"streamSettings": {
"network": "ws",
"security": "tls",
"tlsSettings": {
"serverName": "edge.example.org",
"allowInsecure": false
},
"wsSettings": {
"path": "/tunnel",
"headers": {
"Host": "edge.example.org"
}
}
}
实际连接地址可以是域名,也可以是服务提供方指定的地址;证书名称、TLS 服务器名称和 HTTP 主机名分别参与不同阶段。地址能连通而证书名称不匹配时,应检查服务端给出的域名和本机时间,不要直接把 allowInsecure 改成 true。跳过验证会掩盖证书或目标错误,不能作为日常修复方案。
| 参数 | 主要作用 | 常见核对点 |
|---|---|---|
address | 远端连接目标 | 域名解析与地址可达性 |
serverName | TLS 名称相关处理 | 证书名称与服务端要求 |
headers.Host | WebSocket HTTP 主机名 | 前置服务的虚拟主机匹配 |
path | WebSocket 请求路径 | 大小写、斜杠与完整路径 |
REALITY 与其他传输的边界
REALITY 的客户端参数与普通 TLS 设置不同,不能只把 security 改个名称就完成迁移。服务器名称、密钥相关参数、短标识等需要与服务端一致,而且字段支持取决于具体内核。使用分享链接导入时,也应核对客户端有没有识别全部参数。若界面缺少对应设置,先确认支持情况,不要自行把参数塞进相似名称的对象。
同理,gRPC 的服务名称不是 WebSocket 路径,HTTP 相关传输的请求头也不能跨类型随意复用。传输更换一般要求服务端一起调整,客户端单方面切换不会让服务端自动兼容。比较两条节点时,应先确认它们是否本来就是同一套服务参数,再讨论表现差异;否则测试得到的是不同链路的结果,而不只是某一种传输的性能。
按失败阶段阅读日志
连接超时通常先看地址、端口和路径可达性;出现证书错误,重点核对名称、证书链及系统时间;返回 HTTP 状态但升级失败,常见检查点是路径、主机名和前置服务配置。认证失败则要回到代理协议层检查用户标识或密码。日志给出的错误属于哪一层,下一步就优先查哪一层,避免把所有失败都归为 DNS。
排查时暂时使用单个节点、单个应用和固定测试目标,关闭会改写请求的代理扩展,保留可比较的环境。不要同时切换传输、证书设置和路由模式。每次只改一个变量,记录失败阶段是否前移或后移;从“不能建立连接”变成“认证失败”虽然还没成功,却说明前面的网络与握手环节已经有了进展。
五、routing:规则顺序与目标匹配
从上到下,命中即选择出口
路由不是把所有匹配结果合并后再打分。常见字段规则按数组顺序检查,先命中有效规则就选择其指定出口,因此精确例外应放在宽泛规则之前。一个过早出现的“所有流量走代理”规则,会遮住后面的内网直连规则。查看配置时,不能只搜索有没有某条规则,还要看它前面是否已经存在足以接住这条请求的规则。
规则中的不同条件字段通常需要同时满足,同一字段数组内则通常是多个候选值。比如在同一条规则中同时写 inboundTag 与 domain,含义是来自指定入口且目标域名匹配,而不是满足任一条件即可。需要表达“域名属于某集合,或者目标 IP 属于某网段”时,分成两条规则更直观,也更方便从日志确认是哪条条件发挥作用。
一个可读的规则片段
下面示例先阻断一个说明用途的精确域名,再将私有地址直连,之后把指定域名及其子域交给代理。它依赖已经存在的 block、direct 和 proxy 出站标签。geoip:private 需要可用的 GeoIP 数据文件;如果环境尚未准备好数据,应先用明确网段测试,不能假设集合名称本身就包含了全部地址。
"routing": {
"domainStrategy": "AsIs",
"rules": [
{
"type": "field",
"domain": ["full:blocked.example.org"],
"outboundTag": "block"
},
{
"type": "field",
"ip": ["geoip:private"],
"outboundTag": "direct"
},
{
"type": "field",
"domain": ["domain:example.org"],
"outboundTag": "proxy"
}
]
}
full: 用于完整域名匹配,domain: 用于域名及其子域匹配,regexp: 表示正则表达式,geosite: 引用域名集合。普通未加前缀的文字可能具有更宽泛的匹配语义,不适合拿来代替精确域名。希望只匹配一个主机时,优先使用明确的前缀,避免把名称中恰好含有相同文字的其他域名也纳入规则。
域名解析策略改变匹配过程
AsIs 尽量使用请求原有的目标信息进行路由,不为了 IP 规则主动把每个域名解析一遍。因此上例中以域名发起的请求,不会仅因其实际解析到私有地址就必然命中私有 IP 规则。IPIfNonMatch 会在域名未匹配时尝试解析并再次进行相关匹配;IPOnDemand 则在匹配过程需要 IP 信息时尝试解析。二者都会带来额外的解析依赖。
这些选项主要影响路由判断,不能简单等同于“所有流量强制本地解析”或“DNS 一定走代理”。域名最终由本机解析还是交给远端,也受到所选出站和协议行为影响。若改策略后网页首次打开变慢,先观察是否增加了解析等待。只有明确需要 IP 集合判断时才启用相应策略,而不是认为解析得越早,规则就一定越准确。
集合文件与界面规则的关系
geosite.dat 与 geoip.dat 分别为域名和 IP 集合提供数据,不是内核配置里的普通注释。集合名称必须存在于当前数据文件中,文件加载失败或名称拼错都可能导致启动失败。更新后分类内容也可能变化,排查时应记录数据来源与变更。具体更新路径和规则顺序可参考GeoIP 与 GeoSite 使用说明。
v2rayN 的路由预设及 v2rayNG 的自定义规则,可能在生成运行配置时与界面选项组合。若编辑了文件但结果仍像旧规则,先查看当前实际选中的配置和模式,再确认客户端有没有重新生成文件。验证时选择一个精确域名、一个已知私有地址分别测试,观察出口标签;只凭某个网页打开成功,无法证明全部分流规则都按照预期工作。
六、dns:解析来源与连接路径
内置 DNS 不等于系统 DNS
配置里的 dns 定义内核内置解析器如何工作,但不会自动改写设备所有应用的 DNS 设置。浏览器可能启用自己的加密 DNS,应用可能调用系统解析器,安卓 VPN 接管也可能由客户端安排 DNS 流量。排查之前先问清楚:现在失败的是谁的解析请求、请求从哪里发出、由哪个解析器回答。把内核 DNS 改了,却继续测试浏览器独立的解析路径,往往看不到预期变化。
路由模块、直连出站和其他功能是否调用内置解析器,取决于具体选项。应用已经传入 IP 时,内核可能根本不需要再解析原始域名;应用把域名交给代理时,才有机会保留域名信息用于规则或远端处理。因此“日志没有 DNS 查询”不一定是 DNS 模块故障,也可能是请求早已带着地址进入,或当前流程不需要本地解析。
先建立简单的解析基线
下面的片段指定两个普通 DNS 服务器,并在需要内核解析时优先只获取 IPv4 结果。它是理解服务器列表与查询策略的基础示例,不表示这些地址在所有网络中都可达,也不表示这些查询天然通过代理发送。实际环境应选择自己能够访问且愿意使用的解析服务,再按内核的具体行为安排查询路径。
"dns": {
"servers": [
"1.1.1.1",
"8.8.8.8"
],
"queryStrategy": "UseIPv4"
}
UseIPv4 限制内置解析器的查询结果方向,不会关闭操作系统的 IPv6,也不会改写应用直接发起的 IPv6 连接。它可以用于隔离某些地址族问题,但不应被当作所有连接失败的固定答案。如果设备和节点均有稳定的 IPv6 支持,需要结合实际目标测试后再决定策略,避免因为临时绕过问题而长期失去原本可用的连接路径。
静态映射与域名定向解析
hosts 可以为特定名称提供内置解析器使用的静态映射。例如下面把一个说明用途的测试域名映射到回环地址,适合测试本地服务的解析环节。它不等于修改系统 hosts 文件,也不保证所有带该域名的出站都会被自动重写;只有调用这套解析流程的操作才会使用映射。测试结束后要删除,避免之后把本应访问远端的请求带回本机。
"dns": {
"hosts": {
"lab.example.org": "127.0.0.1"
},
"servers": [
"1.1.1.1"
],
"queryStrategy": "UseIPv4"
}
更复杂的 DNS 配置可以为某些域名指定解析服务器,或设置预期 IP 范围和回退行为。但这些选项的组合存在内核差异,不能只复制一个服务器对象就认为实现了完整的分流解析。尤其是“定向服务器没有返回可接受结果后怎么办”,需要明确回退是否允许、会回到哪些服务器。先画清分支,再加字段,比堆叠多组解析器更好维护。
避免解析依赖绕回自身
使用加密 DNS 时,解析服务器自身的域名也可能需要先解析;如果这个解析动作又依赖尚未连接的代理,就会形成启动依赖。类似地,代理服务器地址是域名,而其解析又被配置成必须经过该代理,也可能造成连接无法开始。排查时先保留一个明确可达的基础解析路径,再逐步加入加密解析和路由要求,不要一开始就把所有查询都绑到同一个未验证出口。
DNS 查询的发送方式还会受到服务器地址形式影响,普通 UDP 查询、加密查询和某些本地处理方式并不具有完全相同的路由行为。不要仅给 DNS 加一个标签,就假定每一种查询都已经按该标签进入路由。应核对当前内核对相应服务器形式的说明,再用日志和实际网络现象确认。域名解析成功只说明得到了地址,仍需验证该地址是否能够通过最终出口连接。
当出现“节点显示连接,但网页打不开”,可以先比较同一目标的解析结果与连接错误,检查系统时间、地址族和分应用代理,再判断是否需要调整 DNS。完整的顺序见已连接却无法访问的逐项清单。每次只替换一个解析器,并重新发起连接;应用缓存可能让旧结果继续生效,需要一并考虑。
七、policy:连接策略与统计开关
策略等级不是系统权限等级
policy 将部分连接行为集中到策略对象中。levels 里的键是用户等级标识,例如字符串键 "0" 对应等级零;协议用户对象可以通过相应的 level 字段引用。这个等级不代表管理员权限,也不是节点质量分数。没有明确分级需求时,先理解默认策略,通常比给所有用户增加多个等级更容易维护。
并非每一种入站与出站都会以相同方式使用用户等级。字段能写进 JSON,不代表当前协议一定有对应的用户上下文。判断某项策略是否生效,需要同时看协议支持、等级引用和连接类型。尤其不能仅在 levels 里新增一个对象,就认为所有流量都会自动使用它;关联关系需要在支持该机制的位置明确建立。
超时参数按阶段理解
handshake 约束适用连接的握手等待,connIdle 处理无有效数据活动的空闲阶段,uplinkOnly 和 downlinkOnly 则对应连接只剩某一方向仍在工作的阶段。它们不是四个相互替代的“连接超时”,也不是写得越小越灵敏。移动网络切换、长连接通知和慢速握手对等待时间的需求可能不同,应按实际故障调整。
"policy": {
"levels": {
"0": {
"handshake": 4,
"connIdle": 300,
"uplinkOnly": 2,
"downlinkOnly": 5
}
},
"system": {
"statsInboundUplink": false,
"statsInboundDownlink": false,
"statsOutboundUplink": false,
"statsOutboundDownlink": false
}
}
上例中的时间字段以秒计,用于展示完整的层级关系,不是针对所有设备的优化建议。若握手尚未完成就被结束,应先检查链路和服务端响应,再评估等待时间是否确实过短。若长时间静置后连接重建,应结合应用的重连机制、系统后台限制和网络变化判断,不能只根据“断开”两个字就增大所有超时。
统计需要模块与开关配合
policy.system 中的统计开关控制相应方向的数据统计。内核侧通常还需要启用顶层 stats 模块,客户端若要读取这些数据,还需要相应的查询接口或自身集成方式。仅打开开关不会自动生成一个可见面板。不同统计维度也不能混为一谈:入站统计看入口,出站统计看出口,用户统计则依赖可识别的用户及相关配置。
"stats": {},
"policy": {
"system": {
"statsInboundUplink": true,
"statsInboundDownlink": true,
"statsOutboundUplink": true,
"statsOutboundDownlink": true
}
}
这是顶层配置片段,合并时应与已有 policy 对象整合,不能在同一层再写第二个同名键。统计通常反映内核处理的数据,不等同于运营商计费流量;连接头部、重传和系统其他流量可能不在同一口径内。客户端显示的累计量还可能在重启后重新开始,观察时应先确认统计范围与生命周期,而不是将不同界面的数字直接相减。
性能问题不要先靠堆策略参数
网速慢更常见的分层因素包括目标服务、远端节点负载、网络线路和本地接管方式。策略里的等待时间主要管理连接生命周期,不会凭空增加可用带宽。部分内核允许调整连接缓冲设置,但缓存大小不等于吞吐能力,过度增加也可能带来内存压力。应先获得可重复的测试结果,再判断某个参数是否与观察到的问题有关。
安卓设备还需要留意电池限制与后台运行条件;桌面端则要查看系统代理、TUN 和其他网络工具是否相互影响。这些因素并不都属于 policy。如果应用退出后台后才失联,先排查客户端是否仍在运行,而不是把空闲时间改成极大值。若只有某个时段慢,先对照不同节点与直连结果,具体方法可查网速慢的分层诊断。
八、配置合并、日志验证与回退
把片段合成一份有效文件
本页多个示例都是局部片段,不应直接首尾拼接。合并时保留一个顶层对象,把入站对象放入同一个 inbounds 数组,把出口对象放入同一个 outbounds 数组;路由规则追加到现有的 rules 中,并重新检查顺序。顶层的 dns、policy 和 log 各自只保留一个明确对象,避免重复键让前面的设置被覆盖。
完成语法检查后,再沿着一条请求走一遍:应用是否连接到正确入站,入口标签是什么,请求保留了域名还是只有 IP,第一条命中的规则是哪条,选择的出站标签是否存在,远端传输参数是否完整。这个手工追踪过程能发现不少“文件可以启动,但访问方式不对”的错误。尤其要核对默认出口,防止测试成功其实来自意外直连。
先在客户端确认加载结果
v2rayN 中应先确认当前选中的配置、内核类型和路由模式,再查看日志窗口;v2rayNG 中则确认当前配置条目、VPN 授权与服务状态。菜单名称可能随客户端调整,以当前界面为准。自定义配置与普通节点编辑的管理方式可能不同,改完后要重新启动相应服务,并确认日志中加载的确实是刚才修改的内容,而不是另一个配置条目。
临时将日志等级提高到 info,通常更容易观察连接目标和出口。排查结束后可以恢复到 warning,减少长期记录的内容。若配置文件还没通过解析,文件内的日志设置本身也可能来不及生效,此时要看客户端显示的启动输出。不要因为没有生成日志文件,就判断内核没有尝试启动。
"log": {
"loglevel": "info"
}
用固定目标验证本地入口
桌面端如果有可用的命令行请求工具,可以显式指定本地代理,隔离系统代理是否生效的问题。下面两个命令分别测试 SOCKS 与 HTTP 入口,对应第二章的端口。SOCKS 命令让域名通过代理侧流程处理,HTTP 命令则使用本地 HTTP 代理访问同一个说明用途的站点。它们验证的是请求能否经过指定入口完成,不是测速命令。
curl --proxy socks5h://127.0.0.1:10808 https://example.org/
curl --proxy http://127.0.0.1:10809 https://example.org/
若命令提示本地连接被拒绝,优先检查监听端口与服务状态;如果能进入内核但远端超时,再查出口和网络;如果命令成功而浏览器失败,重点查看浏览器扩展、独立代理设置或 DNS 行为。安卓端可以用固定浏览器与固定目标重复测试,同时观察 v2rayNG 日志及分应用设置,不必为了照搬桌面命令额外安装工具。
按错误类型缩小范围
| 现象 | 优先检查 | 下一步 |
|---|---|---|
| JSON 读取失败 | 引号、逗号、括号与层级 | 回到最近修改的对象 |
| 监听失败 | 端口占用与地址权限 | 停止冲突程序或更换端口 |
| 找不到集合 | 数据文件与集合名称 | 核对加载路径和规则拼写 |
| 握手或认证失败 | 传输、安全层与用户参数 | 按失败阶段逐项比较 |
| 只有部分应用失败 | 代理入口与接管范围 | 对比显式代理和系统设置 |
日志里的最后一行有时只是前面失败的结果,例如连接关闭可能源自更早的解析或认证错误。保留同一次尝试的上下文,从第一处明确错误往下读,比只截取末尾更有帮助。分享时替换用户标识、密码、订阅地址和访问目标,保留字段名、错误类型及必要结构即可。更细的启动错误分析见v2rayNG 内核日志阅读。
为每次变更保留回退点
比较稳妥的顺序是:基线启动成功,加入远端出站,验证单条连接,再增加路由、DNS 和策略。每一步保存一份副本,并写明变更目的。出现故障时先恢复最近可用配置,确认设备与网络仍然正常,再单独重放失败的修改。这样可以把环境变化与配置问题分开,也避免在一份已经改乱的文件里继续叠加补丁。
最后把需要日常维护的设置尽量放回客户端可管理的位置,确认订阅更新后哪些内容会被保留、哪些会重新生成。本手册用于理解底层关系,不要求每次使用都手写完整 JSON。需要重新梳理导入与连接步骤时,返回快速上手教程;需要更换适配系统的程序时,再到客户端页面选择对应入口。