订阅地址与订阅格式
订阅地址是获取配置的入口,格式是服务器返回内容的结构。一个以 HTTPS 开头的地址,既可能返回 Clash YAML,也可能返回经过 Base64 编码的节点列表,还可能返回登录页面。不能只凭链接后缀判断格式;没有 .yaml 后缀的地址,同样可能提供完整配置。
实际排查时,先区分三层:HTTP 是否成功、返回文本采用什么格式、当前内核是否支持其中的协议。把订阅转换成 YAML,只解决配置结构的适配问题,不会让旧内核自动获得新协议支持,也不会修复已经失效的节点凭据。
三种常见格式的结构差异
| 格式 | 识别线索 | 通常包含 | 不能据此推断 |
|---|---|---|---|
| URI 节点链接 | ss://、trojan://、vmess:// |
单个节点的地址、认证与部分传输参数 | 不代表含有分流规则、DNS 配置或策略组 |
| Base64 节点列表 | 解码后出现逐行 URI | 多个节点链接的编码文本 | 不代表解码后就是 Clash YAML |
| Clash YAML | proxies:、proxy-groups:、rules: 等键 |
节点、策略组、规则及可选的 DNS、监听配置 | 不代表所有 Clash 系内核都支持相同字段 |
URI:单节点的分享表示
URI 常见结构是“协议、认证信息、服务器、端口、查询参数、备注”。不同协议并不完全遵循同一套字段约定。例如,常见的 vmess:// 分享内容会使用 Base64 包裹 JSON;Shadowsocks 的分享方式则可能只编码认证部分。不要对所有协议套用同一个字符串拆分函数。
下面是用于说明字段的虚构 Trojan 链接。example.com 是文档示例域名,密码为演示文本,不提供可用代理服务。查询参数中的 sni 是 TLS 服务器名称,井号后的内容是节点备注。
trojan://[email protected]:443?security=tls&type=tcp&sni=example.com#Lab-Trojan
Base64:包裹列表的编码层
常见订阅把多条 URI 用换行分隔,再对整个文本做一次 Base64 编码。正确流程是先解开外层编码,再逐行交给对应协议的解析器。若解码后的某一行仍包含 Base64,那可能是该协议本身的分享结构,不应反复对整份文本盲目解码。
部分来源使用 URL 安全变体,字符 -、_ 分别替代 +、/,也可能省略末尾的 =。这些差异属于编码处理,不是另一种代理协议。
YAML:完整配置与节点提供器要分开
完整配置可以组织 proxies、proxy-groups、rules、dns 等配置段。只含 proxies: 的文件,常被用作节点提供器文件;它不等于一份已经具备策略组、规则和监听设置的客户端配置。将这种文件当作完整配置导入,可能出现节点存在但规则仍然直连的情况。
客户端能否直接导入
Clash 系客户端通常围绕 YAML 配置工作,但图形界面的导入能力由客户端实现决定。Clash Verge Rev、FlClash 等客户端的配置导入功能,与 mihomo 内核读取配置文件,是两个不同层次。界面支持扫描 URI、粘贴节点或订阅转换,并不意味着内核能把任意 Base64 文本直接作为配置加载。
- 来源提供 Clash 或 Mihomo 选项:先选目标配置,再导入订阅 URL。需要 VLESS、Reality 或其他扩展字段时,应核对目标内核的支持情况。
- 只有一条 URI:检查客户端是否提供单节点导入。如果没有,需要转换成
proxies条目,并补齐策略组与规则。 - 只有 Base64 列表:先识别解码后的协议,再决定用客户端内置转换还是本地转换。修改文件扩展名不会完成转换。
- 已有完整 YAML:优先直接导入,避免先导出 URI 再转回 YAML。中间格式通常无法携带原配置的规则和 DNS 设置。
不要把“Clash 格式”理解为永久固定的兼容承诺。传统 Clash、Clash Premium 与 Clash Meta(现常称 mihomo)的功能集合存在差别。遇到 unsupported proxy type 或未知字段错误,应记录客户端版本、实际内核版本及协议类型,再查兼容范围;只升级界面程序不一定改变正在使用的内核。
本地转换:先解码,再映射节点字段
第一步:确认拿到的是订阅正文
把响应保存到本地文件,用文本编辑器检查开头。若内容是 <!DOCTYPE html>、登录提示或 JSON 错误对象,应先处理请求失败。HTTP 200 只说明服务器返回了响应,不保证响应是有效订阅;401、403 常与认证或访问限制相关。
部分服务根据请求参数或 User-Agent 返回不同格式。浏览器下载到节点列表,客户端却拿到 YAML,可能是服务端做了格式协商。应以服务方文档为准,不要盲目追加网上搜到的转换参数。
第二步:在本机解开列表编码
下面的 Node.js 脚本只依赖内置模块,执行的工作仅为 Base64 解码,不是协议转换。将已保存的编码正文命名为 subscription.txt,脚本另存为 decode-subscription.cjs。输入输出留在本机,输出文件仍包含敏感凭据。
const fs = require('node:fs');
const { TextDecoder } = require('node:util');
const input = fs.readFileSync('subscription.txt', 'utf8').trim();
const compact = input.replace(/\s+/g, '');
if (!compact || !/^[A-Za-z0-9+/_-]+={0,2}$/.test(compact)) {
throw new Error('输入不是预期的 Base64 列表,请先检查响应正文');
}
const normalized = compact.replace(/-/g, '+').replace(/_/g, '/');
const bare = normalized.replace(/=+$/, '');
if (bare.length % 4 === 1) {
throw new Error('Base64 长度异常,输入可能不完整');
}
const bytes = Buffer.from(bare, 'base64');
if (bytes.toString('base64').replace(/=+$/, '') !== bare) {
throw new Error('Base64 内容无效或存在异常尾位');
}
const text = new TextDecoder('utf-8', { fatal: true }).decode(bytes);
fs.writeFileSync('nodes.txt', text, { mode: 0o600 });
console.log('已写入 nodes.txt;请在本机检查协议与行数');
node decode-subscription.cjs
如果原文已经是 URI 列表或 YAML,不要运行这一步。文件权限参数在不同操作系统上的效果不同;输出文件应放在个人目录,避免保存到自动同步或公开分享的文件夹中。解码结束后,检查是否每条节点独占一行,以及是否混入流量说明等非节点文本。
第三步:将 URI 字段映射为 YAML
对前面的 Trojan 示例,可以生成下面这份最小演示配置。它将本地混合代理端口设为 7890,模式设为 rule,并把所有未提前匹配的连接交给 出口选择。这段配置用于说明结构;演示服务器本身不能用于连通性验证。
mixed-port: 7890
allow-lan: false
mode: rule
log-level: info
proxies:
- name: "Lab-Trojan"
type: trojan
server: example.com
port: 443
password: "training-only"
sni: example.com
skip-cert-verify: false
proxy-groups:
- name: "出口选择"
type: select
proxies:
- "Lab-Trojan"
- DIRECT
rules:
- MATCH,出口选择
server 对应连接地址,port 是整数,password 对应 URI 的认证信息,sni 对应 TLS 名称。示例的 type=tcp 没有被机械复制成 YAML 的 type: tcp,因为这里的 type 表示代理协议,应为 trojan。这正是转换不能只靠字段同名替换的原因。
如果实际链接使用 WebSocket、gRPC 或 Reality,还需要按目标内核语法映射路径、Host、服务名、公钥与短 ID 等参数。只保留服务器和密码,会生成语法可能正确、连接却无法建立的节点。不了解字段含义时,应选择明确支持该协议及传输方式的本地转换工具,而不是删掉报错字段。
规则、策略组与节点字段如何保留
| 源字段或配置段 | 转换检查项 | 遗漏后的典型影响 |
|---|---|---|
| 节点名称 | 名称唯一,组内引用完全一致 | 策略组引用失败或出现歧义 |
| TLS 与传输参数 | SNI、证书验证、路径及协议扩展 | 握手失败、连接被关闭 |
proxy-groups |
组名、组类型、成员与测试设置 | 无法选择出口,自动选择行为变化 |
rules |
原顺序、目标名称及末尾兜底 | 本应代理的连接变为直连 |
dns、tun |
由本机配置或客户端覆写继续管理 | 解析与流量接管方式发生变化 |
从 URI 列表生成 YAML 时,分流规则与策略组通常是转换模板新建的,不是从节点中恢复出来的。若模板生成了 DOMAIN-SUFFIX,example.org,DIRECT,这只是模板规则;原始节点链接没有携带这条规则。迁移前后应分别比较节点参数和路由策略,不能只比较节点数量。
从完整 YAML 导出 URI 则会丢失大量上下文。策略组的嵌套关系、rule-providers、DNS 策略和 TUN 设置没有通用的单节点 URI 表达。再次转换回来,也只能依据模板重新生成,不能恢复原文件的注释、排序或所有功能。
用节点提供器减少重复转换
如果服务方能提供节点型 YAML,可以让节点更新与本地规则分开。下面展示 mihomo 配置中的本地 proxy-providers 方式:先由外部流程更新 ./providers/lab.yaml,再由策略组通过 use 引用。该文件本身必须是受支持的提供器格式,而不是仍然编码的 Base64 列表。
proxy-providers:
lab:
type: file
path: ./providers/lab.yaml
proxy-groups:
- name: "出口选择"
type: select
use:
- lab
rules:
- MATCH,出口选择
提供器文件使用下面的节点包装结构,内部条目的字段与 proxies 中的节点一致。相对路径依据内核工作目录或客户端管理方式解析,不一定相对于手工打开的 YAML 所在目录;找不到文件时应查看内核日志中的实际路径。
proxies:
- name: "Lab-Trojan"
type: trojan
server: example.com
port: 443
password: "training-only"
sni: example.com
skip-cert-verify: false
如果改用 HTTP 提供器,更新地址仍需返回目标内核接受的节点内容。更新间隔不等于格式转换器。完整配置中的远程规则、自动更新 URL 也应一并审阅;不希望订阅覆盖的本地设置,可通过客户端提供的覆写或合并机制管理。具体入口因客户端不同而异。
导入失败与转换后的验证清单
先定位错误层级
- 提示 YAML 解析失败:检查缩进是否用了空格,冒号后是否缺少空格,密码和备注是否正确加引号。包含
#的未引用文本可能被解释为注释。 - 提示找不到策略:核对规则目标与策略组名称。
MATCH,出口选择必须对应同名组,节点重命名后也要更新引用。 - 提示不支持协议:检查实际加载的内核。把
type改成另一个协议名称不能实现协议转换。 - 配置加载成功但超时:检查节点认证、TLS 和传输参数,再检查网络与服务端状态。语法有效不等于节点可连接。
- 重启后修改消失:通常是订阅更新重新生成了文件。把长期修改放入客户端覆写配置,或维护独立的本地配置。
按顺序复测
- 备份原配置,并记录原来的节点数、组名及规则模式。不要直接覆盖唯一可用的配置文件。
- 检查转换结果是否包含认证与传输参数。对比敏感字段时在本机操作,不把完整截图发到公共讨论区。
- 有 mihomo 命令行环境时,可运行
mihomo -t -f ./config.yaml检查配置;测试期间仍可能读取外部文件或请求远程资源,应先审阅配置内容。 - 加载后手动选择一个节点,确认当前为规则模式,并查看连接日志中的实际策略命中情况。
- 如果使用示例端口
7890,可用curl --proxy http://127.0.0.1:7890 https://example.com/检查本地代理入口。该测试需替换为实际可用的节点配置,且不代表 TUN、UDP 或所有规则都已验证。 - 最后执行一次订阅更新并重启客户端,确认节点仍存在,覆写设置继续生效,规则目标没有变为不存在的名称。
选择转换路径时,优先级可以固定为:服务方直接提供目标 YAML,其次是客户端明确支持的本地导入,再其次是可审阅的本地转换。需要长期更新时,将节点来源与本地规则分开维护,比反复把整份配置转成 URI 再转回来更容易定位问题。字段语法可继续查阅配置大全;客户端与内核选择可参考横向评测。