订阅 URL 是获取入口,base64 是文本编码,分享链接通常描述一个节点,Xray 原生 JSON 则描述内核运行配置。本文适合遇到“链接能打开却导入失败”“JSON 不能添加到订阅”等问题的用户,按识别格式、选择导入入口、离线解码和检查字段四个步骤,说明哪些转换可以直接完成,哪些必须补齐配置。
一、先区分订阅地址、响应正文与节点配置
复制到剪贴板的内容,并不一定就是节点。以 https:// 开头的订阅地址指向一个资源;客户端请求这个地址后,才得到实际正文。正文可能是逐行排列的分享链接,也可能是这些行经过 base64 编码后的结果,还可能是另一种配置文档。仅凭地址后缀,不能判定它的格式。
| 内容 | 常见外观 | 实际作用 |
|---|---|---|
| 订阅 URL | https:// 开头,可能带访问令牌 | 用于获取及更新远端列表,不直接说明节点协议 |
| base64 订阅正文 | 长串可打印字符,末尾可能有 = | 通常封装多行分享链接,需要解码后识别内容 |
| 单节点分享链接 | vmess://、vless://、trojan:// 或 ss:// | 描述服务器、认证及部分传输参数 |
| Xray 原生 JSON | 常见顶层字段为 inbounds、outbounds、routing | 定义内核的监听入口、出站及流量处理规则 |
base64 不提供加密保护。拿到正文的人通常可以还原节点地址与认证字段;订阅 URL 中的令牌也可能具有同等访问能力。排障时不要把完整订阅地址、二维码或节点导出文本发到公开讨论区。截图应同时遮住路径、查询参数和节点认证值。
二、base64 订阅与 VMess 的编码层次
常见的传统订阅做法,是先把多个分享链接用换行符连接,再对整段 UTF-8 文本进行 base64 编码。解码一次后,应看到一行一个节点。行尾可能使用 LF 或 CRLF,导入器通常会忽略空行,但不能依赖所有版本都能处理多余空格或字节顺序标记。
- 外层:订阅正文整体的 base64,解码后得到多行 URI。
- 节点层:常见
vmess://格式的后半部分本身也是 base64,解码后得到节点描述 JSON。 - 字段层:VLESS 等 URI 的备注、路径和查询参数可能使用百分号编码,例如
%20表示空格。
因此,一个 VMess 节点可能涉及两次 base64 解码,但两个层次的对象不同。不能看到长字符串就反复解码到“看起来像文字”为止。遇到 vless:// 后,主体通常应按 URI 解析,而不是把整个后缀继续当作 base64。
常见 VMess 分享 JSON 使用 v、ps、add、port、id、net、tls 等字段。其中 v 常见值 "2" 是这类分享格式的版本标识,不是客户端或内核版本。它也不是可以直接交给 Xray 运行的原生配置。
- 标准 base64 使用
+和/;URL 安全变体可能使用-和_。 - 末尾的
=是填充符,存在或省略都不能单独证明正文有效。 - 节点备注含中文时,需要按 UTF-8 解码;直接把
atob()的返回值当作 Unicode 文本,可能产生乱码。
三、分享链接可以还原哪些字段
下面使用保留示例域名和全零 UUID 展示 VLESS URI。它只用于说明结构,不能建立实际连接。端口 443 是示例远端端口,与客户端本地监听端口不是同一概念。
vless://[email protected]:443?encryption=none&security=tls&type=tcp&sni=node.example.invalid#TLS%20example
| URI 部分 | 配置位置 | 检查重点 |
|---|---|---|
| 主机与端口 | settings.vnext[].address、port | 端口应转换为数值,不要带入备注文字 |
| 用户信息中的 UUID | settings.vnext[].users[].id | 必须与服务端配置一致 |
encryption=none | users[].encryption | 不表示传输层没有 TLS |
security=tls | streamSettings.security | 与 TLS、REALITY 等传输安全方式区分 |
sni | tlsSettings.serverName | 用于 TLS 服务名称,不应随意替换 |
# 后的片段 | 客户端节点备注 | 通常不参与认证或网络连接 |
转换复杂传输时,至少还要核对 flow、alpn、传输路径、HTTP Host 和相应安全参数。不同分享格式与客户端版本对参数名的约定可能不同。不能因为地址、端口和 UUID 相同,就认为两个配置等价。
转换判断:导入后先核对传输参数
将原链接与节点编辑页逐项对照,先检查传输类型、TLS 服务名称和 flow,再做连接测试;缺失这些字段时,重新编码订阅正文不能修复连接。
四、Xray 原生 JSON 不是节点列表
Xray 原生 JSON 描述的是运行环境。inbounds 决定本地接收流量的方式,outbounds 定义连接远端的方式,routing 可以把不同请求送往不同出站。下面的最小示例对应上一节的 VLESS 节点,设置本地 SOCKS 监听为 127.0.0.1:10808;所有远端参数仍为不可连接的演示值。
{
"log": {
"loglevel": "warning"
},
"inbounds": [
{
"tag": "local-socks",
"listen": "127.0.0.1",
"port": 10808,
"protocol": "socks",
"settings": {
"auth": "noauth",
"udp": true
}
}
],
"outbounds": [
{
"tag": "proxy",
"protocol": "vless",
"settings": {
"vnext": [
{
"address": "node.example.invalid",
"port": 443,
"users": [
{
"id": "00000000-0000-0000-0000-000000000000",
"encryption": "none"
}
]
}
]
},
"streamSettings": {
"network": "tcp",
"security": "tls",
"tlsSettings": {
"serverName": "node.example.invalid"
}
}
}
]
}
这个示例没有显式配置 routing 和 dns,只展示单入口、单出站的结构。监听地址限制为回环地址,不向局域网开放;udp: true 只是允许该 SOCKS 入站处理相应请求,并不保证应用、远端和网络路径都具备可用的 UDP 通路。
转换边界:提取节点与保留运行配置
分享链接转原生 JSON
- 解析地址、端口、认证和传输参数。
- 补充本地入站与监听端口。
- 按实际需求配置 DNS、日志与路由。
原生 JSON 转分享链接
- 选择一个能够表达为 URI 的代理出站。
- 核对当前分享格式能否承载全部传输字段。
- 另行保留入站、路由和 DNS 配置备份。
单节点 URI 通常无法承载整份运行配置;多出站、链式连接和路由规则不能默认随节点一起迁移。
五、v2rayN、v2rayNG 与 v2flyNG 的导入选择
格式能够被解析,与所用内核能够运行该协议,是两项独立条件。v2rayN 是桌面客户端,v2rayNG 使用 Xray 内核,v2flyNG 使用 v2fly 内核。导入前应同时确认客户端入口、内核类型和所需传输能力,不要把某个客户端能保存的配置,直接视为另一个客户端也能运行。
订阅地址:用于持续维护节点列表
推荐在 v2rayN 的「订阅分组」→「订阅分组设置」中添加地址,再执行「更新全部订阅(不通过代理)」或对应的通过代理更新操作。菜单文案随版本可能略有调整,应以实际界面为准。
适合:节点会变更,需要保留后续更新入口。
分享链接:用于导入单个节点
在 v2rayN 中可使用「服务器」→「从剪贴板导入批量 URL」,常见快捷键为 Ctrl+V。v2rayNG 的新增菜单提供「从剪贴板导入」;v2flyNG 应使用其分享链接导入入口,并确认内核支持对应协议及传输。
适合:临时测试、单节点迁移、逐项核对参数。
原生 JSON:用于保留完整运行设置
使用客户端提供的自定义配置导入入口,而不是订阅地址输入框。v2rayNG 可使用自定义配置导入功能;v2rayN 应通过自定义配置服务器入口处理。v2flyNG 需要与其 v2fly 内核兼容的配置,不能直接套用 Xray 专用字段。
适合:需要固定入站、DNS 或复杂路由,且能自行维护配置。
把订阅内容解码后再手工导入,通常只是生成节点快照,不会自动保留原订阅的更新关系。自定义 JSON 也不等同于界面管理的普通节点:系统代理开关指向的端口,必须与实际入站一致。上例只监听 SOCKS 10808,不能假定另有 HTTP 10809 可用。
六、离线解码与重新编码的具体步骤
如果只是检查编码,可在本地空白页面的浏览器开发者工具控制台中处理,不必把订阅提交到在线转换服务。下面仅使用浏览器内置 API,不发送网络请求。先用固定文本测试,确认环境支持 TextEncoder 和 TextDecoder,再在可信本地环境处理已获取的正文;不要在不熟悉的网页控制台粘贴真实订阅。
function decodeBase64Text(input) {
const normalized = input.trim()
.replace(/\s+/g, "")
.replace(/-/g, "+")
.replace(/_/g, "/");
if (normalized.length % 4 === 1) {
throw new Error("base64 长度无效");
}
const padded = normalized
+ "=".repeat((4 - normalized.length % 4) % 4);
const bytes = Uint8Array.from(
atob(padded),
character => character.charCodeAt(0)
);
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
}
function encodeBase64Text(text) {
const bytes = new TextEncoder().encode(text);
const parts = [];
for (const byte of bytes) {
parts.push(String.fromCharCode(byte));
}
return btoa(parts.join(""));
}
decodeBase64Text("5rWL6K+V"); // 测试
encodeBase64Text("测试"); // 5rWL6K+V
- 先识别:若正文已是逐行 URI,不需要外层解码;若开头是 JSON 对象,先检查字段结构。
- 只解一层:对确认属于 base64 的正文调用解码函数,检查结果是否为节点行,而不是错误页面。
- 保留节点原文:不要对整条 URI 反复调用百分号解码。路径中的保留字符可能因此改变含义。
- 按行整理:移除纯空行,保留每条分享链接内部字符;重新编码时使用换行符连接各节点。
- 验证导入:先用单个节点测试,再处理整个列表。只有编码错误时,重新编码才是对应修复方法。
上述函数只完成编码和解码,不会把 VMess 转成 VLESS,也不会把原生 JSON 自动变成订阅。协议转换涉及服务端是否提供相应入口,无法靠修改字符串前缀完成。同样,base64 包裹一份原生 JSON 后,它仍然不是常见的“逐行节点 URI”订阅。
七、导入失败与转换丢字段的排查
订阅地址能打开,为什么客户端说没有节点?
检查实际响应正文,而不是只看状态码。若内容是登录表单、访问限制说明或 HTML 页面,应先修复订阅访问权限。若是原生 JSON,则改用自定义配置入口,不要继续在订阅框中重复添加。
解码后还是 vmess://,是不是解码失败?
这通常说明外层订阅已经正确解开。先把整条 VMess 链接交给客户端导入。只有需要检查字段时,才单独解码它后面的节点描述,且不要把得到的分享 JSON 当成完整内核配置。
节点导入成功,为什么仍然连接不上?
进入节点编辑页核对远端端口、UUID、传输类型、TLS 服务名称及 flow,再查看内核日志。导入成功只能说明解析器接受了数据,不能证明远端可达、认证有效或本地代理已被应用使用。
转换后分流规则和 DNS 怎么不见了?
单节点分享通常只包含代理出站参数。回到原始 JSON 备份,分别迁移 routing、dns 和所需入站。不要通过增加节点备注来携带这些运行规则,客户端不会据此恢复完整配置。
通过代理更新订阅也超时,要先改格式吗?
先判断请求是否真正完成。已有可用节点时,可在 v2rayN「订阅分组」中尝试对应的通过代理更新操作;没有可用连接时,应先检查本地网络与订阅地址权限。请求超时发生在解析正文之前,改 base64 编码不能解决。
最终验收要分三步:订阅是否成功获取、节点参数是否被完整解析、流量是否进入预期出站。修改格式后先保留原始备份;对于自定义 JSON,尤其要确认监听地址和端口没有发生变化,再验证应用的代理设置。
- 需要自动更新:保留订阅 URL,并在订阅分组里管理。
- 只迁移节点:优先使用目标客户端支持的原始分享链接。
- 需要完整行为:迁移并检查原生配置,逐项验证路由与 DNS,而非只比较节点数量。