我这台 NodeMach VPS 原来用的是关闭防火墙模式。NodeMach 更新以后,这个模式不让用了,只能在 IP 白名单和端口转发之间重新选。
我一开始选了 IP 白名单,配置好以后也能正常用。奇怪的是,过一两天又连不上了,再去改一次白名单又恢复。后来才发现,不是 VPS 有问题,而是我这边根本没有固定公网 IP。公网 IP 一变,前几天填进去的白名单自然就失效了。
看了 NodeMach 的说明以后才明白:没有固定公网 IP,或者本地网络不能直连服务器时,端口转发反而是最省事的方式。不需要追着自己的公网 IP 改白名单,只要访问 NodeMach 分配的中转地址和端口就可以。
于是我把整套配置重新走了一遍,也顺便把过程记下来。最早这篇主要写端口对应关系,后来才发现,“节点能连上、Google 能打开”和手机里的应用真正可用,还是两回事。
**2026 年 9 月 23 日补充:**原来的 VLESS + TCP 节点没有启用 TLS/REALITY,出现了 Google 能访问,但 Facebook、YouTube 手机 App 无法加载的问题。排查后,我保留旧节点,另外增加了 REALITY 加密入口,电脑测试通过,手机上的两个 App 也恢复正常。这次把漏掉的安全参数、重新导入步骤和验收方法一起补进来。
这次实际验证的服务端是 3x-ui 3.7.0、Xray 26.7.28,手机使用 iOS 的 Shadowrocket(小火箭)。手机版本当时没有记录,不能把一次成功当成所有版本的兼容保证。下面按这套已验证的 VLESS + TCP + REALITY、Flow 留空配置说明;其他版本的界面和默认值可能不同。
文中的 relay.example.com、IP、外部端口和 WebBasePath 是地址示例,实际使用时换成自己后台显示的值。UUID、公私钥、Short ID 都要使用自己生成的值,不要照抄别人的节点凭证。www.cloudflare.com 则是这次验证过的 REALITY 目标站,仍需从自己的服务器扫描确认可用。
先弄清楚两种端口#
端口转发最容易把人绕进去的地方,是页面里会同时出现两个端口。
- VPS 内部端口:服务真正监听的端口,例如 3x-ui 面板的
17211、VLESS 入站的57149。 - NodeMach 外部端口:NodeMach 随机分配给中转地址的端口,例如
35606、41349。浏览器和客户端最终访问的是这个端口。
可以把它理解成两条转发规则:
relay.example.com:35606 -> VPS:17211 (3x-ui 面板)
relay.example.com:41349 -> VPS:57149 (VLESS 入站)17211 和 57149 是服务器里面使用的端口,35606 和 41349 才是外面访问时使用的端口。NodeMach 做端口转发,3x-ui 的入站负责接收代理连接;端口转发本身不会给 VLESS 增加 TLS 或 REALITY 加密。
第一步:把 NodeMach 切换到端口转发模式#
进入 NodeMach 后台,打开服务器详情页,点击顶部第三个“零信任”标签,然后选择“端口转发”。切换模式以后,原来的白名单或转发规则可能会失效,所以最好当场把需要的规则重新检查一遍。

我先把常见的四个端口加了进去:SSH 的 22、RDP 的 3389、HTTP 的 80 和 HTTPS 的 443。

这里不用完全照抄。Linux 服务器连接 SSH 需要 22;3389 主要是 Windows 远程桌面才会用;80 和 443 则要看自己有没有 Web 服务或证书验证需求。NodeMach 左边显示的“连接地址”是外部地址,右边“指向”的才是 VPS 内部端口。
复制 SSH 那一项的连接地址和外部端口,然后在本地终端连接。比如 NodeMach 给我的中转主机是 relay.example.com,外部端口是 12336,命令就是:
ssh root@relay.example.com -p 12336这里一定要用 NodeMach 分配的外部端口,不能因为 SSH 默认是 22,就在命令后面继续写 -p 22。
第二步:安装 3x-ui,并自定义面板端口#
SSH 连接成功以后,我用 3x-ui 官方仓库 提供的一键安装命令:
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)这种命令会直接下载并执行远程脚本,地址不要从不明教程里复制,最好回到官方仓库确认后再运行。
上面是我当时使用的安装入口,它跟随仓库分支变化,不保证现在安装出来仍是本文实测的版本。安装或升级后,先在面板首页确认 3x-ui 和 Xray 的实际版本,再核对后面的兼容性说明;已经能用的服务不用为了照着文章重装。

