订阅地址是获取配置的入口,Base64 是文本编码,分享链接描述节点,原生 JSON 则可能描述整套内核运行配置。本文适合遇到“订阅能打开却导入失败”或需要迁移节点的用户:先辨认响应内容,再在本地解码,最后核对协议字段。转换可以迁移节点参数,但不能默认保留 DNS、路由与更新关系。
先分清订阅地址、响应正文与节点配置
形如 https://sub.example.com/list?token=demo 的地址,只负责让客户端请求内容,不能单凭网址判断返回格式。本文域名、UUID 和参数均为结构示例,不提供可用连接。实际订阅地址中的令牌通常具有访问权限,分享地址就可能泄露整组节点。
传统 V2Ray 订阅常把多条分享链接按行连接,再对整段文本进行 Base64 编码。也有服务直接返回未编码的链接列表,或按客户端请求返回不同内容。因此,浏览器打开的结果和客户端更新时取得的结果不一定相同。
订阅地址与链接列表
推荐日常批量维护优先保留原订阅地址。服务端可以更新列表,客户端按分组拉取;正文可能是 Base64,也可能是明文链接。
适合:需要持续更新节点、在桌面与安卓端分别导入
单节点分享链接
常见前缀为 vmess:// 或 vless://。链接主要承载节点连接参数,不等于完整运行配置。
适合:临时传递单个节点、排查某条记录的字段
内核原生 JSON
可包含入站、出站、DNS 与路由。文件能否运行取决于目标内核及其配置结构,而不是扩展名是否为 JSON。
适合:明确需要自定义入站、出站与路由的配置维护者
v2rayN 与 v2rayNG 分别从哪里导入
两个客户端都能管理常见的 VMess、VLESS 分享链接及传统链接订阅,但“识别文本”不代表目标内核一定支持其中全部参数。界面名称会随版本变化,以下按功能入口区分,避免把订阅 URL 粘贴到单节点字段。
| 输入内容 | v2rayN 桌面端 | v2rayNG 安卓端 |
|---|---|---|
| HTTPS 订阅地址 | 「订阅分组」→「订阅分组设置」,新建分组并填写地址,再更新该分组 | 「订阅分组设置」中添加地址,返回列表后执行「更新订阅」 |
| 明文分享链接 | 复制完整链接,使用「从剪贴板导入批量 URL」一类入口 | 右上角「+」→「从剪贴板导入」 |
| 整段 Base64 文本 | 优先由订阅更新流程解析;手动迁移时先解码,再导入链接列表 | 优先使用订阅分组;手动导入若未识别,可先解码为分享链接 |
| 内核原生 JSON | 使用自定义配置相关入口,并选择匹配内核;不能当作普通订阅地址 | 使用自定义配置导入入口;支持范围受所用 Xray 内核与客户端版本约束 |
v2rayNG 使用 Xray 内核;v2flyNG 使用 V2Fly 内核。即使某份 JSON 语法正确,也不能据此认定两个内核都支持其中的安全机制或配置字段。尤其涉及 VLESS、REALITY 等组合时,应先确认目标内核能力,再讨论格式转换。格式转换不能增加协议支持。
Base64 订阅解码:先检查外层,再看每行
第一步是确认拿到响应正文,而不是下载错误页面。正文若以 <!DOCTYPE html>、登录提示或接口错误开头,就不是等待解码的订阅内容。只改文件后缀、反复导入,无法解决鉴权过期或服务端返回错误的问题。
- 把响应正文保存为本地文本,保留原件。删除复制时带入的说明文字,不要把 URL 当作 Base64 正文。
- 若正文已经以
vmess://或vless://开头,先按明文链接列表处理,不再解码整个正文。 - 若正文是一长段编码字符,进行一次外层 Base64 解码,再按换行拆分,清除空行。
- 逐行检查协议前缀。某行是
vmess://时,其后还可能有一层 VMess 分享信息编码;这不代表整个订阅需要无限重复解码。
下面代码仅用于在本地空白页面的开发者工具控制台中处理已取得的文本,不会请求订阅地址。先执行函数定义,再用 decodeSubscription(prompt("粘贴订阅响应正文")) 调用。不要在不可信网页的控制台处理真实凭据。
function decodeSubscription(input) {
const raw = String(input ?? "").trim();
if (!raw) throw new Error("正文为空");
if (/^(vmess|vless):\/\//i.test(raw)) return raw;
let encoded = raw.replace(/\s/g, "")
.replace(/-/g, "+").replace(/_/g, "/");
if (!/^[A-Za-z0-9+/]*={0,2}$/.test(encoded)) {
throw new Error("内容不符合 Base64 字符规则");
}
encoded = encoded.replace(/=+$/, "");
if (encoded.length % 4 === 1) {
throw new Error("编码长度异常,请检查是否复制完整");
}
encoded += "=".repeat((4 - encoded.length % 4) % 4);
const bytes = Uint8Array.from(atob(encoded), c => c.charCodeAt(0));
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
}
分享链接生成 Base64:按行连接再编码
明文列表转成传统 Base64 订阅正文,操作顺序是“每行一条链接 → UTF-8 字节 → Base64”。这只生成静态正文,并不会自动创建一个可更新的订阅服务。把结果保存到本地文件,也不等于客户端已经拥有可访问的订阅 URL。
const link = "vless://[email protected]:443?encryption=none&security=tls&type=tcp&sni=node.example.com#Example-TLS";
function encodeSubscription(links) {
const text = links.map(item => item.trim()).filter(Boolean).join("\n");
const bytes = new TextEncoder().encode(text);
let binary = "";
for (const byte of bytes) binary += String.fromCharCode(byte);
return btoa(binary);
}
const encoded = encodeSubscription([link]);
console.log(encoded);
console.log(decodeSubscription(encoded) === link);
中文备注也会进入编码内容,所以不宜直接对含中文的字符串调用 btoa。先使用 TextEncoder 转为 UTF-8 字节,可避免字符超出范围。最后一行的往返比较应得到 true;它验证编码过程没有改变示例文本,不验证节点是否可连接。
以上数字是协议编码规则和配置示例,不是测速结果,也不是所有客户端版本的默认值。Base64 不是压缩;忽略尾部填充时,编码后的长度约为原字节数的四分之三的倒数,即约增加三分之一。
分享链接与原生 JSON:字段映射不等于完整迁移
以刚才的 VLESS 链接为例,主机与端口进入出站服务器设置,UUID 进入用户设置,security=tls 对应 TLS,sni 对应 TLS 的服务器名称。下面是 Xray 风格的单个出站对象,不是可直接运行的完整配置文件,需要放入完整配置的 outbounds 数组。
{
"tag": "proxy",
"protocol": "vless",
"settings": {
"vnext": [
{
"address": "node.example.com",
"port": 443,
"users": [
{
"id": "11111111-1111-4111-8111-111111111111",
"encryption": "none"
}
]
}
]
},
"streamSettings": {
"network": "tcp",
"security": "tls",
"tlsSettings": {
"serverName": "node.example.com"
}
}
}
若要构成运行配置,还要明确本地入站、出站选择及必要的路由设置。例如假设本地 SOCKS 监听为 127.0.0.1:10808,调用它的应用也必须填写这个地址与端口。远端 443 不能填成本地代理端口;两者分别位于链路两端。
| 分享链接内容 | 原生配置对应位置 | 容易遗漏的点 |
|---|---|---|
| 主机、端口、UUID | settings.vnext 中的服务器与用户 | 端口为数字,UUID 不应含首尾空格 |
type、传输参数 | streamSettings | WebSocket 的路径与 Host 不能只迁移其中一个 |
security、sni | TLS 或相应安全设置 | 服务器地址与 SNI 可以不同,不能擅自统一 |
flow 与 REALITY 参数 | 用户字段及相应安全设置 | 需保留服务端要求的 flow、公钥、short ID 等信息 |
# 后的备注 | 客户端显示名称或自行维护的标记 | 备注不是鉴权信息,也不自动变成路由规则 |
VMess 常见分享形式则是 vmess:// 后接 Base64 编码的分享对象。对象里的 add、port、id、net、path 等字段属于分享约定,不是内核原生配置的顶层字段。把解码出的对象直接保存为运行配置,通常会因结构不匹配而失败。
两种迁移目标:节点参数与完整配置分开处理
只迁移节点
- 优先从客户端导出分享链接,再导入目标客户端。
- 核对 UUID、传输、TLS、SNI 与路径等连接字段。
- 在目标客户端重新设置系统代理、DNS 与路由。
保留完整运行配置
- 保留 JSON 中的入站、出站标签与路由引用关系。
- 确认目标内核支持全部字段,检查本地监听端口是否冲突。
- 通过自定义配置入口导入,不强行压缩成单节点链接。
原生 JSON 转回分享链接通常只能提取部分出站;多出站链路、DNS 与路由规则没有通用的单链接无损表达方式。
转换后验证:更新成功与连接成功分开看
订阅更新成功只说明客户端取得并解析了部分或全部内容,不代表所有节点都可用。建议保留原订阅分组,把转换结果导入单独的测试分组;先验证一条,再批量处理。手动导出的静态链接也不会继承原订阅的更新关系。
- 核对记录:比较转换前后的节点数量,检查是否有空行、重复记录或不支持的协议被跳过。
- 核对字段:打开节点编辑界面,检查地址、端口、UUID、传输、路径、SNI 和安全机制;不要只看备注相同。
- 核对运行:选中测试节点并查看内核日志。配置解析错误、鉴权失败与连接超时应分别处理。
- 核对应用接入:确认系统代理或应用代理指向实际本地监听端口,再检查路由是否把测试请求送入预期出站。
解码后还是一大串字符,要再解一次吗?
先检查是否已出现 vmess://。若有,只按 VMess 分享格式处理该行后半段,不要继续解码整个列表。若没有协议前缀,也没有可识别的 JSON 结构,先确认原响应是否完整以及服务端实际返回格式。
JSON 能打开,为什么客户端提示导入失败?
检查顶层字段。只有 add、id 等字段的对象可能是 VMess 分享信息;含 inbounds、outbounds 的才可能是内核运行配置。后者应走自定义配置入口,并继续核对目标内核的字段支持。
同一订阅在桌面能更新,安卓却导入为空?
先在两端核对订阅地址是否完整,再检查返回内容是否因客户端请求而不同。若响应有效,比较协议和传输支持;可把一条明文链接通过「+」→「从剪贴板导入」单独测试,缩小到某条记录,而不是反复转换全部订阅。
能把整个 JSON 转成一条 VLESS 链接吗?
只有当目标是一个可表达的 VLESS 出站时,才适合提取对应字段生成链接。先定位 protocol 为 vless 的出站,保留其连接参数;入站、DNS、路由与其他出站仍需另行保存,不能承诺一次转换全部保留。