01 / JSON 结构与配置归属
先分清客户端设置与内核配置
先找到正在使用的配置来源,再决定修改位置。v2rayN、v2rayNG 与 v2flyNG 都是图形客户端,负责管理订阅、选择配置和启动内核;内核负责实际接收连接、匹配路由并转发数据。界面里保存的订阅地址、窗口选项和自动更新计划,不一定属于内核 JSON。把客户端的设置文件当成内核配置编辑,常会出现字段存在、运行结果却没有变化的情况。
再确认配置是否由客户端自动生成。切换节点、调整路由或重新启动时,客户端可能重写运行配置。直接修改临时文件,往往只能维持到下一次生成。需要长期保留的内容,应优先在图形界面里设置;确实需要自定义 JSON 时,使用客户端明确支持的自定义配置入口,并保留原始文件。导出、查看和编辑属于不同操作,看到一段 JSON 不代表该位置可以持久保存修改。
按对象与数组理解层次
把最外层看成一组职责不同的对象。inbounds 与 outbounds 是数组,因为可以同时存在多个入口和出口;routing、dns、policy 通常是对象,用来容纳各自的细分设置。对象用花括号包围,数组用方括号包围。字段名与字符串使用英文双引号,数字和布尔值不加引号。端口写成数字,开关写成 true 或 false,不要把它们全部转换为文本。
下面是一份可独立运行的本机直连示例,用于理解结构。它只在本机提供 SOCKS 入口,并把收到的连接直接交给系统网络,不会建立远端代理连接。先让需要测试的应用连接到本机端口,再观察内核是否收到请求。示例不包含远端服务参数,不能替代订阅提供的连接配置;在客户端中使用时,也要避免与客户端已有监听端口冲突。
{
"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 语法。常见错误是最后一项后面多留逗号、复制了中文引号,或者删除对象时少删了一层括号。本页示例使用标准 JSON,不加入注释。编辑器能打开文件,只能说明文本可读;语法检查通过,也只能说明结构合法。字段是否被内核识别、协议是否支持、目标是否能访问,仍需分别验证。不要把这几个阶段混成一次“配置正常”的判断。
给每次修改留一条简短记录,例如修改了哪个对象、预期改变哪种连接,以及恢复时要回到哪里。一次只调整一个职责层,避免同时换 DNS、路由和传输设置。配置里出现看似重复的字段时,先比较它们的路径:路由对象里的解析策略与出站对象里的解析策略,可能服务于不同阶段。字段同名并不等于作用相同,抄写时应连同父对象一起核对。
02 / inbounds 入站与监听范围
先确认应用怎样进入内核
入站是内核接收连接的入口。先确认应用使用系统代理、独立 SOCKS 设置,还是由客户端的虚拟网络接口接管,再去查对应配置。桌面应用常通过本机 HTTP 或 SOCKS 端口进入内核;Android 的 VPN 模式则由客户端结合系统接口处理流量,不能仅凭 JSON 中出现一个 SOCKS 端口,就认定所有应用都经过这个入口。入口没有收到连接时,调整出口通常不会解决问题。
逐项核对 listen、port 和 protocol。监听地址决定接受哪个网络接口上的请求,端口决定服务入口编号,协议决定入口接收什么格式的数据。浏览器的 HTTP 代理设置不能随意指向只接受 SOCKS 的端口。若客户端界面显示“混合端口”,还要确认这是客户端提供的组合能力,还是内核配置中的独立入口,不能把界面术语直接当成通用协议名称填入 JSON。
| 字段 | 读取重点 | 常见误区 |
|---|---|---|
tag | 给入口取唯一名称,便于路由引用。 | 名称本身不会自动开启某种模式。 |
listen | 指定本机接收连接的地址。 | 绑定全部接口会扩大可连接范围。 |
port | 使用未被占用的本机端口。 | 应用设置与监听值不一致。 |
settings | 按当前入站协议填写内容。 | 把其他协议的字段原样搬入。 |
把本机访问与局域网访问分开
只给当前设备使用时,优先绑定 127.0.0.1。这个地址属于 IPv4 回环接口,其他设备不能通过它访问当前设备的代理。若需要在局域网共享,通常还涉及监听地址、操作系统防火墙、入口认证和设备之间的网络可达性。单独打开“允许来自局域网的连接”,不能保证其他设备已经获得访问权限,也不意味着这些连接会自动进行身份验证。
下面只展示一个 HTTP 入站对象,应作为 inbounds 数组的一项使用。它与前面的 SOCKS 入口采用不同端口,便于在应用设置中明确区分。端口号只是本机约定,不代表远端服务端口。若应用没有发出请求,先查看系统代理是否仍指向旧值,再检查客户端是否真的加载了该对象;不要因为端口数字常见,就假定系统里一定已经存在对应服务。
{
"tag": "local-http",
"listen": "127.0.0.1",
"port": 10809,
"protocol": "http",
"settings": {}
}
理解 UDP 与嗅探的边界
SOCKS 入站的 udp 开关表示允许该入口处理 SOCKS UDP 请求,不等于所有应用的 UDP 都会自动进入内核。应用必须支持相应代理方式,后续出口与网络链路也要允许转发。只设置系统 HTTP 代理时,部分实时通信或游戏流量可能仍走原有路径。遇到这种情况,先确认应用支持的代理方式,再阅读TUN 模式说明,不要反复更换监听端口。
嗅探是根据连接开头的数据识别目标信息的能力,常用于辅助域名路由。它不是解密所有应用内容,也不是 DNS 解析的替代品。某些内核支持的嗅探字段、目标协议和仅供路由使用的选项有所不同,添加前先核对当前实现。开启后若个别应用行为异常,保留其他设置不变,单独关闭嗅探比较结果。判断依据应是具体连接,而不是一次总体测速。
最后用日志确认入口是否成功绑定。地址被占用时,检查是否同时启动了另一份客户端、旧进程是否仍在运行,以及客户端是否已经自动建立同名入口。不要为了绕过冲突立即改成监听全部接口。需要改端口时,同步更新应用中的代理地址;使用回环地址的应用还要注意 IPv4 与 IPv6 的差别,localhost 在不同系统上可能优先解析到不同地址。
03 / outbounds 出站与默认出口
先找最终被选中的出站
出站决定内核接收请求后,怎样把连接送到下一步。常见类型包括直接连接目标、连接远端代理服务,以及主动阻断请求。先查 routing 是否把流量指向特定标签,再查对应出站对象。列表中存在一个远端代理出站,不代表每条连接都会使用它;规则可能提前命中直连出口,也可能因为标签写错而导致加载失败。排查时要把“对象存在”和“对象被使用”分开。
没有匹配路由规则时,常见的默认行为是使用 outbounds 数组的第一项。因此,调整数组顺序可能直接改变未命中规则的流量去向。不要仅为了阅读方便,把直连对象从列表末尾挪到开头。客户端通常会按自己的模式生成顺序,自定义配置时则需要明确设计默认出口。将默认行为写进维护记录,比依靠标签名称猜测更可靠;名为代理的标签也可以指向直连对象。
区分直连、阻断和远端协议
freedom 是常见的直连出站协议,blackhole 用于阻断。两者都不需要远端账号,但它们只在被选中时起作用。阻断连接的表现也未必完全相同:应用可能显示连接失败、等待重试,或改用其他地址继续请求。看到页面最终打开,不能据此认定阻断规则没有生效,还应查看被阻断的是哪一条连接,以及应用是否发起了后续请求。
下面是完整的出站数组片段,可用于本地路由实验。它只包含直连与阻断,不包含任何远端代理能力。tag 供路由规则引用,协议名供内核选择实现,两者不能互换。若在后续章节中使用 outboundTag,应逐字对应这里的标签。大小写、连字符和多余空格都可能造成引用不一致,重命名时需要一起搜索所有引用位置。
{
"outbounds": [
{
"tag": "direct",
"protocol": "freedom",
"settings": {}
},
{
"tag": "block",
"protocol": "blackhole",
"settings": {}
}
]
}
远端代理出站需要按照协议填写服务地址、端口和认证参数。VMess、VLESS 与 Trojan 的认证结构不同,不应把其中一类的用户对象搬给另一类。由订阅生成的配置还可能包含流控、加密、安全层等配套字段。先在客户端确认原始连接资料是否完整,再检查生成后的对象;如果源资料就缺字段,修改路由并不能补齐服务端所需的认证信息。
别把协议名当成全部连接条件
同一代理协议可以搭配不同传输方式和安全层。两份配置都写 VLESS,仍可能因为传输路径、服务名称、TLS 服务器名称或安全参数不同而不能互换。服务端地址负责建立连接,证书验证名称负责核对远端身份,两者有时相同,有时不同。应按服务提供的参数分别填写,不要用一个域名覆盖所有看起来相似的字段,也不要把订阅分组名称写进网络地址位置。
多出口设计还要避免连接递归。如果某个代理出站到远端服务器的连接,又被客户端的全局接管机制送回同一个入口,就可能形成循环。图形客户端通常会处理自身进程、服务器地址或绑定接口等细节,自定义配置不应随意删除这些安排。发现连接数持续增长、日志反复访问同一地址时,先停用新增的链式转发与全局接管设置,恢复到单出口验证。
检查远端不可达问题时,按地址、端口、认证、传输和安全层的顺序定位。能建立 TCP 连接只说明基础链路可达,不能证明代理协议握手成功。延迟测试与实际网页请求也可能使用不同目标。若需要比较指标,参照节点测速数值说明,先确认测量对象再解释数值。不要把一次低延迟当成整份出站配置正确的证明。
04 / 传输参数与安全层
分开核对协议、传输和加密连接
先把三个问题分开:代理协议怎样组织认证与请求,传输方式怎样承载这些数据,安全层怎样验证并保护外层连接。客户端常把这些选项放在同一张编辑页面中,但它们属于不同配置层。代理协议正确而传输错误,仍然无法完成连接;传输能够建立而认证错误,也会在后续被拒绝。排错时要指出失败发生在哪一层,避免把所有失败统一归结为节点不可用。
在常见配置中,streamSettings 负责组织传输与安全相关选项。它可以位于相应入站或出站对象内部,不是放到最外层就能全局生效的通用开关。不同内核对传输名称和专属字段的支持范围不同,同一技术在不同阶段也可能有不同配置写法。保留客户端当前生成的结构,再对照当前内核说明调整,比把旧文章中的对象整体覆盖进去更稳妥。
逐项核对 TLS 名称与验证
TLS 是建立加密连接并验证远端身份的一组机制。先核对安全类型,再核对 serverName 是否与服务提供的参数一致。该名称通常参与服务器名称指示与证书验证,不是随便填写的备注。连接地址使用 IP 时,也不意味着验证名称必须改成同一个 IP。若远端证书为域名签发,随意替换名称可能让原本可达的服务在握手阶段失败。
下面只展示常见的 TLS 设置对象,说明字段层次。example.com 是文档示例域名,必须按真实服务参数设置,不能据此建立代理连接。示例不指定传输类型,因为类型必须与实际服务一致。allowInsecure 保持关闭表示不主动跳过证书验证;不要把关闭验证作为常规修复方式,否则会掩盖名称、证书链或设备时间错误。
{
"streamSettings": {
"security": "tls",
"tlsSettings": {
"serverName": "example.com",
"allowInsecure": false
}
}
}
遇到证书相关报错,先检查设备日期和时区,再核对名称、服务地址与网络环境。系统时间偏差会让证书看起来尚未生效或已经过期。某些网络还可能要求先完成门户登录,导致请求实际到达登录页面而非目标服务。确认这些基础条件后,再查看内核日志中的具体握手错误。只看到浏览器能打开某个站点,并不能证明代理服务的 TLS 参数完全正确。
路径与服务名称必须按传输匹配
使用 WebSocket 时,路径和相关请求头可能参与服务端分流;使用 gRPC 时,服务名称又是另一组参数。它们不能相互替代,也不能根据界面里某个“主机”标签的字面意思自行推导。复制参数时保留路径开头的斜杠与大小写,留意空字符串和缺省字段是否代表不同含义。服务地址、HTTP 主机字段与 TLS 名称有时分别承担职责,不要为了统一外观而强行改成相同值。
REALITY 属于特定内核支持的安全方案,不能因为另一个内核也支持 VLESS,就假定所有相关字段通用。涉及公钥、短标识或流控的配置,应与服务端约定及当前内核实现逐项对应。这里不提供拼装式远端示例,避免把来自不同连接资料的参数组合成看似完整、实际不能工作的对象。使用客户端导入后,先保持参数原样测试,再进行单项调整。
还要区分传输设置与连接复用。复用会改变多条应用连接如何共享底层通道,不会自动修复错误的证书名称、传输路径或认证参数。为排除变量,先在原始连接条件下验证,再决定是否启用兼容的复用方案。某些协议、流控或传输组合对复用有额外限制,不能从其他配置里只复制一个开关。若开启后才出现间歇性中断,应把复用作为独立变量回退测试。
05 / routing 路由规则与命中顺序
从第一条规则开始向下读
路由的任务是为每条连接选择出站。先按数组顺序读取 rules,找到第一条符合条件的规则,再核对它指向的出口。不要把规则列表理解成所有条件都执行一遍的清单。范围很大的规则放在前面,可能让后面的细分规则永远没有机会命中。新增规则时,应先写清需要影响哪类连接,再选择插入位置,而不是习惯性地添加到末尾。
单条规则里的不同条件通常共同限制匹配范围,例如入口标签与目标域名要同时符合;同一个条件数组中的多个候选项通常表示其中任意一项符合即可。具体组合仍应以当前内核实现为准。初次调试时,先使用一个条件验证目标,再逐步增加限制。把入口、端口、协议和域名一次性写满,虽然看起来精确,却会让未命中的原因很难定位。
先使用不依赖额外资源的规则
下面是可合并到第一章本机示例的 routing 对象,但需要同时加入第三章的 block 出站。它把文档示例域名交给阻断出口,把列出的私有地址范围交给直连出口。其他流量仍按原配置的默认出口处理。示例没有依赖外部域名分类文件,便于先理解引用和顺序;它也不是完整的局域网地址清单,不应直接当成生产分流方案。
{
"routing": {
"domainStrategy": "AsIs",
"rules": [
{
"type": "field",
"domain": ["full:example.com"],
"outboundTag": "block"
},
{
"type": "field",
"ip": [
"10.0.0.0/8",
"172.16.0.0/12",
"192.168.0.0/16"
],
"outboundTag": "direct"
}
]
}
}
域名匹配应明确选择方式。full: 表示完整域名匹配,domain: 常用于匹配该域名及其子域名,正则表达式则适合更特殊的模式。普通字符串的匹配含义与前缀形式不同,写规则前应确认预期。需要控制一个站点时,还要考虑其静态资源、登录和接口可能位于其他域名;只匹配首页域名,未必涵盖该应用实际建立的所有连接。
区分路由解析与最终连接解析
domainStrategy 决定路由阶段怎样对待目标域名。常见的 AsIs 不为了 IP 规则主动解析域名;IPIfNonMatch 会在域名规则没有匹配时尝试解析,再给 IP 条件匹配机会;IPOnDemand 会在匹配过程遇到需要 IP 的规则时尝试解析。它们控制的是路由判定,不是全局 DNS 出口开关,也不直接决定所有应用最终使用哪种解析方式。
选用更积极的解析策略前,先判断是否真的需要按目标 IP 分流。额外解析会引入等待、缓存和返回地址变化等因素。同一域名可能同时返回多个地址,内核如何据此匹配也应结合实际行为确认。若目标本来就是 IP,域名规则通常无法凭空恢复原始域名;嗅探可能为部分协议补充信息,但不是对所有连接都有效,更不能保证识别所有加密应用流量。
geosite 与 geoip 是分类数据的引用方式,不是写进字符串后就自动获得的在线查询。规则是否可用,取决于内核支持、资源文件是否存在、分类名称是否正确,以及客户端怎样管理这些资源。出现分类加载失败时,先恢复为明确的域名或地址规则验证。不要把一份环境中的分类标签照搬到另一份环境,却忽略其所依赖的数据来源与文件位置。
调整路由后,用目标明确的单个请求验证。清理旧连接,观察新连接使用哪个出口,再测试相关应用的其他域名。现有长连接可能继续沿用原来的通道,导致界面已改但表现暂时不变。遇到绕行或误阻断,按“入口标签、目标信息、首条命中规则、出口标签”的顺序检查。更一般的使用疑问可到常见问题查阅,不必先重置全部配置。
06 / dns 解析器与分流边界
先问清域名由谁解析
DNS 是把域名转换成网络地址的查询机制。先确认应用把域名还是已经解析好的 IP 交给代理,再检查内核 DNS。浏览器、操作系统、内核和远端服务都可能参与解析,配置中存在 dns 对象,并不意味着设备上每次查询都会经过它。应用使用独立的加密解析时,也可能绕开系统的默认解析路径,因此需要把应用层设置与内核配置一起核对。
内核内置解析器通常服务于需要解析域名的内部功能,例如按 IP 路由或某些直连出站行为。远端代理接收域名后,也可能在远端完成最终解析。由此产生的两次解析不一定使用相同服务器,结果也可能不同。排查时写下查询发生的位置、查询的域名和返回地址,不要只根据系统自带查询工具的结果,推断内核已经得到相同答案。
按查询用途选择服务器
下面展示一个 DNS 对象片段:对文档示例域名优先使用指定解析器,其余查询可使用另一台解析器。示例里的地址是公开解析服务地址,使用前应确认当前网络可以访问,并理解这些服务会收到相应查询。该片段用于说明 servers 的对象与字符串写法,不代表适合所有地区和所有网络,也没有承诺查询一定经由代理出口发送。
{
"dns": {
"servers": [
{
"address": "223.5.5.5",
"domains": ["domain:example.com"],
"skipFallback": true
},
"1.1.1.1"
],
"queryStrategy": "UseIP"
}
}
domains 用于给服务器设置优先处理的域名范围,不等于永远只会使用这一台服务器。失败后的回退行为还受全局回退设置与单个服务器选项影响。skipFallback 常用于避免该服务器参与不相关域名的回退查询,而不是给它匹配的域名提供绝对排他的保证。需要严格隔离解析路径时,应同时核对当前内核的回退语义,并在日志或可观察的查询记录中验证。
理解返回地址过滤与地址族
expectIPs 用于约束预期的响应地址范围。它筛选的是解析结果,不是把连接自动送到某个出口。预期范围写得太窄,可能拒绝正常的内容分发地址;写成依赖分类资源的形式时,还要确保资源确实存在。解析失败也可能不是服务器没有响应,而是响应不符合筛选条件。先移除筛选进行对照,再决定是否需要更精确的范围,不要直接判定目标域名失效。
queryStrategy 控制查询地址族的策略,常见形式包括同时考虑 IPv4 与 IPv6,或只查询其中一种。设备有 IPv6 地址,不代表到所有目标的 IPv6 路径都可用;反过来,只查询 IPv4 也可能隐藏某些网络环境中的实际能力。选择前先测当前链路,再确认出口支持。不要为了消除一次偶发超时,就永久关闭整个地址族而不记录原因。
hosts 可在内核解析器中提供静态映射,但不是对系统所有应用生效的全局主机表。映射会绕过正常解析更新,适合受控测试或明确的本地需求,不适合长期固定变化频繁的公共服务地址。填写后还要考虑 TLS 仍按什么名称验证,以及路由是否根据映射后的地址重新匹配。静态地址能连接,并不能证明原来的解析服务已经恢复。
加密 DNS 需要同时考虑引导解析与连接路径。解析服务自身使用域名时,先要解决怎样找到该服务地址的问题;如果这一步又依赖同一服务,就可能形成启动循环。不同内核对查询协议、直发方式和路由接入的语法有所差别,不要仅替换一个地址前缀就认定完成分流。先用简单解析建立基线,再分别验证解析服务可达性与目标域名结果。
最后清理缓存再比较改动。应用缓存、系统缓存、内核缓存以及仍在使用的连接,都可能保留旧结果。重启一个组件不一定清掉其他组件的状态。建议固定一个测试域名,记录修改前后的地址族、返回地址和实际出口;需要国内外域名分组解析的进一步说明时,阅读DNS 分流解析设置笔记,不要把解析分组直接等同于连接分流。
07 / policy 策略与运行状态
先保留默认值,再解释超时
policy 用于控制部分连接生命周期与统计行为,不是自动提速模块。先保留默认策略运行,只有在日志和复现步骤能指向某个时间条件时,再考虑调整。网络慢、服务器响应迟缓、应用暂停发送,都可能表现为等待,但对应的原因不同。把全部超时一起调大,可能只是让失败更晚出现;全部调小,则可能让原本可以维持的空闲连接过早结束。
策略常按用户等级组织。levels 对象中的键表示等级编号,对应协议用户的等级设置;它不是节点排序,也不是客户端窗口中的分组编号。未显式指定时,常见场景使用默认等级。若配置中存在多个用户或不同入口,先确认具体连接实际归属哪个等级,再判断自定义策略是否会应用。只增加一个没有被引用的等级对象,通常不会改变正在观察的连接。
按连接阶段理解时间字段
下面是一段策略对象示例,时间字段以秒为单位,缓冲字段以千字节为单位。这些值用于展示字段关系,不是针对所有设备的优化建议。某些字段的适用范围与内核实现有关,尤其涉及不同协议和缓冲管理时,应以当前内核行为为准。将片段合入配置后,先确认启动成功,再用相同应用和相同操作过程比较,不要同时更换出口。
{
"policy": {
"levels": {
"0": {
"handshake": 4,
"connIdle": 300,
"uplinkOnly": 2,
"downlinkOnly": 5,
"bufferSize": 64
}
},
"system": {
"statsInboundUplink": false,
"statsInboundDownlink": false,
"statsOutboundUplink": false,
"statsOutboundDownlink": false
}
}
}
handshake 限制相应连接握手阶段允许等待的时间。它不是网页完整加载时间,也不负责约束应用下载一个文件需要多久。若失败总发生在连接建立之前,先排查地址、端口和传输协商,再判断是否真的需要更长的握手等待。基础参数错误时,延长等待只会让用户更久才看到失败,不能把不匹配的协议变成可用连接。
connIdle 描述连接空闲状态的时间限制。推送、聊天或远程会话可能长时间没有明显数据交换,过短的空闲限制会让应用不断重连。反之,过长的限制也会让失去用途的连接保留更久。需要区分应用层心跳、底层网络保活与内核策略:它们处于不同层次,某一层设置了保活,不代表其他层一定会认定连接仍然活跃。
uplinkOnly 与 downlinkOnly 关注连接一侧结束后,另一方向还能继续维持多久。它们不是上传限速和下载限速。某些应用会先结束请求发送,再等待较长的响应,过于激进的单向等待可能造成响应被截断。遇到“请求已经发出但结果中断”,应结合日志时间线判断,而不是看到字段里有上行、下行就把数值理解成带宽。
缓冲与统计都需要配套理解
bufferSize 影响相关连接的缓冲配置。增大缓冲不等于直接增加线路带宽,多连接场景下还可能增加内存压力。尤其在 Android 设备上,进程被系统终止、后台运行受限与缓冲设置过大可能产生相似的表面现象,不能只看最后一次断连。先观察系统资源和客户端状态,再考虑是否需要调整;不要把不明确的性能建议当成所有设备通用的固定值。
统计开关也不是打开后就会自动生成可见报表。内核统计通常还需要相应统计模块、查询接口与客户端读取逻辑配合,客户端界面显示的流量也可能来自其他计数方式。启用前确认是否有实际消费这些数据的组件,不要为了让配置看起来完整而加入不需要的接口。统计结果只能说明相应计数口径下的数据量,不能据此推断实际吞吐、响应时间或远端服务质量。
修改策略后,应同时记录启动日志、应用行为和发生时间。若客户端整个进程消失,优先检查进程退出与系统限制,而不是继续放宽连接超时。桌面端运行环境、解压路径和配置读取错误,Android 端权限与后台限制,都属于另一条排查链;可结合启动闪退排查区分。连接策略只作用于运行中的连接,不能修复一个尚未正常启动的内核。
08 / 配置验证与故障定位顺序
把启动成功与访问成功分开验证
先保存修改,再让客户端重新加载相应配置。确认界面仍选中预期配置,并检查日志中的实际内核是否与计划一致。客户端显示连接按钮处于启动状态,只能说明某个启动流程已经执行,不能替代对监听、路由和远端访问的验证。自定义文件存在于磁盘,也不代表它就是当前运行文件。若行为与内容完全不符,先回到配置来源排查,避免继续修改一份未被读取的文件。
第一步检查加载。语法错误、字段类型错误、未知协议和分类资源缺失,通常发生在开始处理业务流量之前。把日志中第一条相关错误与最近修改对应起来,而不是只看最后出现的进程退出提示。修复一个结构错误后可能暴露下一个错误,这并不意味着修复无效。每次只处理当前明确的问题,确认启动阶段通过后,再进入连接测试。
沿着请求路径逐段缩小范围
第二步检查入口。让一个明确支持代理设置的应用发送请求,核对地址、端口和协议,并观察内核是否接收到连接。没有入口记录时,先检查应用自己的代理选项和系统代理范围。部分程序只在启动时读取系统设置,需要关闭旧连接或重新启动应用;其他程序则完全不使用系统代理。不要在尚未确认请求进入内核时,就把失败归因于出口认证。
第三步检查路由与解析。先确认目标以域名还是 IP 形式进入,再按顺序找可能命中的规则,最后核对出口标签。如果日志显示的目标信息不够,可在受控测试期间临时提高日志详细程度,但测试后应恢复。详细日志可能包含访问域名、连接地址和本机路径,分享前需要删去与问题无关的信息。日志级别影响可观察性,不会自动改变路由结果。
| 观察到的现象 | 先检查 | 下一步 |
|---|---|---|
| 内核启动后立即退出 | JSON 语法、字段类型、资源与端口。 | 恢复最近一次改动,重看首条错误。 |
| 应用请求没有入口记录 | 应用代理范围、地址、协议与端口。 | 使用单个明确支持代理的应用测试。 |
| 请求使用了意外出口 | 规则顺序、条件与标签引用。 | 清理旧连接,观察新请求。 |
| 基础连接可达但握手失败 | 认证、传输路径与安全参数。 | 恢复订阅原始参数逐项比对。 |
| 只有部分域名失败 | 解析结果、资源域名与分流范围。 | 固定目标,对比地址族和出口。 |
第四步检查出口握手。区分连接被拒绝、等待超时、证书验证失败和用户认证失败,它们指向的环节不同。被拒绝通常先查目标端口与服务状态;超时要考虑路径和防火墙;证书错误先查名称与时间;认证错误则核对连接资料。不要把这些错误全部用更换 DNS 处理。更详细的逐条说明见运行日志解读。
使用受控回退,而不是连续叠加补丁
准备两份记录清楚的配置:一份保留正常基线,一份只加入当前要验证的改动。测试时使用同一个应用、同一个目标和相同的连接方式,避免一边换服务器、一边换路由,再根据结果猜测哪个变量起作用。若新增规则导致部分连接异常,先撤回该规则确认是否恢复;恢复后再缩小规则条件,比继续增加更多例外更容易找出根因。
合并对象时也要注意数组不是普通文字段落。把两个完整的 routing 对象直接并排粘贴,会造成重复键或结构错误;把第二份 outbounds 覆盖第一份,则可能删除仍被路由引用的出口。正确做法是先明确保留哪个父对象,再把需要的数组项逐项合并,检查标签唯一性、引用关系和默认出口顺序。重复键即使被某些解析器接受,也不应依赖其覆盖行为。
完成验证后,把稳定设置迁移回可持久保存的位置,并再次测试客户端重启、切换配置与更新订阅之后的结果。如果重启就恢复旧值,说明改动仍停留在自动生成文件中,应回到客户端支持的设置入口。保存最终配置时附上简短说明:使用哪个内核、修改了哪些层、依赖哪些分类资源,以及恢复方法。无需记录不相关的设备细节,更不要把认证资料写进公开笔记。