安装时我做了三个选择:
- 数据库使用默认的 SQLite;
- 选择自定义面板端口;
- 把面板端口设置为
17211。
SQLite 对我这种单机使用已经足够。17211 也不是必须使用的固定值,换成其他未占用的高位端口同样可以,关键是后面 NodeMach 的转发规则必须指向同一个端口。
第三步:SSL 这一步不要只看“安装完成”#
这里申请的是 3x-ui 管理面板的 HTTPS 证书。它保护浏览器登录后台的连接;第五步配置的 REALITY,保护手机连接代理节点的通道。两者分别设置,面板有证书不代表 VLESS 入站已经启用加密,启用 REALITY 也不会自动让面板变成 HTTPS。
接下来安装器会询问证书方式。我当时选择的是第二项:Let's Encrypt for IP Address (6-day validity, auto-renews),也就是给服务器公网 IP 申请有效期约 6 天、自动续期的短期证书。后面的 IPv4、IPv6 等问题,我基本使用默认值继续。

不过这张图里有一个不能忽略的问题:我的证书实际上没有签发成功。终端已经显示 Timeout during connect,并提示确认外部 80 端口能够转发到验证端口。后面虽然仍然出现了 Panel Installation Complete,那只代表面板装好了,不代表 HTTPS 证书也成功了。
3x-ui 的证书说明也写得很清楚:HTTP 验证需要公网 80 端口能够访问服务器。NodeMach 即使把某个随机外部端口转发到 VPS 的 80,也不一定等于公网 IP 的 80 已经打通,所以在端口转发模式下很容易卡在这里。
如果屏幕上明确显示证书签发成功,可以按实际配置使用 HTTPS。如果和我图里一样失败了,就不要看到示例 Access URL 后自己强行加上 https://。面板只有 HTTP 时,我更建议先通过 SSH 隧道登录,避免把登录信息直接经公网 HTTP 发送。
例如 SSH 的外部端口是 12336、面板内部端口是 17211,可以在本地电脑运行:
ssh -N -L 127.0.0.1:17212:127.0.0.1:17211 root@relay.example.com -p 12336保持这条 SSH 连接,再在电脑浏览器打开 http://127.0.0.1:17212/自己的WebBasePath/。这里的 17212 是电脑上的空闲本地端口,17211 是服务器上的面板端口,12336 是 NodeMach 提供的 SSH 外部端口。需要长期直接用域名登录面板时,再配置与该域名匹配的 HTTPS,例如自己的域名配合 DNS 验证或反向代理。
另外,即使裸 IP 证书申请成功,最后访问的却是 NodeMach 中转域名,浏览器仍可能因为证书名称和访问域名不一致而报警。这是端口通了和证书匹配完全不同的两件事。
安装结束以后,终端会给出 Username、Password、Port、WebBasePath 和 Access URL。这些信息要保存好,尤其不要公开密码、WebBasePath 和 API Token。
第四步:给 3x-ui 面板增加一条转发#
安装器显示的地址通常类似这样:
WebBasePath: N1rk2ct8ArryUuuil
Access URL: https://203.0.113.10:17211/N1rk2ct8ArryUuuil但在 NodeMach 的端口转发模式下,直接访问 服务器 IP:17211 会被拒绝。现在回到 NodeMach 后台,新建一条规则,让它指向 VPS 内部的 17211 端口。

假设 NodeMach 给出的连接地址是 relay.example.com:35606,把它和刚才的 WebBasePath 拼起来,面板地址就是:
http://relay.example.com:35606/N1rk2ct8ArryUuuil如果证书已经正确配置并且与访问域名匹配,再把协议写成 https://。这里替换的是主机和端口,末尾的 WebBasePath 不能丢。
上面的公网 HTTP 地址只是说明拼接方法;证书未配置好时,实际登录使用上一节的 SSH 隧道。如果一直通过 SSH 隧道管理面板,就不需要额外开放这条面板公网映射。手机使用的 VLESS 入站映射仍然需要。
第五步:创建 VLESS + TCP + REALITY 入站#
打开 3x-ui 面板,进入“入站”,点击“添加入站”。如果是在升级原来的节点,先备份配置;下面这些步骤也可以用来编辑已有入站。

