进阶技巧 预计阅读 17 分钟

Xray JSON配置深度解析:模块化路由与工程实践

面向开发者与运维工程师的 Xray JSON 配置进阶指南,系统讲解核心模块、路由引擎、DNS 防泄露和模块化管理,并提供可直接粘贴运行的配置示例与排障方法。

Xray 的 JSON 配置并不是把节点参数、DNS、入站、出站和路由规则简单堆在同一个文件里。真正影响可维护性的,是这些模块之间如何传递信息:入站决定流量怎样进入核心,出站决定请求从哪里离开,DNS 决定域名在哪一侧解析,路由决定每条连接最终交给哪个出站。只要其中一个模块的假设不一致,就可能出现“核心已经启动,但网页打不开”“域名规则没有命中”或“代理正常却发生 DNS 泄漏”等问题。

本文以 Xray-core 25.8.3 的常见 JSON 结构为例,围绕模块化设计、路由匹配、DNS 防泄露和配置验证展开。示例使用本机 SOCKS 入站 127.0.0.1:10808、HTTP 入站 127.0.0.1:10809,并提供代理、直连和阻断三类出站。配置中的服务器地址、UUID、Reality 公钥等值均为占位符,实际使用前必须替换为服务端提供的完整参数。

本文速览

先用单文件配置验证 Xray 入站、出站和路由,再按日志、DNS、路由三个边界拆分模块。读完后可以理解 JSON 顶层对象的职责,搭建一套带 DNS 分流和防泄露策略的基础配置,并使用配置检查、端口检查和日志关键词定位常见故障。

先建立 JSON 配置的模块边界

Xray 配置通常由 logdnsinboundsoutboundsrouting 和可选的 policy 等顶层对象组成。它们不是相互独立的功能开关,而是一条有顺序的处理链。客户端或应用先连接入站端口,核心根据入站标签和目标信息执行路由,然后选择出站;如果规则需要域名或 IP 分类,DNS 模块还会参与解析和缓存。

6
常用顶层模块
10808
SOCKS 入站端口
10809
HTTP 入站端口
3
代理/直连/阻断出站

流量入口

SOCKS
127.0.0.1:10808
HTTP
127.0.0.1:10809
标签
socks-in、http-in

桌面应用可按协议选择本地代理端口,两个入口都只监听回环地址。

流量出口

代理
proxy
直连
direct
阻断
block

路由规则通过 outboundTag 引用标签,而不是依赖出站在数组中的位置。

inboundsoutbounds 都是数组,因为同一份配置可以同时声明多个入口和出口。每个对象的 tag 是模块之间的引用名称,必须保持唯一并且拼写完全一致。例如路由规则写了 outboundTagproxy,出站数组中就必须存在 "tag": "proxy"。标签不存在时,核心可能在启动阶段报错,也可能在运行时无法按照预期选择出口。

模块化不等于把任意 JSON 文件放进一个目录就能自动加载。Xray 的启动参数、发行版服务文件和客户端封装方式,决定了核心实际读取哪些文件。最稳妥的工程实践是:先维护一个可独立运行的完整配置,再使用明确的生成脚本或部署工具把片段合并成最终的 config.json。不要直接假设存在类似 include 的通用字段,也不要把多个完整 JSON 对象机械拼接到一起。

结论:标签是配置的接口

模块拆分后最容易出错的不是 JSON 逗号,而是标签引用。把入站标签、出站标签和路由目标整理成一张清单,通常比继续增加规则更能提升配置可靠性。

先构建一份可验证的基础配置

下面是一份结构完整的示例。它使用 VLESS over TCP with REALITY 作为代理出站,参数仅作格式示例;其中 addressidserverNamepublicKeyshortId 必须替换。若服务端实际使用 VMess、WebSocket 或其他传输方式,不能只修改协议名称,必须同步替换对应的 settingsstreamSettings 和安全参数。

