01 / ROOT OBJECT
JSON 结构总览与数据流边界
顶层对象不是执行步骤清单
V2Ray 配置文件的根节点是一个 JSON 对象。常见顶层字段包括 log、dns、inbounds、outbounds、routing、policy 和 stats。这些字段在文件中的书写顺序通常不决定执行顺序:把 routing 写在 inbounds 前面不会让路由先于入站启动。真正的运行关系由模块职责和标签引用组成,因此整理配置时可以按阅读习惯排序,但不要把视觉顺序当成控制流。
inbounds 与 outbounds 都是数组,因为一个内核实例可以同时监听多个入口,也可以准备多个出口。数组中的每个对象通常通过 tag 获得稳定名称。路由规则使用 inboundTag 限定来源,并通过 outboundTag 指向目标出口。标签属于配置内部的引用键,不是协议名;将出口标签命名为 proxy、direct 或更具体的角色词都可以,前提是引用完全一致且便于维护。
JSON 语法与客户端生成配置
标准 JSON 要求属性名和字符串使用双引号,不允许尾随逗号,也不支持原生注释。布尔值必须写成 true 或 false,端口等数字不应写成带引号的字符串。中文、路径和域名可以直接放进 UTF-8 文件,但 Windows 路径中的反斜杠需要转义。手工编辑后若出现“无法解析配置”一类错误,应先检查语法,再检查协议字段;语法解析失败时,内核尚未进入网络连接阶段。
v2rayN、v2rayNG 与 v2flyNG 会根据界面设置生成或组合内核配置。桌面端首推 v2rayN,它适合查看路由、系统代理和内核日志;Android 上的 v2rayNG 使用 Xray 内核,v2flyNG 使用 V2Fly 内核。客户端生成的临时配置可能在重启、切换节点或更新订阅后被覆盖,因此长期规则应通过客户端提供的自定义配置、路由设置或受支持的模板入口维护,不宜直接修改运行目录中的临时文件。
{
"log": {
"loglevel": "warning"
},
"dns": {
"servers": [
"1.1.1.1",
"8.8.8.8"
]
},
"inbounds": [
{
"tag": "socks-in",
"listen": "127.0.0.1",
"port": 10808,
"protocol": "socks",
"settings": {
"udp": true
}
}
],
"outbounds": [
{
"tag": "direct",
"protocol": "freedom"
}
],
"routing": {
"domainStrategy": "AsIs",
"rules": []
}
}
最小配置只需要一个可接收流量的入站和一个可建立连接的出站,但真实客户端还会加入本地 API、统计、DNS 或多组路由。阅读大型配置时,可以先隐藏与目标问题无关的模块:连接完全无法启动先看 JSON、入站和出站;只有部分域名走错出口再看路由;域名失败而 IP 可用则优先看 DNS。按故障边界缩小范围,比逐行盲改更可靠。
02 / INBOUND
inbounds 入站监听、协议与流量识别
listen、port、protocol 与 tag
入站决定哪些本地或网络连接可以进入内核。常见本地入口是 SOCKS 和 HTTP 代理:浏览器、终端或系统代理把请求发到该监听地址,内核再处理后续路由。listen 设为 127.0.0.1 时只接受本机连接,适合单机客户端;写成全部接口监听会扩大可访问范围,除非明确需要局域网设备接入,否则不应随意开放。port 必须未被其他程序占用,同一地址与端口组合不能被两个入站重复绑定。
protocol 指定入口协议,settings 的结构随协议变化。SOCKS 入站常用 udp 控制 UDP 转发,HTTP 入站则接收普通 HTTP 代理和 CONNECT 请求。tag 用于路由识别来源,例如给浏览器专用入口标记 browser-in,就能让该入口使用独立规则。标签不能代替端口:应用仍需连接正确的监听端口,路由模块才有机会读取标签。
{
"inbounds": [
{
"tag": "socks-in",
"listen": "127.0.0.1",
"port": 10808,
"protocol": "socks",
"settings": {
"auth": "noauth",
"udp": true
},
"sniffing": {
"enabled": true,
"destOverride": [
"http",
"tls"
]
}
},
{
"tag": "http-in",
"listen": "127.0.0.1",
"port": 10809,
"protocol": "http",
"settings": {}
}
]
}
sniffing 的用途和边界
当应用只把目标 IP 交给代理,而路由规则需要按域名判断时,sniffing 可以从 HTTP 请求或 TLS 握手中识别目标域名。destOverride 表示允许识别哪些流量类型。启用后,路由模块获得更完整的域名信息,按域名分流的命中率通常更稳定;但嗅探不是 DNS 的替代品,也不能从所有加密流量中恢复任意内容。它只利用连接建立阶段可见的目标信息。
排查嗅探相关问题时,应分别观察“应用提交的原始目标”和“路由实际使用的目标”。如果关闭嗅探后规则恢复正常,可能是识别出的域名触发了另一条更靠前的规则;如果开启后域名规则仍不命中,应确认流量确实经过该入站,而不是被系统中的另一代理端口接收。透明代理、虚拟网卡和普通 SOCKS 入口的流量来源不同,客户端界面开启某种模式后,实际生成的入站也会变化。
| 字段 | 作用 | 常见检查点 |
|---|---|---|
listen |
限定监听的本地地址 | 仅本机使用时优先绑定回环地址 |
port |
接收应用连接的端口 | 与系统代理设置一致,并确认未被占用 |
protocol |
定义入口协议 | 应用设置的代理类型必须匹配 |
tag |
供路由和统计模块引用 | 大小写与规则中的引用完全一致 |
入站故障通常表现为应用无法连接本地代理、端口绑定失败或 UDP 请求单独失效。应先用客户端日志确认监听是否成功,再检查系统代理地址与端口。若浏览器可用而某个应用不可用,重点检查该应用是否支持所选代理类型、是否绕过系统代理以及是否需要 UDP。不要在尚未确认本地入口可达时反复更换远端节点,因为流量可能根本没有进入内核。
03 / OUTBOUND
outbounds 出站协议对象与出口选择
远端出口、直连出口和阻断出口
出站负责把路由选中的连接送往最终目标或远端服务。一个完整配置通常至少包含远端代理出口和直连出口,按需再加入阻断出口。远端出口的 protocol 可以是 VMess、VLESS、Trojan 等客户端与服务端共同支持的协议;settings 保存服务器地址、端口和认证信息;streamSettings 描述底层传输、安全层与相关参数。字段必须与服务端配置成对,协议名正确并不代表传输参数可以互换。
freedom 表示由当前设备直接访问目标,常将标签设为 direct。blackhole 用于主动终止被规则选中的连接,常将标签设为 block。它们仍然是标准出站,因此路由规则只需更换 outboundTag,无需使用特殊动作语法。若规则引用不存在的标签,配置可能加载失败,或在运行阶段无法找到目标出口;修改标签后必须同步搜索全部引用。
{
"outbounds": [
{
"tag": "proxy",
"protocol": "vless",
"settings": {
"vnext": [
{
"address": "server.example.com",
"port": 443,
"users": [
{
"id": "11111111-2222-3333-4444-555555555555",
"encryption": "none"
}
]
}
]
},
"streamSettings": {
"network": "tcp",
"security": "tls",
"tlsSettings": {
"serverName": "server.example.com"
}
}
},
{
"tag": "direct",
"protocol": "freedom",
"settings": {}
},
{
"tag": "block",
"protocol": "blackhole",
"settings": {
"response": {
"type": "none"
}
}
}
]
}
streamSettings 需要整体核对
streamSettings.network 描述底层传输,例如 TCP、WebSocket 或 gRPC;security 描述 TLS、REALITY 等安全层选择。与具体传输相关的设置位于对应子对象中,例如 WebSocket 使用路径和请求头,TLS 使用服务名等参数。排错时应把远端出站拆成三层:首先确认服务器地址和端口可达,其次确认用户认证与协议一致,最后确认传输和安全层细节一致。只看到“连接被关闭”并不能直接判断是哪一层。
如果服务器地址使用域名,内核必须先完成解析,因此出站失败也可能由 DNS 引起。若日志显示已解析出地址但握手失败,应转向协议、时间、服务名和传输参数;若连解析结果都没有,则先检查 dns 配置和系统网络。使用 IP 直接测试可以帮助区分解析与连接问题,但 TLS 场景通常仍需要正确的服务名,不能把 IP 测试结果直接当作最终配置。
mux 等连接复用设置应在确认基本连接稳定后再调整。复用不是所有网络和所有协议下都必然更快,过早加入优化参数会增加变量。建立配置时可先保留一个远端出口、一个直连出口和最少传输字段,成功后再逐步加入路由、复用或额外出口。每增加一层都记录变更,出现回退时就能快速定位。
客户端订阅导入会自动生成远端出站,手工覆盖前应理解更新机制。订阅更新可能替换节点参数,但本地路由通常由客户端单独管理。需要重新获取客户端时可前往获取客户端,需要理解分享链接与订阅地址区别可阅读分享链接与订阅导入说明。
04 / ROUTING
routing 路由匹配顺序与分流规则
规则按顺序命中,不做自动合并
路由模块读取连接属性并选择出站。rules 是有顺序的数组,通常由上到下检查,连接命中一条可执行规则后便使用该规则指定的出口。因此越具体、越需要优先处理的规则应放在前面,范围宽泛的兜底规则放在后面。两条规则即使条件部分重叠,也不会自动计算“更具体者优先”;实际优先级来自数组位置。
常用条件包括 domain、ip、port、network、inboundTag 和 protocol。同一规则内放入多个不同类别条件时,连接通常需要同时满足这些类别;同一类别数组中的多个值则表示匹配其中之一。例如一条规则同时写入域名和端口,就只处理既符合域名条件又落在该端口范围内的连接。把互不相关的条件塞进一条规则,常会造成规则看起来齐全却始终无法命中。
{
"routing": {
"domainStrategy": "IPIfNonMatch",
"rules": [
{
"type": "field",
"ip": [
"geoip:private"
],
"outboundTag": "direct"
},
{
"type": "field",
"domain": [
"domain:example.cn",
"full:intranet.example"
],
"outboundTag": "direct"
},
{
"type": "field",
"domain": [
"geosite:category-ads-all"
],
"outboundTag": "block"
},
{
"type": "field",
"network": "tcp,udp",
"outboundTag": "proxy"
}
]
}
}
域名、IP 与 domainStrategy
域名条件可以使用不同匹配形式。full: 表示完整域名匹配,适合固定主机名;domain: 可覆盖指定域及其子域;regexp: 提供正则匹配,但复杂表达式会降低可读性,应只在普通匹配无法表达时使用。geosite: 引用内核数据文件中的域名集合,集合是否可用取决于客户端携带的数据资源。写入一个名称并不保证当前环境存在对应集合,加载错误或规则无效时应查看日志。
IP 条件支持单个地址、CIDR 网段和 geoip: 集合。私有地址通常应优先直连,避免局域网服务被送入远端出口。域名规则是否需要先解析为 IP,受 domainStrategy 影响。AsIs 主要按原始域名匹配;IPIfNonMatch 在域名规则没有匹配时尝试解析并继续检查 IP 规则;IPOnDemand 会更积极地为 IP 规则触发解析。更积极的解析策略可能增加 DNS 查询,也可能改变分流结果,不能只按名称判断“更高级”。
| 策略 | 主要行为 | 适用判断 |
|---|---|---|
AsIs |
保留请求中的域名形态进行匹配 | 规则主要依赖域名,且不希望额外解析 |
IPIfNonMatch |
域名未命中后解析并检查 IP 规则 | 域名规则与 IP 网段规则并存 |
IPOnDemand |
遇到需要 IP 的判断时触发解析 | 明确理解解析路径并需要 IP 分类 |
验证路由时不要只看网站是否打开,因为多个出口都可能成功。应临时把目标域名放进一条位置靠前、标签明确的规则,再从日志确认它选择了哪个出口。若规则不命中,依次检查流量是否经过预期入站、嗅探是否提供域名、匹配前缀是否正确、规则位置是否被前项截获、出口标签是否存在。有关 DNS 分流与污染规避的完整思路,可继续阅读V2Ray DNS 分流解析指南。
05 / DNS
DNS 配置服务器选择、hosts 与解析路径
内置 DNS 只处理进入其路径的查询
dns 模块定义内核如何解析域名,但配置了 DNS 服务器不等于系统中所有查询都会自动改走内核。应用可能自行解析,也可能由操作系统先解析后只把 IP 交给代理。是否进入内置 DNS,取决于客户端模式、入站类型、路由策略以及应用行为。因此出现 DNS 问题时,第一步不是不断替换服务器地址,而是确认查询发生在哪一层。
servers 可以放简单地址,也可以放带匹配域名的服务器对象。简单写法按列表提供通用解析来源;对象写法可以用 domains 指定某类域名优先交给某个服务器,并通过 expectIPs 约束期望的地址范围。hosts 用于静态映射或别名,它在已知固定结果、内部服务映射和测试规则时很有用,但不适合维护大量频繁变化的公共域名。
{
"dns": {
"hosts": {
"domain:internal.example": "192.168.10.20",
"dns-alias.example": "target.example"
},
"servers": [
{
"address": "223.5.5.5",
"domains": [
"geosite:cn"
],
"expectIPs": [
"geoip:cn"
]
},
{
"address": "1.1.1.1",
"domains": [
"geosite:geolocation-!cn"
]
},
"localhost"
],
"queryStrategy": "UseIP"
}
}
查询策略与 DNS 出站
queryStrategy 控制查询地址族的倾向,例如同时允许 IPv4 与 IPv6,或只请求其中一种。选择前应确认当前网络、远端出口和目标服务是否真正具备对应地址族的连通性。若网络没有稳定的 IPv6 路径却优先获得 IPv6 结果,表现可能是解析成功但连接超时;这不是 DNS 服务器失效,而是解析结果与实际出口能力不一致。
高级配置可以加入 dns 协议出站,并通过路由让内核发起的 DNS 流量走指定出口。此时需要同时检查三个对象:dns.servers 决定向谁查询,DNS 出站决定如何发送查询,routing.rules 决定这类流量选哪个出口。只改其中一个对象,可能产生查询回环或与预期相反的路径。尤其不要把用于解析远端服务器域名的 DNS 请求,依赖到尚未建立的同一远端连接上。
{
"outbounds": [
{
"tag": "dns-out",
"protocol": "dns"
}
],
"routing": {
"rules": [
{
"type": "field",
"protocol": [
"dns"
],
"outboundTag": "dns-out"
}
]
}
}
DNS 排错可以采用固定顺序:先用系统工具确认设备本身有网络,再从内核日志确认是否发起查询、查询交给哪个服务器、返回哪类地址,最后确认该地址通过选定出口可达。如果域名访问失败而直接访问测试 IP 能建立 TCP 连接,应继续检查域名解析和 TLS 服务名;如果解析有结果但所有地址都超时,应检查路由与出口。清理缓存只能消除旧结果,不能修复错误的规则链。
分流配置应保持可解释性。把本地域名、本地地址和明确的内部服务交给本地解析,其余请求再按需求选择远端解析,是比堆叠大量例外更稳定的起点。每次修改只改变一个变量,并记录修改前后的查询日志。详细字段拆解和防止解析泄漏的实践,可结合前述DNS 配置详解继续核对。
06 / POLICY
policy 策略连接时限、统计与资源约束
level 与策略对象的对应关系
policy 用于设置用户级和系统级运行策略。用户级策略位于 levels,键名是等级数字的字符串;协议用户对象中的 level 决定使用哪组策略。它不是网络质量评分,也不表示权限高低,而是把一组连接参数映射给指定用户。多数单用户客户端使用等级 0 即可,只有确实需要区分多组连接行为时才增加等级。
常见用户级字段包括握手超时、连接空闲时间、仅上行或仅下行时的保留时间,以及是否启用用户上行和下行统计。时间字段的具体单位和生效边界应结合内核文档与日志确认,不能把所有数值都当成毫秒。值设置过短会让长连接、后台同步或低频请求提前断开;设置过长则可能让失去活动的连接占用资源。优化前应先观察真实故障,不要把策略对象当成通用提速开关。
{
"policy": {
"levels": {
"0": {
"handshake": 4,
"connIdle": 300,
"uplinkOnly": 2,
"downlinkOnly": 5,
"statsUserUplink": true,
"statsUserDownlink": true
}
},
"system": {
"statsInboundUplink": true,
"statsInboundDownlink": true,
"statsOutboundUplink": true,
"statsOutboundDownlink": true
}
},
"stats": {}
}
policy、stats 与 API 的关系
打开策略中的统计开关,只表示允许收集相应维度的数据;顶层通常还需要存在 stats 对象。若客户端界面需要读取统计信息,还可能生成本地 API 入站和相关路由。三者职责不同:policy 决定哪些维度启用,stats 启动统计模块,API 则提供读取入口。只复制其中一段,界面可能仍然看不到数据。
统计会增加一定运行工作量,实际是否需要取决于客户端功能。只为确认连通性时,日志往往比累计统计更直接;需要长期观察入站和出站流量时,再启用对应维度。不要为了“配置完整”打开所有开关,也不要根据短时间统计值判断协议优劣。应用缓存、并发连接、系统更新和后台任务都会影响观察结果。
| 策略项 | 控制范围 | 设置过紧的表现 |
|---|---|---|
handshake |
连接建立阶段允许的时间 | 网络稍慢时频繁握手超时 |
connIdle |
无活动连接的保留时间 | 低频长连接被提前关闭 |
uplinkOnly |
仅剩上行活动时的保留窗口 | 单向传输过早终止 |
downlinkOnly |
仅剩下行活动时的保留窗口 | 下载尾段或响应流被中断 |
策略排错应从默认值开始。若某类连接在固定空闲时间后断开,可以对照 connIdle;若远端网络偶发变慢时才失败,可以检查握手时间是否被压得过低;若只有统计缺失但连接正常,则检查统计开关、顶层模块和客户端 API,而不是修改出站协议。把连接行为和观测行为分开,能避免为了修复界面数据显示而破坏正常链路。
客户端可能根据图形界面自动管理策略和统计项。手工配置与客户端设置并存时,应先确认最终生成文件,而不是只看自定义片段。v2rayN 更适合在桌面环境中检查生成结果和日志;v2rayNG、v2flyNG 的移动端配置通常由应用管理,手工字段应通过其支持的入口导入。不同内核对扩展字段的接受范围可能不同,迁移配置时应从核心字段开始逐段加入。
07 / LOGGING
日志与观测:从错误阶段定位模块
loglevel 决定信息密度
log 是配置排错的第一入口。常用 loglevel 从更详细到更精简可包含 debug、info、warning、error 和 none 等等级。日常运行可保留 warning;复现复杂路由或 DNS 问题时临时提高到 info 或 debug,记录完成后再恢复。详细日志可能包含目标域名、地址、标签与连接过程,不应直接公开未经整理的完整文件。
access 与 error 可以指定访问日志和错误日志的输出位置。省略文件路径时,客户端通常从标准输出或自身日志窗口收集内容。相对路径基于进程工作目录,不一定是配置文件所在目录;在受限制目录下还可能因写入权限失败。图形客户端已经提供日志面板时,优先使用客户端管理的输出方式,避免自定义路径与更新、权限或便携目录冲突。
{
"log": {
"access": "",
"error": "",
"loglevel": "warning",
"dnsLog": false
}
}
按阶段读取日志,而不是只搜索 error
一条连接大致经历配置加载、入站接收、目标识别、DNS 解析、路由选择、出站拨号、协议与安全层握手、数据传输等阶段。日志中的最后一行只是表面结果,真正原因常出现在前几行。例如“连接关闭”可能由远端主动断开、握手参数不一致或上游超时引起;“找不到出口”则更接近标签引用问题;“地址已被占用”发生在入站监听阶段,与节点参数没有关系。
排错时先记录复现时间和目标,然后清空旧日志或从时间点附近开始阅读。多次尝试会产生交错记录,若同时打开多个应用,很难判断哪一条连接对应测试目标。可以先关闭无关程序,只保留一个浏览器请求,再观察从入站到出站的完整链路。路由问题重点查入站标签、域名或 IP 条件以及最终出站标签;DNS 问题重点查查询服务器、返回地址和后续连接;握手问题重点查服务名、传输、安全层和系统时间。
如果客户端启动后立即退出,应先确认运行环境、目录权限、内核文件与端口占用。Windows 桌面端还可能受到运行库和受保护目录写入限制影响;Android 客户端则需要检查系统是否限制后台运行。相关处理步骤可查阅v2rayN 启动崩溃与 v2rayNG 闪退排查。这类问题发生在配置链路之外或加载早期,不应直接归因于远端协议。
日志对比比单次截图更有价值。保留一份可用配置的启动与连接记录,再与修改后的记录比较:是否少了某个监听、DNS 返回是否变化、同一域名选出的出站是否不同、握手失败发生在解析前还是解析后。比较阶段差异可以快速缩小范围。完成诊断后,应移除临时 debug 设置和测试规则,避免长期积累大量日志或让高优先级测试规则继续影响日常分流。
08 / VALIDATION
配置验证与排错的固定执行顺序
先语法、再引用、后网络
稳定的验证流程应分层执行。第一层检查 JSON 语法:括号是否配对、逗号是否正确、字符串是否闭合、数字与布尔值类型是否正确。第二层检查内部引用:所有 outboundTag、inboundTag 和策略等级是否存在对应对象,标签大小写是否一致。第三层才检查网络:监听端口、DNS、服务器地址、协议认证、传输与安全层。跳过前两层直接更换节点,会让简单错误被网络现象掩盖。
内核通常提供配置测试或以指定配置启动的能力,但不同客户端封装方式和内核命令参数可能不同。图形客户端用户应优先查看客户端的配置检查和日志入口,避免在不了解运行目录时直接执行命令。若使用独立内核环境,可先查看当前可执行文件的帮助信息,再按其支持的参数加载配置。验证成功只代表结构与字段被接受,不代表远端服务一定可达。
{
"log": {
"loglevel": "info"
},
"inbounds": [
{
"tag": "test-in",
"listen": "127.0.0.1",
"port": 10808,
"protocol": "socks",
"settings": {
"udp": true
}
}
],
"outbounds": [
{
"tag": "direct",
"protocol": "freedom"
}
],
"routing": {
"rules": [
{
"type": "field",
"inboundTag": [
"test-in"
],
"outboundTag": "direct"
}
]
}
}
用最小配置切分故障范围
上面的最小配置只验证本地 SOCKS 入站、路由标签和直连出站。若它无法启动,问题集中在语法、端口和本机运行环境;若它能启动并访问目标,再替换为真实远端出站,就能判断故障是否进入远端协议层。随后依次恢复 DNS、域名规则、IP 规则、阻断规则、统计和策略。每次只恢复一组字段,发现故障后即可把范围锁定在刚加入的模块。
常见错误可以按现象分类。启动即失败,多为 JSON、未知字段、标签引用、端口冲突或文件权限;本地代理不可连接,多为监听地址、端口和应用代理类型不一致;所有域名失败但部分 IP 可达,多为 DNS 路径问题;只有特定域名走错出口,多为规则顺序、嗅探或匹配前缀问题;连接建立后很快断开,则需要检查策略时限、远端握手和网络稳定性。分类不是最终结论,但能决定先读哪一段日志。
| 现象 | 优先模块 | 第一项确认 |
|---|---|---|
| 配置无法加载 | JSON / 字段结构 | 解析错误所在行及前一行逗号 |
| 本地端口不可连接 | inbounds | 监听是否成功、端口是否一致 |
| 域名失败而地址可达 | dns / routing | 查询是否进入内核及返回结果 |
| 只有一组规则异常 | routing | 规则顺序和最终出站标签 |
| 握手后立即断开 | outbounds | 协议、传输、安全层是否整体一致 |
客户端环境还要考虑配置覆盖。v2rayN 切换节点、更新订阅或改变路由模式后,可能重新生成运行配置;v2rayNG 与 v2flyNG 也会按应用设置构建内核参数。手工修改临时文件后短暂有效、重启后失效,通常不是内核忽略配置,而是文件被客户端重新生成。应把长期设置放到客户端支持的自定义路由、模板或导入入口。
最终验收不应只看一个网页能否打开。至少验证本地地址直连、普通域名解析、预期代理域名、UDP 需求、客户端重启和订阅更新后的行为,并从日志确认出口标签符合设计。若刚开始使用客户端,先按使用文档完成基础链路,再回到本页增加规则;若需要重新选择安装包,前往获取客户端按 Windows、macOS、Android 或 Linux 平台下载。复杂配置的可靠性来自清晰边界、逐段验证和可回退记录,而不是字段数量。