这张旧截图只展示了基础配置,不能填完端口就直接当作完成。还要继续设置协议、客户端和“安全”里的 REALITY。
基础、协议和传输怎么填#
| 位置 / 参数 | 本文使用的值 | 说明 |
|---|---|---|
| 启用 | 开启 | 保存后入站应处于启用状态 |
| 备注 | 自己能认出的名称,例如 NodeMach-REALITY | 只用于识别节点 |
| 协议 | VLESS | 手机端也使用 VLESS |
| 监听地址 | 留空 | 本文让入站监听服务器可用地址;不要误填中转域名 |
| 端口 | 57149 | 示例内部监听端口,要未被其他服务占用 |
| 解密 / decryption | none | 保持这个字符串,不是留空 |
| 加密 / encryption | none | 本文没有启用 VLESS 自身的另一套加密 |
| 协议页“生成密钥” | 不操作 | REALITY 的公私钥在下面“安全”页生成 |
| 传输 | TCP | 本文不用 WebSocket、gRPC 等其他传输 |
| TCP 头部伪装 | none / 无 | 不额外配置 HTTP 伪装头 |
| 客户端 UUID | 使用面板生成的 UUID | 手机导入时必须与它一致 |
| 客户端 Flow | 留空 | 本次没有使用 xtls-rprx-vision,服务端和手机保持一致 |
| Vision testseed | 保持默认,不用调整 | 本文 Flow 留空,这一项不参与当前方案 |
| 嗅探 | 本次关闭 | 不把开启嗅探当成修复 App 故障的必要条件 |
在 3x-ui 的“客户端”里创建并启用一个客户端,确认它关联到这个入站。自用时可以把流量和到期时间设成界面中的“无限制”;已有客户端则先检查是否被禁用、到期或用尽流量。
这里有一个容易搞混的地方:**VLESS 的 encryption=none 可以和 REALITY 同时存在。**前者表示没有开启 VLESS 协议自身的加密,后者在传输层提供保护。原来的问题配置是两边都没有开启,不能因为第一张参数表里仍写 none,就把后面的 REALITY 也关掉。Xray 对 VLESS 传输保护的说明区分了这两种情况。
“安全”页的 REALITY 参数怎么填#
安全类型选择 Reality,按下面填写。这里列的是这次成功的配置,不需要把所有高级功能都打开。
| 参数 | 本次填写方式 | 说明 |
|---|---|---|
| 安全 | Reality | 不选“无”,也不同时再选另一套 TLS 配置 |
| 显示 / Show | 关闭 | 不开启 REALITY 调试输出 |
| Xver | 0 | 本文不使用 PROXY protocol |
| uTLS / 指纹 | chrome | 这是供客户端使用的指纹,手机导入后也核对为 chrome |
| 目标 / Target | www.cloudflare.com:443 | 是 REALITY 目标站及其端口,不是自己的中转地址或入站端口 |
| SNI | www.cloudflare.com | 只写域名,不带 https://、端口或路径 |
| 最大时间差(ms) | 0 | 本次未限制该时间差,不能理解成只允许 0 毫秒误差 |
| 最小客户端版本 | 本次显式填 0.0.0 | 仅对应下文说明的 Xray 26.7.28 与第三方客户端兼容处理 |
| 最大客户端版本 | 留空 | 本次不设置上限 |
| Short IDs | 使用面板生成的非空值 | 可保留多个;手机使用其中一个,值必须完全一致 |
| SpiderX | / | 这是本次实测值;面板已生成以 / 开头的路径,也可以保留 |
| 公钥 / 私钥 | 在 REALITY 区域点击“获取新证书”生成一对 | 在这个界面中,该按钮生成的是 REALITY 密钥对,不是申请面板 HTTPS 证书 |
| mldsa65 Seed / Verify | 留空 | 本次没有启用这项可选功能,不点击“获取新 Seed” |
| 主密钥日志 / Master key log | 留空 | 不配置密钥日志文件 |
Short ID 可以让面板自动生成。若自行填写,使用偶数长度的十六进制字符串,最多 16 位;不用把一串多个 ID 原样粘到手机的单个 Short ID 输入框。公私钥要成对生成,不能各复制一份不同来源的值。私钥只留在服务器,手机配置使用公钥。
填写目标后,点击旁边的“扫描”,确认服务器到目标站的检测通过。这次 www.cloudflare.com:443 的 TLS 1.3、h2、密钥交换能力和证书检查都通过了;如果自己的扫描不通过,先处理目标可达性或重新选择可用目标,不能把我这次的结果当作永久保证。
扫描可能带回多个 SNI,例如 www.cloudflare.com 和 timeline.www.cloudflare.com。这不代表必须全部保留。为与本文的已验证配置一致,我只保留 www.cloudflare.com。扫描后再核对一遍表格,尤其是版本限制、公私钥和 Short IDs;扫描成功只是目标站通过检测,不代表手机已经连接成功。
为什么这次最小客户端版本填 0.0.0#
这个地方不能只看灰色提示文字。Xray 26.7.28 的源码中,minClientVer 留空会采用 26.3.27 的最低版本门槛,并提示其他客户端可能被拒绝。它不是要求在这里填写 Shadowrocket 的 App 版本号。为了兼容这次手机上的小火箭,我显式填写了 0.0.0,并最终通过手机实测。
这是放宽最低客户端版本检查的兼容性取舍,不是所有节点都应该照抄的安全默认值;该版本源码也提示,修改最低版本限制会增加被识别和封锁的风险。如果自己的客户端能满足默认要求,可以保留默认;使用其他 Xray 或客户端版本时,应重新核对兼容性,不能只靠改成 0.0.0 保证能用。具体行为可以查看 Xray 26.7.28 的 REALITY 配置源码。
全部填好后保存,确认 Xray 正常运行、入站启用。保存以后,列表里的 57149 仍然只是 VPS 内部监听端口,手机最终使用的是下一步配置的外部中转端口。

