JSON 顶层结构与配置读取顺序
先确认数据类型、标签引用和处理链,再讨论单个协议参数。
顶层对象不是执行步骤清单
V2Ray 配置文件以一个 JSON 对象作为根节点,常见顶层字段包括 log、dns、inbounds、outbounds、routing、policy 和 stats。这些字段写在文件中的先后位置不会改变处理顺序。内核加载配置时先解析整个对象,再建立监听入口、出站对象、路由器和名称解析器。把 routing 写在 inbounds 前面不会让路由先于入站执行;真正决定关联关系的是标签和规则引用。
inbounds 与 outbounds 都是数组,因为一个进程可以同时监听多个本地端口,也可以保留多个出口。数组中的每个对象通常带有 tag。路由规则通过 inboundTag、outboundTag 或 balancerTag 引用这些标签。标签应当在同一配置内保持唯一,并使用短而稳定的名称,例如 socks-in、proxy、direct 和 block。修改标签后必须同步检查全部引用,否则配置可能能够解析,却在建立路由时提示目标不存在。
一份可读的最小结构
{
"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"
},
{
"tag": "block",
"protocol": "blackhole"
}
],
"routing": {
"domainStrategy": "AsIs",
"rules": [
{
"type": "field",
"ip": ["geoip:private"],
"outboundTag": "direct"
}
]
}
}
这份示例只建立本地 SOCKS 入口和两个基础出口,没有配置远程代理连接。它适合用来观察结构层级:协议专属参数放在对应对象的 settings 中,传输层参数通常放在 streamSettings 中,连接复用等扩展能力则有自己的子对象。不要把另一个层级的字段平移到 settings,因为 JSON 语法即使正确,内核也可能忽略不属于该对象的字段,或者直接报告反序列化错误。
对象、数组与数据类型
配置错误经常不是协议问题,而是 JSON 数据类型不匹配。端口通常是数字,因此写作 10808,不应随意写成字符串 "10808";布尔开关使用 true 或 false,不能写成引号包围的文字。规则中的域名和 IP 条件大多采用数组,即使只有一项也应保留方括号。对象属性之间需要逗号,最后一个属性后不能保留多余逗号。中文全角引号、不可见空格和从富文本复制来的弯引号,也会让解析器在看似正常的位置报错。
客户端生成配置时往往会把订阅节点、路由预设和本地端口合并为最终文件。v2rayN 适合在 Windows、macOS 与 Linux 上查看和调整这类桌面配置;Android 上的 v2rayNG 与 v2flyNG 通常由界面保存设置,再在启动时生成运行配置。界面里的名称未必与底层字段逐字一致,例如“绕过局域网”最终可能表现为一条 geoip:private 直连规则。需要核对时,应以客户端实际导出的运行配置和日志为准,而不是把界面截图当作完整配置。
建立可维护的阅读方法
阅读较长配置时,可以先列出所有入站和出站标签,再沿路由规则逐条追踪目标标签,最后检查 DNS 与传输参数。不要从某个深层字段开始猜测整个连接行为。一个请求先进入某个入站,携带目标域名、目标 IP、端口和可能的协议识别结果;路由器根据条件选出出站;出站再按照协议和传输设置连接目标。DNS 可能在规则匹配或出站连接阶段参与其中,policy 则影响连接统计、空闲超时和会话行为。按这条链路阅读,能够把“配置能加载”和“连接能完成”区分开。
inbounds 入站:本地监听与流量入口
入站决定哪些应用能够接入、使用什么本地协议,以及请求带着哪些识别信息进入路由器。
listen、port 与暴露范围
入站对象最先需要确认的是 listen 与 port。桌面客户端为本机应用提供代理时,通常监听 127.0.0.1,这表示只有当前设备能够连接。将监听地址改为覆盖外部网卡的地址,会改变入口可达范围,并要求同时考虑本地网络边界、系统防火墙和身份验证。仅为浏览器、终端或本机软件提供代理时,没有必要扩大监听范围。
端口必须没有被其他进程占用。同一配置中的两个入站也不能在相同地址上监听同一个端口。常见客户端会分别创建 SOCKS 与 HTTP 入站,或者使用支持混合入口的实现。系统代理一般使用 HTTP 入口,而明确支持 SOCKS 的应用可以直接填写 SOCKS 端口。应用把 HTTP 请求发到 SOCKS 端口时,日志可能出现无法识别握手、连接被关闭或请求格式错误;这类情况不是远程节点故障,而是本地入口协议填错。
SOCKS 入站与 UDP
{
"tag": "socks-in",
"listen": "127.0.0.1",
"port": 10808,
"protocol": "socks",
"settings": {
"auth": "noauth",
"udp": true,
"ip": "127.0.0.1"
},
"sniffing": {
"enabled": true,
"destOverride": ["http", "tls"],
"routeOnly": true
}
}
auth 控制 SOCKS 入口的认证方式;仅监听回环地址时,经常使用 noauth。udp 决定是否接收 SOCKS UDP 请求。打开这个开关不代表所有应用都会自动把 UDP 交给代理,应用本身还需要支持 SOCKS UDP,远端协议与传输链也要能够处理对应流量。ip 用于 UDP 关联返回地址时,应与实际可达方式相符。本机入口填回环地址较直观;如果入口面向其他设备,则需要重新检查该地址能否被请求方访问。
HTTP 入站与系统代理
{
"tag": "http-in",
"listen": "127.0.0.1",
"port": 10809,
"protocol": "http",
"settings": {
"allowTransparent": false
},
"sniffing": {
"enabled": true,
"destOverride": ["http", "tls"],
"routeOnly": true
}
}
HTTP 入站处理普通代理请求和 CONNECT 隧道请求,适合浏览器及遵循系统代理设置的软件。它与网站服务器使用的 HTTP 服务不是一回事:应用需要明确知道这里是代理端口。终端命令、开发工具和后台服务不一定读取桌面系统代理,因此浏览器可以连接并不能证明所有程序都会经过该入站。遇到“网页正常但命令行直连”的情况,应先检查目标程序的代理选项或环境变量,再检查 V2Ray 配置。
sniffing 的作用与边界
流量探测用于从部分连接中恢复目标域名,从而让域名路由规则有机会生效。以 TLS 连接为例,应用可能先在本地完成域名解析,再向一个 IP 地址发起连接;如果入站只看到 IP,单纯的域名规则可能无法匹配。启用 sniffing 后,内核可从握手信息识别目标域名。destOverride 指定允许识别的协议类型,routeOnly 表示识别结果主要用于路由判断,而不强制替换最终连接目标。
探测不是通用解密,也不能保证每条连接都有可识别域名。非标准协议、加密握手变化、应用自行封装或直接访问 IP 时,都可能只保留 IP 条件。配置路由时应同时考虑域名规则和 IP 规则,而不是把所有判断都寄托在探测结果上。若某个应用启用探测后表现异常,可以对特定入站关闭探测,或者拆分为独立入站标签,让路由规则针对入口分别处理。
多个入站如何协作
多个入站最有价值的用途是区分流量来源。例如为浏览器保留 http-in,为开发工具保留 socks-in,再通过 inboundTag 给两类入口设置不同路由。这样比只按域名猜测应用来源更稳定。每个入口都应有清晰标签,端口也应在客户端界面、系统代理和应用设置之间保持一致。修改 v2rayN 的本地端口后,旧的系统代理值或终端环境变量不会必然同步更新;排查时要同时核对监听日志和应用侧配置。
| 入口类型 | 常见用途 | 优先检查项 |
|---|---|---|
| SOCKS | 支持 SOCKS 的浏览器、终端与开发工具 | 协议类型、端口、UDP 支持 |
| HTTP | 系统代理与支持 HTTP 代理的软件 | CONNECT 支持、系统代理端口 |
| 独立入口 | 按应用类别实施不同路由 | 标签唯一性与 inboundTag 规则 |
outbounds 出站:协议、服务器与传输层
出站描述请求最终从哪里离开,以及连接远端时采用的协议、身份参数和传输方式。
出站标签与三类基本去向
一份实用配置通常至少包含代理、直连和阻断三种出站。代理出站连接订阅或手动配置中的远程服务器;freedom 出站直接访问目标;blackhole 出站终止匹配流量。它们分别可以使用 proxy、direct、block 等标签。标签本身没有特殊含义,真正的行为由 protocol 决定,但稳定命名有助于阅读路由规则。
数组顺序可能影响没有命中路由规则时使用的默认出口。为了避免依赖模糊默认值,建议将主要代理出站放在清晰位置,并为局域网、阻断列表和需要直连的目标写出明确规则。客户端可能在生成配置时加入额外出站,例如 DNS 专用出口或链式代理中间出口。手动编辑前要先看清这些标签是否被其他对象引用,不要仅凭名称删除。
VLESS 出站示例
{
"tag": "proxy",
"protocol": "vless",
"settings": {
"vnext": [
{
"address": "server.example.com",
"port": 443,
"users": [
{
"id": "00000000-0000-4000-8000-000000000000",
"encryption": "none",
"flow": ""
}
]
}
]
},
"streamSettings": {
"network": "tcp",
"security": "tls",
"tlsSettings": {
"serverName": "server.example.com",
"allowInsecure": false
}
}
}
address 是远端服务器地址,既可以是域名,也可以是 IP;port 是远端监听端口。users 中的 id 是身份标识,必须与服务端配置一致。VLESS 的 encryption 常按协议要求填写指定值,它不等同于传输层的 TLS。flow 只有在服务端明确启用对应流控方式时才填写,不能根据其他节点的配置随意复制。
streamSettings 描述连接如何承载协议数据。network 必须与服务端一致;security 决定是否启用 TLS 或其他安全层;serverName 用于证书名称与握手目标。远端地址、握手名称和实际证书名称可能承担不同职责,不能只因为它们经常相同,就认为任何时候都可以互换。allowInsecure 控制证书验证行为,正常配置应保留验证。证书名称不匹配时,应检查节点参数、系统时间、服务器名称和中间网络,而不是直接关闭验证掩盖问题。
REALITY 连接参数如何对应
{
"streamSettings": {
"network": "tcp",
"security": "reality",
"realitySettings": {
"serverName": "www.example.com",
"fingerprint": "chrome",
"publicKey": "example-public-key",
"shortId": "0123456789abcdef",
"spiderX": "/"
}
}
}
REALITY 配置通常位于 realitySettings。serverName、publicKey、shortId 和服务端设置之间存在严格对应关系,任何一项抄错都可能在握手阶段失败。fingerprint 表示客户端握手指纹选项,应使用内核与服务端方案支持的值。示例中的公钥只是字段格式说明,不能用于实际连接。
遇到 REALITY 节点无法连接时,应先区分网络不可达、时间异常、参数不匹配和路由误分流。先确认远端地址与端口可达,再核对服务器名称、公钥、短标识和流控参数,最后检查目标服务器地址是否被某条直连或阻断规则错误处理。只盯着协议名称反复切换设置,通常无法定位真正问题。
直连、阻断与 DNS 专用出站
[
{
"tag": "direct",
"protocol": "freedom",
"settings": {
"domainStrategy": "UseIP"
}
},
{
"tag": "block",
"protocol": "blackhole",
"settings": {
"response": {
"type": "none"
}
}
}
]
freedom 表示由当前设备直接连接目标。其 domainStrategy 决定出站接到域名时是否以及如何解析,但它与顶层 routing.domainStrategy 不是同一个控制点:前者作用于直连出站建立连接的阶段,后者作用于路由匹配阶段。blackhole 用于终止命中的连接,适合明确的阻断规则。阻断后应用通常只看到连接失败,因此必须通过路由日志确认是否命中了 block。
订阅节点与手写配置的边界
v2rayN、v2rayNG 和 v2flyNG 会把订阅内容转化为各自支持的节点模型,再生成底层出站配置。订阅更新可能覆盖节点协议参数,因此不宜直接在自动生成文件里长期维护服务器字段。需要调整本地路由、DNS 或入站时,优先使用客户端提供的自定义配置、路由预设或覆写入口。若客户端不支持某个字段,应先确认所用内核家族是否认识该字段,再决定是否使用完整自定义配置。
桌面场景首推 v2rayN,是因为它同时提供节点管理、路由设置和运行日志入口,便于对照本章字段。安装包应从安装包页面按平台选择。Android 场景可在 v2rayNG 与 v2flyNG 之间按内核需求选择,两者生成配置的细节可能不同,导入同一订阅后也应以各自运行日志为准。
routing 路由:规则匹配与分流顺序
路由不改变协议本身,它根据请求特征,把流量交给已经定义好的出站。
规则从上到下匹配
routing.rules 是有顺序的数组。请求到达路由器后,规则通常从上到下检查,先匹配到的规则决定目标出站。因此,范围较小、意图明确的规则应放在前面,覆盖范围大的规则放在后面。例如局域网直连应在广泛的代理规则之前,特定域名阻断应在通用域名代理规则之前。若把所有端口代理规则放在首位,后面的局域网直连规则就没有机会生效。
规则的 type 通常使用 field,随后通过 domain、ip、port、network、protocol、inboundTag 等条件描述目标。一个规则同时包含多种条件时,需要满足这些条件组合后才会命中;同一字段中的多个值通常表示其中任意一个匹配。把彼此无关的条件塞进同一个规则,容易造成规则比预期更窄。更清晰的方法是拆成多条规则,并为每条规则保持单一目的。
常用的基础分流结构
{
"routing": {
"domainStrategy": "IPIfNonMatch",
"domainMatcher": "hybrid",
"rules": [
{
"type": "field",
"ip": ["geoip:private"],
"outboundTag": "direct"
},
{
"type": "field",
"domain": ["geosite:private"],
"outboundTag": "direct"
},
{
"type": "field",
"protocol": ["bittorrent"],
"outboundTag": "direct"
},
{
"type": "field",
"domain": [
"domain:example.net",
"full:status.example.org"
],
"outboundTag": "proxy"
}
]
}
}
geoip:private 用于匹配常见私有地址范围,适合避免访问路由器、打印设备和局域网服务时绕到远端。geosite:private 处理相应的私有域名集合。GeoIP 与 GeoSite 是不同的数据来源:前者按 IP 范围分类,后者按域名集合分类。数据文件过旧可能导致分类结果落后,更新方法可参阅GeoIP 与 GeoSite 数据文件更新方法。
domain: 匹配指定域名及其子域名,full: 只匹配完整主机名,keyword: 根据关键字匹配。精确服务应优先使用 full:,一个站点及其子域名可使用 domain:。关键字范围较宽,容易意外命中名称相似但无关的域名,适合范围明确且经过日志验证的场景。规则中的普通字符串在不同内核配置语境下可能有默认解释,手写配置时使用前缀能够减少歧义。
domainStrategy 如何影响域名与 IP 规则
AsIs 表示路由阶段尽量按原始目标处理。请求以域名进入时,域名规则可以参与匹配;请求只带 IP 时,域名规则通常无法凭空恢复名称。IPIfNonMatch 表示域名规则没有匹配时,再解析域名并尝试 IP 规则。IPOnDemand 则可能在遇到需要 IP 的规则时更早触发解析。不同策略会影响 DNS 查询时机、路由命中与连接耗时,不应只根据名称选择。
如果希望域名集合优先、IP 地理规则作为补充,IPIfNonMatch 通常更容易理解:先检查域名,未命中时解析并继续检查 IP。若配置大量 IP 规则且需要尽早获得地址,可以评估 IPOnDemand。若完全不希望路由器为匹配规则主动解析,则使用 AsIs,同时确保入站探测或应用请求能够提供足够的目标信息。
按端口、网络与入站标签分流
{
"type": "field",
"inboundTag": ["socks-in"],
"network": "tcp",
"port": "443",
"outboundTag": "proxy"
}
端口可以写单个值或范围,例如 80,443、10000-20000。端口只描述目标服务端口,不能代表具体网站或应用;大量服务共享 443 端口,因此只按端口分流通常范围很大。network 可用于区分 TCP 与 UDP,inboundTag 则根据请求进入的本地入口分流。为不同软件建立独立入口,再用入站标签处理,往往比猜测进程名称更通用。
规则未命中与错误命中
没有规则命中时,请求会走默认出站。默认行为必须通过配置结构和内核实现确认,不要假设一定直连或一定代理。排错时先打开适当级别的运行日志,记录请求目标、入站标签和最终出站标签。若规则未命中,检查请求携带的是域名还是 IP、域名探测是否生效、Geo 数据是否存在、规则前缀是否正确。若错误命中,则从数组顶部向下寻找第一条能够覆盖该目标的规则。
路由调整后应使用少量明确目标分别验证:一个局域网地址、一个计划直连的域名、一个计划代理的域名和一个阻断目标。不要用“多数网页能打开”作为分流正确的依据,因为默认出口可能掩盖规则错误。节点测速也只能说明特定测试请求的连接结果,不能替代逐条验证。有关系统代理与终端走向不同的问题,可继续阅读浏览器与命令行终端分开排查。
dns 配置:解析路径、服务器选择与路由协作
DNS 不只是服务器地址列表,它还关系到域名规则、IP 规则和最终出站连接。
先区分系统解析与内置解析
应用发起连接时,域名可能由应用或操作系统先解析,也可能以域名形式交给本地代理。前一种情况下,入站看到的目标可能已经是 IP;后一种情况下,V2Ray 的内置 DNS 与路由策略才有机会直接参与解析。启用 TUN 模式时,更多系统流量可能进入客户端处理链,但具体请求是否由内置 DNS 处理,仍取决于客户端生成的入口、DNS 劫持和路由配置。
因此,设置了 dns.servers 并不意味着设备上所有名称查询都会自动使用它。浏览器自身的安全 DNS、应用内置解析器、系统缓存与终端工具都可能形成独立路径。排查解析异常时,要先回答三个问题:查询由谁发起、发送到哪个服务器、得到的地址最终是否被路由规则采用。只更换 DNS 服务器而不确认路径,容易出现配置改了但现象没有变化。
基础 DNS 对象示例
{
"dns": {
"hosts": {
"domain:internal.example": "192.0.2.10"
},
"servers": [
{
"address": "1.1.1.1",
"domains": ["geosite:geolocation-!cn"],
"skipFallback": true
},
{
"address": "223.5.5.5",
"domains": ["geosite:cn"],
"expectIPs": ["geoip:cn"]
},
"localhost"
],
"queryStrategy": "UseIP"
}
}
hosts 提供静态域名映射,适合受控测试和固定内部地址,不适合代替持续变化的公共解析。示例地址只用于说明结构。servers 可以包含简单地址,也可以使用对象形式附加域名范围、预期 IP 范围和回退控制。列表顺序与匹配条件共同决定查询去向。配置多个服务器不是简单地让所有服务器同时竞争;具体行为会受域名条件、回退逻辑和内核实现影响。
domains 用于指定某个服务器优先处理的域名集合,语法与路由域名规则相近。expectIPs 用于检查返回地址是否符合预期分类,它不是地址过滤器的通用替代。若返回结果不满足条件,解析器可能尝试其他服务器。数据文件缺失或过期时,基于 Geo 分类的预期判断也会失去准确性。skipFallback 会影响该服务器是否参与回退,设置前要理解当前服务器分组,而不是对每项都统一开启。
queryStrategy 与地址族选择
queryStrategy 控制查询使用的地址族策略。常见含义包括同时考虑可用地址、只使用 IPv4 或只使用 IPv6,具体可用值应与当前内核支持保持一致。地址族选择必须符合设备网络条件:如果本地网络没有可用的 IPv6 路径,却强制只查询 IPv6,域名本身可能解析成功,但后续连接仍会失败;反过来,网络只在特定地址族上可达时,也不能简单依赖默认结果。
双栈环境中的失败还可能来自“解析得到多个地址,但应用或出站优先尝试了不可达地址”。这时需要同时观察 DNS 返回、出站选择和连接日志。不要把每次超时都归因于 DNS;解析日志已经给出地址而连接阶段超时,说明问题已经进入出站网络或远端服务阶段。相反,日志显示域名查询失败、没有可用记录或所有预期条件都不满足,才应集中检查 DNS 段。
DNS 查询走哪个出站
DNS 服务器本身也是一个连接目标,它可能被路由规则交给直连或代理出站。如果 DNS 服务器使用域名表示,还可能产生“解析 DNS 服务器域名所需的前置解析”问题。为降低循环依赖,基础 DNS 服务器地址常使用可直接连接的 IP,或者通过明确的专用出站和路由规则处理。使用基于 HTTPS 的解析端点时,还要考虑端点域名、证书名称和它自身的解析路径。
可以为 DNS 查询设置独立出站标签,再通过协议条件或入站标签路由。但这种设计应保持路径清楚:请求进入内置 DNS,DNS 查询经指定出站到服务器,结果返回路由器用于匹配,最终业务流量再走业务出站。若把 DNS 查询错误地送入一个依赖同一 DNS 结果才能连接的代理出站,就可能形成启动阶段的循环等待。客户端预设通常会处理基本依赖,手动覆写时不要删除不熟悉的 DNS 路由。
缓存、Fake DNS 与 TUN 场景
系统缓存、应用缓存和内置解析缓存可能同时存在。修改 DNS 后立即重复访问同一域名,旧结果不一定马上消失。验证时可以重启目标应用与客户端,或使用一个尚未查询的新域名观察完整过程。TUN 模式可能使用 Fake DNS 把域名映射为保留地址,再在流量进入时还原域名,以便进行域名路由。这类保留地址不应被当成真实远端 IP,也不应直接拿去判断 GeoIP 分类。
Fake DNS 适合解决只向系统提交 IP 流量时缺少域名信息的问题,但它要求 DNS 接管、映射表和 TUN 入站保持一致。出现应用获得地址却无法连接时,应检查映射是否被同一运行实例识别、请求是否绕过了 TUN,以及保留地址是否被其他规则提前直连。关于 TUN 的系统接管逻辑和客户端入口,可参阅TUN 模式全局接管流量原理详解。
policy 策略:会话超时、统计与系统级控制
policy 不负责选择节点,而是为连接生命周期和统计信息设定运行边界。
levels 与用户等级引用
policy.levels 以用户等级为键,为对应等级的连接设置策略。部分入站或出站协议的用户对象可以带有 level,运行时会查找同名策略。等级不是速度评分,也不是权限高低的自动排序;它只是一个用于引用策略集合的数字键。若用户没有明确设置等级,通常使用默认等级,但具体默认行为应结合当前配置和内核实现确认。
同一等级可以统一控制握手时间、连接空闲时间、上下行统计等项目。不要为了“看起来完整”而创建大量没有引用的等级。对于客户端生成的单用户出站,通常只需理解默认等级是否启用了统计或改动了超时。服务端多用户场景会更频繁地使用等级区分,但本页重点是客户端配置阅读,因此更应关注策略是否意外缩短了正常连接。
策略对象示例
{
"policy": {
"levels": {
"0": {
"handshake": 4,
"connIdle": 300,
"uplinkOnly": 2,
"downlinkOnly": 5,
"statsUserUplink": false,
"statsUserDownlink": false,
"bufferSize": 4
}
},
"system": {
"statsInboundUplink": true,
"statsInboundDownlink": true,
"statsOutboundUplink": true,
"statsOutboundDownlink": true
}
},
"stats": {}
}
handshake 用于限制建立连接阶段可等待的时间。设得过短时,网络波动、名称解析或远端握手稍慢就可能被提前终止;设得过长则会让不可达连接更久才反馈失败。connIdle 描述连接在没有数据传输时可保持的时间。长连接应用、消息推送、远程终端和持续传输工具对空闲时间更敏感,不能只根据网页浏览体验调整。
uplinkOnly 与 downlinkOnly 处理只剩单方向数据后的连接保留时间。它们用于连接生命周期管理,不代表带宽限制。bufferSize 影响每个连接的数据缓冲策略,盲目增大可能提高内存占用,盲目减小也可能影响吞吐。大多数客户端用户应保持内核或客户端提供的默认值,只有在日志和可复现测试显示问题与连接生命周期有关时才调整。
统计开关需要成对理解
statsUserUplink 与 statsUserDownlink 控制用户维度统计,policy.system 中的项目控制入站和出站维度统计。仅写 "stats": {} 不一定会自动收集所有指标;对应 policy 开关也需要启用。反过来,启用了统计但没有任何界面或 API 读取这些值,只会产生额外的记录工作。桌面客户端是否展示流量统计,还取决于它如何启动内核和读取统计接口。
统计值适合观察当前运行实例中的流量方向,不应被当作订阅额度、计费记录或远端服务器流量的权威来源。客户端重启、配置重载和内核切换都可能让本地统计重新开始。排查“界面没有流量数字”时,先确认客户端是否设计为读取底层统计,再检查 stats 对象和 system 开关,而不是直接修改协议参数。
policy 与超时错误的关系
日志中的 timeout 可能发生在名称解析、TCP 连接、TLS 或 REALITY 握手、代理协议握手、业务读写等不同阶段。policy 只能解释其中一部分会话超时。若连接在固定且很短的时间后中断,可以检查 handshake 与 connIdle;若日志明确指出连接远端地址超时,应先检查网络和出站;若只有某个应用的长连接定期断开,则比较其空闲周期与策略值。
修改超时前,应先记录错误发生阶段和时间规律。简单地把所有值调得很大,会延迟故障反馈,也会让失效连接长期占用资源。把值统一调小,则会损害慢网络和长连接。合理做法是保持默认策略作为基线,只对能够复现的场景做单项调整,并在客户端重启后重新测试同一目标。
系统策略与配置可移植性
不同客户端和内核家族可能支持不同的策略字段或默认值。v2rayN 使用的运行内核、v2rayNG 常见的 Xray 内核以及 v2flyNG 对应的 v2fly 内核,在兼容字段之外仍可能存在行为差异。将一份完整配置复制到另一个客户端前,应先检查目标客户端是否允许完整自定义配置,以及它会不会在启动时重写 policy。
配置可移植的核心不是字段越少越好,而是清楚区分标准结构、内核扩展和客户端生成内容。入站端口、日志路径、TUN 参数等往往与平台环境有关;服务器协议参数通常可以迁移;客户端界面状态与运行配置则未必一一对应。跨平台迁移时,先导入订阅建立可运行基线,再逐项迁移路由、DNS 和策略,比直接覆盖整份文件更容易发现不兼容字段。
配置加载、客户端覆写与验证流程
把语法验证、结构验证和实际连接验证分开,能够快速缩小问题范围。
第一层:确认 JSON 可以解析
语法验证只回答配置是否是合法 JSON。重点检查双引号、逗号、方括号与花括号是否成对,以及数字、布尔值和字符串是否使用正确类型。JSON 不接受注释,也不接受对象末尾的多余逗号。从网页、聊天记录或文档复制配置时,先保存为纯文本 UTF-8 文件,避免弯引号和不可见控制字符。
语法错误通常会给出行号和列号,但真正错误可能位于上一行。例如解析器在新属性开头报告错误,常见原因是上一项末尾缺少逗号。遇到文件结尾错误,则优先检查某个对象或数组是否没有闭合。编辑器的括号配对功能很有帮助,但不能替代对数据类型和字段层级的检查。
第二层:确认标签和字段属于正确位置
合法 JSON 仍可能不是合法的 V2Ray 配置。第二层验证需要检查顶层字段名称、协议专属 settings、传输层 streamSettings 和路由引用。常见结构错误包括把 tlsSettings 放到出站根部、把单个出站对象写成对象而不是数组、在规则中引用不存在的 outboundTag,以及把字符串数组写成单个字符串。
未知字段的处理方式可能因内核而异:有的会直接拒绝加载,有的可能忽略字段。被忽略比立即报错更难排查,因为配置看似启动成功,预期功能却没有生效。若日志出现 unknown field、failed to parse、failed to build 或找不到标签,应先回到对应对象层级检查,不要继续测试网络连通性。
第三层:确认客户端实际加载了哪份配置
v2rayN、v2rayNG 和 v2flyNG 都可能根据界面设置在启动时生成运行配置。用户编辑的文件不一定就是内核最终读取的文件,尤其在订阅节点、路由预设、系统代理和 TUN 模式共同启用时。验证前应从客户端日志确认配置生成与内核启动是否成功,并查看客户端提供的导出、预览或运行目录入口。
订阅更新通常会替换节点参数,但未必替换本地路由;完整自定义配置则可能绕过部分界面设置。需要明确当前使用的是“订阅节点加客户端模板”,还是“完整自定义配置”。两种模式混在一起时,经常出现界面里修改了端口但运行文件没有变化,或者手写路由被预设重新生成。订阅刷新失败的独立排查方法可查看订阅更新失败与自动更新设置。
分阶段建立连接基线
- 验证启动:确认内核启动完成,本地 SOCKS 或 HTTP 端口已经监听,没有端口占用和配置解析错误。
- 验证本地入口:让一个明确支持代理的应用连接本地端口,检查访问日志是否出现对应入站标签。
- 验证单一出站:暂时使用简单路由,确认目标请求能够经代理出站完成连接,避免复杂规则干扰。
- 恢复 DNS 与路由:逐组加入局域网、域名、GeoIP 和阻断规则,每次记录请求最终使用的出站。
- 恢复系统接管:最后再开启系统代理或 TUN,让更多应用进入处理链,并分别验证浏览器与终端。
这套顺序把问题拆成启动、本地入口、远程出站、分流和系统接管五层。若第一层尚未完成,就没有必要分析远程协议;若本地入口没有收到请求,则应检查应用代理设置;若简单出站可用而恢复路由后失败,则问题集中在 DNS 或规则;若手动代理可用但系统接管后异常,则重点检查系统代理状态、TUN 权限和路由冲突。
日志级别与可读信息
log.loglevel 可以在排错期间调整为包含更多细节的级别。记录越详细,越容易观察入站、路由和出站,但输出量也更大。正常使用时不必长期保留高详细度。排错日志应围绕一次明确测试截取,从内核启动开始,到目标请求失败或成功结束,避免在大量历史记录里混淆不同配置。
{
"log": {
"access": "",
"error": "",
"loglevel": "info",
"dnsLog": true
}
}
日志配置的具体路径与输出方式可能由客户端接管。空字符串是否表示控制台输出,也应结合当前内核和客户端行为确认。dnsLog 有助于观察内置解析过程,但只有经过内置 DNS 的查询才会出现。完整的日志阅读方法可参考V2Ray 运行日志常见报错含义与定位。
变更记录比反复试错更有效
每轮测试只改一个字段组,并记录修改前值、修改后值、测试目标和日志结果。例如把 domainStrategy 从 AsIs 改为 IPIfNonMatch 后,只验证需要 IP 规则的域名;不要同时更换节点、DNS 服务器和 TUN 模式。若结果变差,可以准确回退;若结果改善,也能知道是哪项生效。
客户端升级或切换内核后,应重新执行最小验证流程,而不是默认旧配置全部兼容。不要编造或依赖固定版本号判断字段支持;应查看当前客户端选择的内核类型、启动日志和配置错误信息。需要重新安装客户端时,可从安装包页面选择 Windows、macOS、Android 或 Linux 对应入口。
常见错误定位与长期维护方法
从错误发生的层级出发处理,而不是在全部配置段之间随机切换开关。
启动失败:先看语法、端口和引用
内核启动后立即退出,通常应先检查三类问题。第一类是 JSON 语法错误,包括缺少逗号、括号不匹配和错误的数据类型;第二类是本地资源冲突,例如监听端口已被另一个进程占用、日志目录不可写或 TUN 设备无法建立;第三类是配置对象引用错误,例如路由目标标签不存在、协议设置缺少必要字段或当前内核不认识某个扩展项。
处理顺序应与日志一致。解析错误发生在读取阶段,先修复文件;地址占用发生在监听阶段,检查重复客户端和本地端口;找不到出站发生在构建路由阶段,核对标签;只有内核明确进入运行状态后,才进入远端连接排查。看到客户端界面显示“未连接”并不足以判断是哪一层,必须查看最后一段错误日志。
本地端口存在,但应用没有流量
先确认应用填写的是正确代理类型和端口。HTTP 代理与 SOCKS 代理不是同一个入口,系统代理也不保证终端和后台服务自动使用。随后检查应用是否绕过本地地址、是否启用了自己的解析或代理设置,以及请求是否实际到达 access 日志。日志中完全没有这次请求,说明问题在应用到入站之间;日志出现入站记录后才说明流量已经进入 V2Ray。
如果只有部分应用不工作,可以为问题应用配置明确的 SOCKS 或 HTTP 入口做对照。手动入口可用而系统代理不可用时,检查操作系统代理状态和排除列表;系统代理下浏览器可用而终端不可用时,为终端工具设置它支持的代理选项。不要因为一个应用绕过系统代理,就修改远程节点协议。
域名失败而 IP 可用
这种现象优先检查解析路径。确认应用提交的是域名还是已经解析的 IP,内置 DNS 是否收到查询,服务器是否返回可用地址,以及 queryStrategy 是否选择了当前网络可达的地址族。若域名规则依赖探测,还要看入站是否能够识别目标名称。使用 Fake DNS 时,检查保留地址是否被同一运行实例还原。
如果 DNS 已经返回地址,但连接在出站阶段超时,问题不再是“没有解析结果”,而是该地址的连接路径、路由或远端服务。若返回地址被 expectIPs 判定不符合预期,检查 Geo 数据和服务器分组。若只有刚修改的域名仍使用旧地址,应考虑应用、系统与内置缓存,并通过重启相关进程或测试新域名排除缓存影响。
规则看似正确却没有命中
先从请求的实际形态检查:路由器拿到的是完整域名、子域名还是 IP;规则使用的是 full:、domain: 还是关键字;前面是否存在更宽的规则提前命中;Geo 数据文件是否可用。规则文字与目标看起来相似,不代表匹配语义相同。full:example.com 不匹配其他子域名,而过宽的关键字可能匹配多个无关目标。
将问题规则临时移动到数组靠前位置,可以用于判断是否被其他规则遮挡,但测试结束后仍要重新审视整体顺序。更可靠的方法是启用路由相关日志,记录请求最终选择的出站标签。若请求只带 IP,域名规则自然不会命中,此时应检查 sniffing、应用解析方式或补充合理的 IP 规则,而不是不断改写同一域名字符串。
节点参数正确但握手失败
先确认服务器地址和端口可达,再核对协议身份参数、传输方式与安全层。TLS 场景检查服务器名称、系统时间和证书错误;REALITY 场景检查服务器名称、公钥、短标识、指纹和流控参数;其他协议同样要确保客户端参数与服务端逐项对应。不要把不同节点中的传输字段拼成一份配置,因为每一层都可能有依赖关系。
如果订阅更新后突然失败,可保留旧节点作为对照,检查更新是否改变了地址、端口、传输方式或身份参数。不要手动猜测缺失字段。若所有节点同时失败,应优先检查本地网络、系统时间、内核启动和路由;只有单个节点失败时,才更可能集中在该节点参数或远端状态。
配置逐渐膨胀后的整理原则
长期使用后,配置中容易积累失效标签、重复域名规则、互相覆盖的 Geo 规则和不再使用的入站。整理时先绘制当前处理链:列出所有入站标签、所有出站标签、每条规则的目标和 DNS 服务器用途。确认没有引用后再删除对象。相同目标和相同出口的规则可以合并,但不要为了减少行数把不同目的的条件塞进一条难以解释的规则。
建议为标签建立稳定命名规则,并按“特殊阻断、局域网直连、特定代理、默认处理”的逻辑排列路由。DNS 服务器对象按适用域名范围分组,policy 只保留实际使用的等级。配置文件本身不支持注释时,可以在独立的维护记录中写明每个标签用途、最后一次验证场景和客户端生成方式,避免以后看到字段却不敢调整。
客户端更新与配置迁移
更新客户端前保存当前可运行配置、路由预设和订阅设置。更新后先用原有节点完成最小连接测试,再检查高级功能。若客户端切换了运行内核,关注启动日志中的未知字段和弃用提示。不要因为界面选项名称变化,就立即删除底层配置;先导出新生成的运行文件,与旧配置按顶层段比较。
从桌面端迁移到另一桌面平台时,服务器协议参数通常可以通过订阅恢复,但本地监听端口、系统代理、TUN 权限和日志路径需要按新平台重新设置。Android 上迁移到 v2rayNG 或 v2flyNG 时,同样应先导入订阅,再按目标内核恢复路由与 DNS。平台差异主要落在系统接管和文件位置,不应在服务器出站中加入与平台无关的猜测字段。
形成固定排错清单
JSON、字段层级、端口占用、标签引用、运行权限。
应用代理类型、本地端口、系统代理、TUN 接管、访问日志。
域名或 IP、DNS 返回、规则顺序、Geo 数据、最终出站标签。
服务器可达性、身份参数、传输方式、安全层与握手日志。
每次故障都按这四层顺序记录,能够避免重复劳动。日志出现 rejected 时查看拒绝发生在本地入口、路由阻断还是远端;出现 timeout 时确认超时阶段;出现身份或握手错误时回到对应出站参数。若仍无法判断,可到疑难解答按基础认知、安装配置、使用技巧和故障排查分类继续查找。
配置维护的目标不是堆满可选字段,而是让每一段都有明确作用,并能够通过日志证明处理结果。先保持一份结构简单、可重复验证的基线,再增加路由、DNS、TUN 和策略。遇到问题时回到基线逐层恢复,比从一份复杂配置中随机删除字段更可靠。