{
  "log": {
    "loglevel": "warning",
    "error": "/var/log/xray/error.log",
    "access": "/var/log/xray/access.log"
  },
  "inbounds": [
    {
      "tag": "socks-in",
      "listen": "127.0.0.1",
      "port": 10808,
      "protocol": "socks",
      "settings": {
        "auth": "noauth",
        "udp": true
      },
      "sniffing": {
        "enabled": true,
        "destOverride": ["http", "tls", "quic"]
      }
    },
    {
      "tag": "http-in",
      "listen": "127.0.0.1",
      "port": 10809,
      "protocol": "http",
      "settings": {}
    }
  ],
  "outbounds": [
    {
      "tag": "proxy",
      "protocol": "vless",
      "settings": {
        "vnext": [
          {
            "address": "node.example.net",
            "port": 443,
            "users": [
              {
                "id": "00000000-0000-0000-0000-000000000000",
                "encryption": "none",
                "flow": "xtls-rprx-vision"
              }
            ]
          }
        ]
      },
      "streamSettings": {
        "network": "tcp",
        "security": "reality",
        "realitySettings": {
          "serverName": "www.example.com",
          "fingerprint": "chrome",
          "publicKey": "REPLACE_WITH_SERVER_PUBLIC_KEY",
          "shortId": "REPLACE_WITH_SHORT_ID",
          "spiderX": "/"
        }
      }
    },
    {
      "tag": "direct",
      "protocol": "freedom",
      "settings": {}
    },
    {
      "tag": "block",
      "protocol": "blackhole",
      "settings": {
        "response": {
          "type": "http"
        }
      }
    }
  ],
  "routing": {
    "domainStrategy": "AsIs",
    "rules": [
      {
        "type": "field",
        "ip": ["geoip:private"],
        "outboundTag": "direct"
      },
      {
        "type": "field",
        "domain": ["geosite:category-ads-all"],
        "outboundTag": "block"
      },
      {
        "type": "field",
        "domain": ["geosite:cn"],
        "outboundTag": "direct"
      },
      {
        "type": "field",
        "ip": ["geoip:cn"],
        "outboundTag": "direct"
      },
      {
        "type": "field",
        "network": "tcp,udp",
        "outboundTag": "proxy"
      }
    ]
  }
}

这份配置的规则顺序有明确含义。私有地址排在最前面,避免访问路由器、局域网存储或本地服务时进入代理。广告域名在后续地区规则之前被阻断。geosite:cngeoip:cn 分别处理域名分类和 IP 分类。最后的 network 规则是兜底项,把尚未命中的 TCP、UDP 流量交给代理。

  1. 准备目录

    在 Linux 中创建 /etc/xray/ 与日志目录;Windows 使用固定的 Xray 运行目录,并确认配置文件编码为 UTF-8。

  2. 替换节点值

    把地址、端口、UUID、服务器名称、公钥和 shortId 替换为服务端参数,保持 VLESS 的 flow 与服务端能力一致。

  3. 检查 JSON

    先执行 xray run -test -config /etc/xray/config.json,确认没有逗号、括号、字段类型或标签错误。

  4. 启动核心

    测试通过后再执行 xray run -config /etc/xray/config.json,观察错误日志并确认 10808、10809 已监听。

  5. 验证出口

    让浏览器或命令行使用 SOCKS5 端口 10808,先访问普通站点,再测试局域网地址和需要代理的目标。

在 v2rayN 中,若配置由客户端自动生成,不建议直接覆盖客户端管理的完整文件。更合适的做法是先在 v2rayN 中确认节点可用,再依据实际节点类型导出或核对核心参数。桌面客户端的本地端口、系统代理和 TUN 模式可能由界面动态管理,手工配置与客户端配置同时启动时,还要防止两个核心抢占同一个端口。

理解路由引擎与 domainStrategy

Xray 路由通常按照 rules 数组从上到下匹配。它不是自动寻找“看起来最精确”的规则,也不会因为某条规则写在后面就覆盖前面的结果。一个请求一旦命中某条规则并选定出站,后续规则通常不会再参与。因此,范围小、影响面大的例外应放在前面,范围宽泛的兜底规则必须放到最后。

domainipportnetworkinboundTag 是不同维度的条件。一个规则同时写多个维度时,通常表示这些条件需要共同满足;同一个数组字段中的多个值则用于扩大该字段的匹配范围。比如同时写 domainip,并不等于“域名命中或 IP 命中”,而可能使规则只匹配同时具备两类信息的请求。

配置项 判断对象 工程用途 常见误区
domain 域名、后缀或 geosite 类别 按站点名称分流 目标已经只剩 IP 时无法直接命中
ip 目标地址或 geoip 类别 处理局域网、地区地址段 把域名分类误认为实时地理定位
network TCP、UDP 或两者 构造最终兜底策略 放在前面导致所有流量提前结束匹配
inboundTag 流量来自哪个入站 为 TUN、SOCKS、DNS 分别制定策略 标签拼写与入站定义不一致

domainStrategy 决定路由阶段如何处理域名与 IP 的关系。AsIs 尽量保持原始目标形式,适合已经能够稳定取得域名信息的代理入站。IPIfNonMatch 表示域名规则没有命中时,再尝试把域名解析成 IP 并继续 IP 规则。IPOnDemand 则更积极地触发解析,适合确实依赖 IP 规则的场景,但会增加解析时机和 DNS 路径的复杂度。

选择策略时不要只看“规则能否命中”,还要看解析发生在哪里。如果设为 IPIfNonMatch,而 DNS 模块仍然使用本地运营商解析器,那么未命中的域名可能在路由判断前就通过不希望使用的 DNS 完成解析。防泄露配置需要同时设计 dns、入站 DNS 流量和路由规则,不能只修改一个字段。

应用发起请求入站接收流量提取域名信息DNS 完成解析路由规则匹配出站建立连接

DNS 分流与防泄露设计