57149 要不要改成 443#
不需要因为启用 REALITY 就强制改端口。57149 可以继续作为内部监听端口,只要未被占用、NodeMach 映射正确,客户端也用对应的外部端口。
9 月 23 日排查时,我为了保留旧节点,新建了内部 443 的 REALITY 入站,复用已有的 32438 → 443 映射,最终手机验证成功。这是一条独立测试入口,不是说 57149 被废弃,也不是说 REALITY 只能用 443。如果直接把原入站改成 REALITY,可以保留原来的端口映射,但所有使用该入站的客户端都要重新导入匹配的配置,并重新测试原入口是否可用。
第六步:转发 VLESS 端口,并在 3x-ui 中添加主机#
再次回到 NodeMach 后台,新建一条 TCP 端口转发规则,目标端口填写刚才的 57149。如果原来就有指向它的规则,可以继续使用。保存以后,记录 NodeMach 给出的外部连接地址和端口。

假设这次得到的是:
relay.example.com:41349回到 3x-ui,打开左侧的“主机”页面,点击“添加主机”:
| 参数 | 填写方式 |
|---|---|
| 启用 | 开启 |
| 备注 | 自己能认出的名称,例如 NodeMach-REALITY-中转 |
| 端点 | relay.example.com:41349,替换成自己的中转域名和外部端口,不加 http:// 或 https:// |
| 入站 | 选择刚配置 REALITY、监听 57149 的入站 |
| 安全 | same,继承入站的 REALITY 设置 |
保存后确认这条主机记录确实绑定到新配置的入站。如果同时保留旧节点和新节点,就分别添加主机记录,不要把新端点错绑到旧入站。

**“主机”页面负责生成分享地址,不负责执行网络转发。**真正把外部连接送到服务器的是 NodeMach 的转发规则。这里告诉 3x-ui:服务虽然在本机监听 57149,但分享给客户端时应使用 relay.example.com:41349,并继承 REALITY 参数。
如果不添加主机,面板生成的订阅或二维码可能仍使用服务器地址或内部端口,需要手工修正。我排查时给手机生成的测试二维码已经手动写入正确的外部端点,所以当时没有新增“主机”记录也能连接;但 NodeMach 后台的端口映射一直存在。以后想直接使用面板的二维码和订阅,把这里补齐更方便。
第七步:重新导入小火箭,核对节点参数#
完成保存后,找到关联这个入站的客户端,重新生成二维码或分享链接,再导入 Shadowrocket。**给旧入站加了 REALITY,不能继续使用手机里原来那份没有 REALITY 参数的配置。**只改服务器、不更新手机,连接会失败。

