订阅格式对照:Clash YAML、Base64 节点链接与 sing-box 配置互转
梳理常见订阅格式的结构差异,说明各客户端能识别哪些格式,以及本地转换订阅时需要注意的字段与安全事项。
先辨认内容,再选择导入入口
订阅链接是获取内容的地址,不是格式名称。同一个地址可能按请求参数或客户端的 User-Agent 返回不同内容;文件名以 .yaml 结尾,也不能保证响应一定是 Clash 配置。导入失败时,应先在本地查看实际响应,而不是反复修改文件后缀。
这里需要分清三个层次:Base64 是编码方式,ss:// 等 URI 是节点描述方式,Clash YAML 与 sing-box JSON 则是各自内核的配置结构。编码解开之后得到节点列表,不等于已经生成了包含分流规则、DNS 与监听端口的完整配置。
| 内容类型 | 本地识别特征 | 通常包含什么 |
|---|---|---|
| Clash 完整 YAML | 顶层常见 proxies、proxy-groups、rules | 节点、策略组、规则,也可能带 DNS 与 TUN 设置 |
| Clash 节点提供器 YAML | 通常以顶层 proxies: 列表为核心 | 供 proxy-providers 引用的节点集合 |
| 节点 URI 列表 | 多行 ss://、vmess://、trojan:// 等 | 节点连接参数,通常没有完整分流策略 |
| Base64 订阅 | 看似连续字母、数字及编码符号;解码后再识别 | 常见为节点 URI 列表,实际以解码结果为准 |
| sing-box JSON | 顶层常见 inbounds、outbounds、route | 入站、出站、路由与可选 DNS 配置 |
Clash YAML:完整配置与节点提供器
能解析 YAML,不代表能直接运行代理
YAML 负责表达数据;mihomo 还会检查字段类型、节点参数和引用关系。缩进应使用空格,不能用 Tab。端口应按字段要求填写数字;含特殊字符的名称、密码或 URL 可用引号包围,避免 # 被识别成注释、冒号改变结构。
下面是一份用于理解结构的直连示例,不含代理节点。它监听本机 127.0.0.1:7890,启用规则模式,最终交给名为“出口选择”的策略组;组内只有 DIRECT,因此运行后仍然直连。示例不会自动打开客户端的系统代理或 TUN。
mixed-port: 7890
allow-lan: false
bind-address: 127.0.0.1
mode: rule
log-level: info
proxies: []
proxy-groups:
- name: 出口选择
type: select
proxies:
- DIRECT
rules:
- MATCH,出口选择
提供器文件需要主配置引用
节点提供器通常只交付 proxies 集合。主配置通过 proxy-providers 定义来源、更新间隔及健康检查,再由策略组的 use 引用提供器名称。只把提供器文件当作完整配置导入,可能出现“看得到节点,却没有预期策略组”的情况。
- 检查引用:规则中的策略名必须存在;策略组中的节点名或提供器名也必须对应。
- 检查重名:两个节点同名会让选择和引用变得不明确。转换时重命名后,要同步更新相关引用。
- 检查内核:Clash 兼容配置不等于所有旧版 Clash 内核都支持。部分协议或扩展字段需要 mihomo,实际能力以客户端使用的内核为准。
Base64:解码不等于协议转换
Base64 不会隐藏订阅凭据,也不会自动补全字段。常见订阅把多条节点 URI 用换行拼接,再把整段文字编码一次;其中某些 URI 自身还含有 Base64 片段。因此,外层解码成功后,应先识别每行协议,不要把所有内容反复解码到“看起来像文字”为止。
用 Python 3 在本地检查外层内容
将已获取的编码正文保存为 subscription.txt,把下列脚本保存为 decode_subscription.py,执行 python3 decode_subscription.py。脚本兼容常见标准与 URL-safe 字母表,并补齐末尾填充;它只解码,不连接节点、不转换配置,也不输出节点内容到终端。
from pathlib import Path
import base64
import binascii
source = Path("subscription.txt").read_text(encoding="utf-8-sig")
compact = "".join(source.split())
if not compact:
raise SystemExit("输入为空")
compact += "=" * (-len(compact) % 4)
try:
raw = base64.b64decode(compact, altchars=b"-_", validate=True)
text = raw.decode("utf-8")
except (ValueError, binascii.Error, UnicodeDecodeError):
raise SystemExit("无法按 UTF-8 Base64 文本解码,请先检查原始响应")
with Path("decoded.txt").open("x", encoding="utf-8") as output:
output.write(text)
print("已写入 decoded.txt;请在本地查看内容类型")
使用独占写入模式是为了避免覆盖已有的 decoded.txt。如果文件已存在,先确认它是否仍需保留。解码后文件仍然含有敏感信息,不要把它上传到公开代码仓库或发进群聊。
URI 内层仍需按协议解析
ss://存在不同表达形式,用户信息可能经过 Base64 编码,还可能附带插件参数;不能只按第一个冒号或@拆分。vmess://常见形式是编码后的 JSON,但具体字段仍需校验,不能把它当作任意通用节点字符串。trojan://、vless://等链接常通过查询参数携带 TLS、传输层与路径信息;URL 百分号解码也要遵循字段边界。- 片段中的显示名称通常不是连接参数。重命名节点可以改善识别,但不会修复错误的服务器地址或认证信息。
sing-box JSON 与 Clash YAML 的转换边界
两者都能描述代理连接,但配置模型并不相同。Clash 常以节点、策略组和有序规则组织出口;sing-box 通过带 tag 的出站与路由引用组织流量。将 proxies 改名为 outbounds,不会得到有效的 sing-box 配置。
下面是 sing-box 的直连结构示例:HTTP 与 SOCKS 共用 127.0.0.1:7890,最终路由到 direct-out。这是用于对照字段的独立示例,与上面的 mihomo 示例不能同时占用同一个监听地址和端口。
{
"inbounds": [
{
"type": "mixed",
"tag": "mixed-in",
"listen": "127.0.0.1",
"listen_port": 7890
}
],
"outbounds": [
{
"type": "direct",
"tag": "direct-out"
}
],
"route": {
"final": "direct-out"
}
}
| 转换项目 | 需要处理的差异 |
|---|---|
| 节点连接 | 逐项映射协议、地址、端口、认证、TLS 与传输层参数 |
| 策略选择 | Clash 策略组与 sing-box 选择类出站的结构、选项并非完全一致 |
| 分流规则 | 规则语法、动作与规则集格式不同,不能原样复制 |
| DNS | 服务器定义、解析路由、Fake-IP 与引导解析需要独立重建 |
| 流量接管 | 系统代理由客户端或系统配置;TUN 还涉及权限、路由与平台差异 |
版本要与配置一起记录
sing-box 的 DNS 与路由字段经历过版本迁移。例如 1.12.0 引入了新的 DNS 服务器配置格式,因此旧配置能被某个客户端导入,不代表更新内核后仍然适用。转换前记录源客户端内核版本、目标内核版本和转换器版本,按目标版本的文档生成配置,不要只记录应用界面上的版本号。
客户端识别能力:区分导入器与内核
“客户端支持某格式”可能指它内置了导入或转换功能,并不代表底层内核直接读取该格式。以 mihomo 为内核的客户端通常围绕 Clash YAML 工作;部分客户端可将节点链接整理成配置,但是否接受整段 Base64、是否保留规则与 DNS,要看具体导入入口。sing-box 内核主要读取其 JSON 配置,图形客户端也可能另外实现订阅管理。
- 先看入口用途:区分“从 URL 导入配置”“添加单个节点”“导入节点提供器”和“导入本地文件”。单节点入口通常不会自动建立完整规则体系。
- 再看导入预览:确认节点数量、协议类型、策略组名称。源文件有 12 个节点而结果只有 9 个时,应先核对被跳过的 3 个节点,而不是直接启用。
- 最后看实际启用配置:订阅缓存、用户覆写和客户端生成配置可能是三份不同文件。界面修改是否会在下一次订阅更新后保留,需要单独验证。
需要重新选择客户端时,可从下载中心按操作系统查看入口;导入和首次接管流量的步骤见订阅导入指南。不要仅凭客户端名称推断其支持所有 Clash 扩展字段。
本地转换与验证:按五步收敛问题
1. 保存源文件与目标版本
保留一份不参与编辑的源文件,再另存转换结果。记录获取时间、内容类型和目标内核版本。若订阅地址带访问令牌,记录时只保留用途说明,不把完整地址写进共享文档。
2. 先转换节点,再迁移策略
先核对服务器、端口、密码或 UUID、加密方式和传输参数,再处理分组与规则。协议无法在目标内核中表达时,应保留失败记录,不能悄悄替换成另一种协议。只拿到节点列表时,需要另行设计 DNS 和分流,而不是期待转换器恢复从未存在的规则。
3. 使用目标内核检查语法
下列命令假定内核程序在当前终端路径中。mihomo 的 -t 检查配置,sing-box 的 check 检查配置有效性;Windows 下可使用对应的 .exe 文件名。检查通过只表示配置层面可接受,不代表服务器可达或认证成功。
mihomo -t -f converted.yaml
sing-box check -c converted.json
4. 先用显式代理验证节点
为减少系统设置干扰,可以先不启用 TUN,通过已确认的本地 HTTP 代理端口测试。下例要求配置中存在 7890 端口,且当前策略已选中待测节点;测试站点只是请求目标,不是节点服务器。
curl --proxy http://127.0.0.1:7890 --connect-timeout 10 --max-time 20 -I https://example.com/
这条命令成功,只能证明该次请求经过本地代理入口完成了访问。还要在连接面板或日志中确认最终出站:如果命中 DIRECT,不能据此断言代理节点工作正常。失败时依次区分端口未监听、DNS 失败、TLS 握手失败与远端拒绝认证。
5. 再验证系统代理与 TUN
显式代理测试正常后,再开启系统代理测试浏览器;确实需要接管不遵循系统代理的应用时,再测试 TUN。TUN 的权限、路由或 DNS 接管问题不属于订阅编码问题。一次只修改一层,才能判断错误来自节点转换还是流量接管。
订阅凭据与转换结果的保存
把订阅 URL 交给在线转换服务,通常意味着对方能够获取原始订阅及其中的节点凭据。只在浏览器里打开一个转换页面,并不能证明处理过程发生在本机。需要离线处理时,应使用已确认不依赖远程服务的本地工具,并检查规则模板、远程提供器等附加资源是否仍需联网。
- 限制本地接口:转换服务若只供本机使用,应监听
127.0.0.1,不要直接暴露在0.0.0.0或公网端口。 - 控制可读权限:在 Linux 或 macOS 可用
chmod 600 subscription.txt decoded.txt converted.yaml限制已有文件的访问;Windows 使用文件安全权限设置。 - 脱敏再排错:分享错误信息时保留协议、字段名和报错位置,移除订阅令牌、密码、UUID,以及不宜公开的服务器信息。
- 泄露后更换凭据:Base64 文本也应按明文配置保护。若订阅链接已公开,删除帖子之外,还应到提供方后台重置链接或相关凭据。
完成转换的标准不是“文件能打开”,而是目标内核检查通过、关键字段保留、策略引用完整,并且实际连接按预期出站。优先使用提供方直接生成的目标格式;确需互转时,保留源文件、记录版本,再逐层验证,能明显减少更新订阅后的重复排错。