Base64 是编码,订阅是节点集合的获取方式,原生 JSON 是内核运行配置,三者不是同一层级。本文适合遇到“订阅能打开却无法导入”“解码后仍是一串字符”或“分享链接不能当配置文件使用”的读者。按外层订阅、单条链接、内核字段逐层检查,可判断该解码、导入还是补齐配置。
先分清三种格式:订阅地址不是节点配置
结论:先看拿到的内容,再选导入入口。一个以 https:// 开头的订阅地址只是获取入口;它的响应正文才决定客户端收到的是 Base64 节点列表、明文分享链接,还是其他结构。把地址本身做 Base64 解码,不会得到服务器配置。
订阅地址与节点列表
推荐常见响应是多条分享链接按行排列,再整体进行 Base64 编码。客户端保存订阅地址,更新时重新获取节点;这适合持续维护节点集合,但不是所有服务都使用这种响应格式。
适合:服务方提供有效订阅地址,需要持续更新节点。
vmess:// 分享链接
本文讨论常见的 v2 分享格式:前缀后面是 Base64 编码的节点 JSON。它主要描述一个 VMess 出站所需的信息,不包含完整的本地监听、DNS 与路由策略。
适合:单节点迁移、剪贴板导入和二维码传递。
内核原生 JSON
通常包含 inbounds、outbounds,并可配置 dns 与 routing。结构和支持字段受内核及版本约束,不能仅凭文件扩展名判断兼容。
适合:自定义入站、路由规则及明确指定内核的配置管理。
Base64 不负责保密。订阅地址中的访问令牌、节点 UUID 和分享二维码都可能提供连接权限。排查时使用本地编辑器,不把真实内容粘贴到在线解码站;向他人提供日志前,也要移除这些身份信息。
Base64 订阅解码:外层列表与内层节点分别处理
常见的两层结构是:外层 Base64 解码得到若干条 vmess:// 链接;取其中一条,移除前缀后再次解码,才得到节点 JSON。不能对所有输入固定解码两次。若订阅正文已经以分享链接开头,外层就不需要解码。
-
取得响应正文
用本地工具查看订阅返回内容。若看到登录页、验证码或错误页,先处理访问问题;HTML 页面不是节点列表,即使请求返回成功也不代表订阅可用。
-
识别外层
正文直接出现
vmess://或vless://时,按行读取。若是连续编码文本,再尝试 Base64 解码;以{或[开头的内容先按 JSON 检查。 -
分离节点行
把解码结果按换行拆开,清除每行首尾空白并过滤空行。不要把多条链接拼成一条,也不要破坏链接内部的字符。
-
解析单条链接
对本文所述的 VMess v2 格式,去掉
vmess://后解码并解析 JSON。若内容不是该结构,应确认分享格式来源,而不是强行套用字段。
以下函数用于本地浏览器控制台,兼容标准 Base64 与常见的 URL 安全字符,补齐省略的尾部填充,并按 UTF-8 还原中文备注。先阅读代码再执行。函数不会发送网络请求,也不会自动读取剪贴板。
function decodeBase64Utf8(input) {
let value = input.trim().replace(/\s+/g, "");
value = value.replace(/-/g, "+").replace(/_/g, "/");
if (!/^[A-Za-z0-9+/]*={0,2}$/.test(value)) {
throw new Error("输入含有非 Base64 字符");
}
value = value.replace(/=+$/, "");
if (value.length % 4 === 1) {
throw new Error("Base64 长度不完整");
}
value += "=".repeat((4 - value.length % 4) % 4);
const binary = atob(value);
const bytes = Uint8Array.from(binary, char => char.charCodeAt(0));
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
}
function readSubscription(body) {
const text = body.trim();
const decoded = /^(vmess|vless):\/\//i.test(text)
? text
: decodeBase64Utf8(text);
return decoded.split(/\r?\n/)
.map(line => line.trim())
.filter(Boolean);
}
function readVmess(link) {
const value = link.trim();
if (!value.startsWith("vmess://")) {
throw new Error("需要 vmess:// 链接");
}
return JSON.parse(decodeBase64Utf8(value.slice(8)));
}
readSubscription 只演示“明文链接列表或其 Base64 编码”这一种订阅约定,不是通用订阅解析器。遇到其他结构时应停止,检查服务方给出的格式说明。Base64 解码成功也只说明编码可读,不代表节点有效或账号仍有权限。
拆开 vmess://:分享字段怎样映射到原生配置
下面是一个用于结构演示的 VMess v2 节点对象。example.com 是文档示例域名,UUID 也仅用于示例,不能据此连接代理服务。字段 v 的值 "2" 表示分享格式版本,不是客户端或内核版本。
{
"v": "2",
"ps": "文档示例",
"add": "example.com",
"port": "443",
"id": "00000000-0000-4000-8000-000000000001",
"aid": "0",
"scy": "auto",
"net": "ws",
"type": "none",
"host": "example.com",
"path": "/ws",
"tls": "tls",
"sni": "example.com"
}
| 分享字段 | 原生出站位置 | 转换注意点 |
|---|---|---|
add、port | settings.vnext[].address、port | 地址为字符串;原生端口使用数字。 |
id、aid | settings.vnext[].users[].id、alterId | 本例 aid 为 0,对应现代 VMess AEAD 用法。 |
scy | settings.vnext[].users[].security | 这是 VMess 用户加密设置,不是 TLS 开关。 |
net | streamSettings.network | 本例为 ws;不同传输不能只改一个字段。 |
host、path | wsSettings.headers.Host、wsSettings.path | 这里按 WebSocket 解释,路径中的查询参数也需保留。 |
tls、sni | streamSettings.security、tlsSettings.serverName | TLS 服务器名与 WebSocket Host 分属不同层。 |
ps、v | 备注与分享格式信息 | 不是内核连接所需字段,不应原样塞进出站设置。 |
转换判断:先保留传输语义,再调整字段类型
逐项核对地址、端口、UUID、传输、TLS、SNI、Host 与路径。不要把分享字段 type: "none" 当作关闭 TLS 的依据,也不要用地址字段自动覆盖服务方明确提供的 SNI。
VMess 与 VLESS 是不同协议。vless:// 常见结构是带用户信息、主机、端口和查询参数的 URI,不能照搬“移除前缀后 Base64 解码 JSON”的流程。把链接前缀从 VMess 改成 VLESS,也不会完成协议转换;服务端必须支持对应协议与身份配置。
转成原生 JSON:节点之外还要补齐入站
原生配置不是把解码后的节点 JSON 换一个文件名。下面给出 Xray 常见 VMess、WebSocket、TLS 结构的完整示例:本地提供 SOCKS 入站,出站使用上面的演示节点。具体字段仍应以实际安装的内核版本为准;不能把它视为所有 V2Fly 版本都接受的统一配置。
{
"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": "vmess",
"settings": {
"vnext": [
{
"address": "example.com",
"port": 443,
"users": [
{
"id": "00000000-0000-4000-8000-000000000001",
"alterId": 0,
"security": "auto"
}
]
}
]
},
"streamSettings": {
"network": "ws",
"security": "tls",
"tlsSettings": {
"serverName": "example.com",
"allowInsecure": false
},
"wsSettings": {
"path": "/ws",
"headers": {
"Host": "example.com"
}
}
}
}
]
}
这个示例没有添加分流规则,默认流量交给首个出站。它也不会自动设置系统代理;应用需要明确使用 127.0.0.1:10808 的 SOCKS 服务。127.0.0.1 限制监听于本机,不应为了排错直接改成面向所有网卡的地址。
- 先查端口。若现有客户端已占用 10808,第二个内核可能启动失败。可停止重复实例,或同步修改本地入站与应用端代理端口。
- 保持证书验证。示例保留
allowInsecure: false。证书错误应检查系统时间、服务器名与证书链,而不是直接关闭验证。 - 区分配置作用域。通过客户端导入自定义配置时,系统代理、TUN 和内置路由是否继续生效取决于导入模式,不应假定与普通节点模式相同。
反向转换:分享链接只能带走部分配置
由单个 VMess 出站生成分享链接,需要先从原生配置提取服务器、用户及传输字段,组装为前文的 v2 分享对象,再按 UTF-8 编码成 Base64,最后加上 vmess:// 前缀。不能把整个原生配置直接编码后加此前缀,那样得到的结构不符合常见节点导入约定。
function encodeBase64Utf8(text) {
const bytes = new TextEncoder().encode(text);
let binary = "";
for (const byte of bytes) {
binary += String.fromCharCode(byte);
}
return btoa(binary);
}
function createVmessLink(share) {
return "vmess://" + encodeBase64Utf8(JSON.stringify(share));
}
function createBase64Subscription(links) {
return encodeBase64Utf8(links.join("\n"));
}
createVmessLink 接收的是已按字段表整理好的分享对象,不负责自动提取复杂配置,也不验证节点参数。createBase64Subscription 接收分享链接数组,生成常见订阅正文;它并不会创建一个可更新的订阅地址,后者还需要由受控服务提供响应。
- 原生配置有多个服务器或多个用户时,必须明确选取哪个组合。不要默认数组中的所有内容都属于同一条链接。
- 本地入站、DNS、路由、日志级别和多出站策略通常无法通过一条 VMess 分享链接完整保留。
- 高级传输字段若没有对应的分享格式约定,可能在导入时丢失。需要完整迁移时,应使用配置备份并确认目标内核兼容。
迁移判断:节点用链接,策略用备份
仅迁移服务器连接信息时使用分享链接;需要保留路由、DNS 与本地监听设置时使用经过检查的配置备份。转换后逐项对照关键字段,不以“导入成功”作为无损迁移的证明。
客户端导入:订阅、剪贴板与自定义配置分开使用
客户端能识别某种外层格式,不等于其内核支持里面的所有协议与传输。v2rayN 是桌面客户端;v2rayNG 使用 Xray 内核;v2flyNG 使用 V2Fly 内核。先匹配连接参数与内核能力,再选择导入入口。以下菜单以常见中文界面为参照,不同发布版本可能调整名称和位置。
| 输入内容 | v2rayN | 安卓客户端 |
|---|---|---|
| 订阅地址 | 「订阅分组」→「订阅分组设置」,添加地址后更新分组。 | v2rayNG 在侧边栏「订阅分组设置」添加,再用主界面菜单「更新订阅」。v2flyNG 按当前版本订阅入口操作。 |
| 单条分享链接 | 复制完整链接,使用「服务器」→「从剪贴板导入批量 URL」。 | v2rayNG 在主界面右上角「+」→「从剪贴板导入」。v2flyNG 使用其分享链接导入入口。 |
| 原生 JSON 文件 | 使用「服务器」菜单中的「添加自定义配置服务器」,并核对使用的内核。 | 使用当前版本的自定义配置导入功能;不要粘贴到订阅地址栏,跨内核迁移先检查字段。 |
v2rayN 常见订阅更新操作位于「订阅分组」→「更新当前订阅(不通过代理)」。这一项控制获取订阅时是否经过代理,不会把节点的连接方式改成直连。若订阅站点必须经代理访问,应选择对应的通过代理更新方式,并先确认现有代理链路可用。
- 更新失败先看响应。地址过期、访问令牌失效或返回网页时,不应继续做节点格式转换。先检查更新日志与订阅权限。
- 导入失败先看结构。检查是否多复制了引号、缺少前缀,或把完整 JSON 错当成分享链接。中文备注乱码时检查 UTF-8 编解码。
- 连接失败再看参数。节点已出现在列表却无法连接,应核对 UUID、端口、WebSocket 路径、Host、TLS 与 SNI,而不是反复重做 Base64 编码。
- 手工修改单独保存。订阅更新可能重建节点条目。临时修改的节点宜复制到独立分组,并记录与订阅原值的差异。
常见问题:解码成功为什么仍然不能使用
解码后还是 vmess://,是否说明解码失败?
不是。外层订阅装的是分享链接列表,看到 vmess:// 通常说明已取得节点行。若要查看节点 JSON,再对单条链接的前缀后内容解码;若只是导入客户端,通常可以直接使用整条分享链接。
任何 JSON 都可以作为 V2Ray 原生配置吗?
不可以。VMess 分享对象、客户端设置备份和内核配置都可能采用 JSON,但字段语义不同。先检查是否具有该内核要求的入站、出站结构,再检查协议与传输字段。JSON 语法正确只代表文本能被解析。
把本地节点列表编码后,就有可自动更新的订阅了吗?
只有订阅正文,没有订阅服务。自动更新还需要一个稳定、受访问控制的获取地址。把正文保存到本地不会让其他设备自动收到变更;只传递一次节点时,分享链接更直接。
客户端导入成功,是否意味着连接参数完整?
不一定。解析器可能接受基础字段,却忽略未支持的扩展参数。导入后应打开节点编辑界面,对照传输类型、路径、Host 与 TLS 服务器名,再进行实际连接测试。二维码也只是文本载体,不会补齐缺失参数。