上面是旧版端口核对截图,现在还要检查 REALITY 信息。导入后的节点应与下面一致:
| 小火箭里的项目 | 应当是什么 |
|---|---|
| 类型 | VLESS |
| 服务器 | 自己的 NodeMach 中转域名,本文示例为 relay.example.com;不是 www.cloudflare.com |
| 端口 | NodeMach 外部端口,本文示例为 41349,不是内部 57149,也不是目标站的 443 |
| UUID | 与关联入站的客户端 UUID 一致 |
| 加密 | none |
| 传输 | TCP |
| Flow | 留空,与服务端一致 |
| 传输安全 | REALITY;有些版本把入口放在 TLS 设置内,应确认是 REALITY 模式和参数,而不是普通 TLS |
| SNI / Server Name | www.cloudflare.com |
| 指纹 / uTLS | chrome |
| 公钥 / Public Key | 服务器 REALITY 页生成的公钥,不是私钥 |
| Short ID | 服务器 Short IDs 列表中的一个有效值,不是整个列表 |
| SpiderX | 与导出配置一致;按本文设置为 / |
不同版本的客户端字段名称可能不同,以导入结果和服务端对应关系为准。不要为了消除报错去关闭证书校验,也不要给 REALITY 另外安装一张所谓的“信任证书”。
以后如果重新生成了公私钥、修改了 Short ID、SNI、UUID 或入口地址,手机上的配置也要同步更新。首次导入时给新节点起一个容易区分的名字,并确认首页真正选中的是它。
第八步:网页和手机 App 都要实际测试#
第一次验证时,先把小火箭的“全局路由”临时设成“代理”,选中刚导入的节点,断开再连接,然后彻底退出并重新打开要测试的 App。这样可以先减少分流规则的干扰;确认节点可用后,再切回日常配置模式复测。
至少检查 Google 页面能否加载、Facebook App 能否刷新内容、YouTube App 能否加载并播放视频。根据自己的使用场景,再分别测试 Wi-Fi 和蜂窝网络。这是建议的验收清单,不代表我已经对所有网络和功能做过长期测试。
节点延迟有数字、面板显示在线、Google 能打开,都只是部分证据,不能代替 App 实际测试。浏览器与 App 使用的接口和连接方式可能不同,不能因为网页能用就断定剩下的必然是手机问题。
这次故障查到了什么,还不能下什么结论#
这次旧节点的端口对应关系是正确的,Xray 也在运行。它的 VLESS 加密和 TLS/REALITY 都没有开启。电脑从物理网络直连旧入口时,Facebook、YouTube 接口在 TLS 握手阶段出现连接重置;访问日志同时显示,请求已经到达 Xray,并选择了正常的 direct 出站。这里的 direct 指服务器直接访问目标网站,不代表手机绕过了代理。
相同旧节点经另一条 VPN 路径连接时,同样的接口能够返回响应。改用新的 REALITY 入口后,电脑从原物理网络完成了五个 HTTPS 检查,手机也确认 Facebook、YouTube 两个 App 都能用了。测试过程中没有更换服务器静态 IP,也没有升级或降级这台服务器的 Xray。
这支持“故障与原入口的接入路径或传输方式有关”,但新入口同时改变了端口和传输安全,不能据此宣称唯一原因一定是没开 REALITY,更不能认定具体是哪家运营商或哪一段中转在重置连接。原来没有外层加密,也不等于网站自身的 HTTPS 内容全部是明文。
我还做过关闭手机 IPv6 的对照,当时没有改善这次故障。因此,这篇不把“关 IPv6”“关 UDP”写成人人都要照做的步骤。UDP 的测试回包也不能代替完整视频播放或所有 App 功能的验证。
如果按步骤配置后仍然不能用,我会依次核对:
- 手机真正选中的节点、外部域名和端口,是否对应当前 NodeMach 规则。
- NodeMach 的 TCP 规则是否指向正确的内部端口,切换安全模式后规则是否仍生效。
- Xray 是否运行正常、入站与客户端是否启用,流量和有效期是否允许继续连接。
- 手机与服务端的 REALITY 公钥、SNI、Short ID、UUID、Flow 是否匹配,修改后是否重新导入。
- REALITY 目标扫描是否通过,实际 Xray 与客户端版本是否符合当前兼容条件。
- 临时全局“代理”和日常“配置”模式的结果是否不同,再结合连接日志判断是否需要检查分流、DNS、IPv6 或 UDP。
需要临时开访问日志时,测试完恢复原设置,不把含客户端信息的完整日志公开发到博客。
这篇更新后,我会把三件事分别检查:**外部端口是否正确转发、代理传输是否完整配置、手机应用是否实际可用。**面板 HTTPS 也单独检查,不再把“安装完成”或“Google 能打开”当成整个流程的终点。
配置核对参考#
- 3x-ui 官方仓库:安装与面板项目说明。
- Xray VLESS 入站说明:VLESS 配置与传输保护的区别。
- Xray 26.7.28 REALITY 配置源码:本文涉及的最低版本默认值、Short ID 和密钥校验行为,以这次实测版本为准。
READERS' DESK
读者评论
正在读取评论...