DNS 防泄露的重点不是“只配置一个远程 DNS”,而是确保 DNS 请求的去向、解析结果的使用方式和最终出站路径彼此一致。应用可能直接访问系统 DNS,浏览器可能启用自己的加密 DNS,TUN 模式还可能捕获 UDP 53 端口。若这些路径没有统一,代理流量虽然正常,域名查询却可能仍然从本地网络发出。

下面的示例把国内域名交给一个直连 DNS,把其他域名交给远程 DNS。skipFallback 用于避免匹配到国内服务器的查询再落到 fallback;实际地址应根据网络环境替换。这里的 DNS 地址只是配置示例,不代表所有环境都能访问。

{
  "dns": {
    "hosts": {
      "dns.google": "8.8.8.8",
      "cloudflare-dns.com": "1.1.1.1"
    },
    "servers": [
      {
        "address": "223.5.5.5",
        "domains": ["geosite:cn"],
        "skipFallback": true
      },
      {
        "address": "https+local://1.1.1.1/dns-query",
        "domains": ["geosite:geolocation-!cn"],
        "expectIPs": ["geoip:!cn"]
      },
      "1.1.1.1"
    ],
    "queryStrategy": "UseIPv4"
  }
}

该片段需要合并到完整配置的顶层 dns 对象中,不能把第二个顶层 JSON 直接追加到文件末尾。若客户端或部署工具不支持把 DNS 片段合并进主配置,应手工整理成一个合法 JSON 对象。配置完成后,可以在核心日志中确认 DNS 请求是否被接收入站、是否出现 fallback,以及代理域名是否在不希望的本地解析器上出现。

在透明代理或 TUN 场景中,还需要把 DNS 查询本身送到 Xray 的 DNS 处理链。仅填写 dns.servers 不会自动接管所有设备的 53 端口。旁路由需要配合防火墙重定向、TUN 设置或专用 DNS 入站;桌面端则要确认系统 DNS、TUN DNS 和浏览器自定义 DNS 没有互相绕开。若使用 DoH,连接 DoH 服务器的流量也必须有明确的路由,否则可能因为路由循环或错误的出站选择而失败。

结论:防泄露是路径问题

检查 DNS 时不要只问“用了哪个服务器”,还要追踪“请求从哪个入站进入、由哪条规则处理、通过哪个出站离开”。只有三段路径一致,才有资格判断 DNS 策略生效。

模块化维护、变更流程与排障

长期维护时,可以按照职责把源文件分为 00-log.json10-inbounds.json20-outbounds.json30-dns.json40-routing.json。这些文件是人为管理单元,不代表 Xray 一定会按文件名自动加载。最终部署前,应通过明确的合并过程生成唯一的 config.json,并在生成后执行 JSON 语法检查和 Xray 配置检查。

/etc/xray/
├── config.json
├── fragments/
│   ├── 00-log.json
│   ├── 10-inbounds.json
│   ├── 20-outbounds.json
│   ├── 30-dns.json
│   └── 40-routing.json
└── backup/
    └── config-2026-09-05.json

每次变更只改一个职责范围。例如先修改 DNS,再单独验证域名解析;不要同时更换节点、调整路由、启用 TUN 和改写防火墙。变更前保存带日期的备份,变更后记录使用的核心版本、配置摘要、监听端口和测试结果。这样即使问题只在某些应用或某类 UDP 流量中出现,也能快速回退到上一个可用版本。

配置测试通过但端口没有监听?

先确认启动命令实际加载了目标文件,再检查 10808 或 10809 是否被其他 Xray 进程、v2rayN 实例或系统程序占用。

规则写了 geosite 却没有命中?

检查 geosite.dat 是否存在且版本匹配,再确认入站是否保留域名信息;若目标只有 IP,可考虑 IPIfNonMatch 或协议嗅探。

代理能用但 DNS 仍然泄露?

检查系统 DNS、浏览器自定义 DNS、TUN 接管和 UDP 53 转发,确认 DNS 请求没有绕过 Xray 入站直接发送到局域网网关。

改了路由后所有网站都打不开?

暂时删除新增规则,保留最后的代理兜底规则,确认基础出站可用后再逐条恢复;不要先修改 REALITY 或 UUID。

排查日志时,可以按层级阅读。出现 failed to read configinvalid characterunknown field,优先处理 JSON 结构和字段兼容性;出现 bind: address already in use,检查本地监听端口;出现 connection refusedi/o timeout,检查远端地址和网络可达性;出现 reality handshake failed 或 TLS 握手错误,则逐项核对服务器名称、公钥、shortId、指纹和服务端配置。

invalid character
unknown field
bind: address already in use
connection refused
i/o timeout
reality handshake failed
failed to find an available destination

最后进行三类验证:第一类是核心验证,使用 xray run -test 确认配置可解析;第二类是入口验证,使用 netstatss 或系统端口工具确认 10808、10809 正在监听;第三类是链路验证,让测试请求分别访问局域网地址、明确直连域名和明确代理域名。只有三类结果都符合预期,才说明模块化配置不仅能够启动,也实现了设计中的路由和 DNS 行为。

下载 v2rayN