用 Cloudflare Workers 把机场订阅转成「带 Token 鉴权 + 自定义域名」的 Clash/Mihomo 订阅
本文把一条原始的机场订阅链接,通过一个 Cloudflare Worker,转换成形如
https://sub.example.com/?token=YOUR_TOKEN的可访问地址:访问时实时拉取机场订阅、
解析节点、套上你自己定制的一整套 Mihomo 规则(DNS / 分流 / TUN),并用 Token 做访问鉴权。
全程零服务器、免费额度足够个人使用。文中所有敏感信息已脱敏,替换为占位符。
一、背景:为什么要做订阅转换
大多数机场(本文以 JustMySocks 为例)给你的「订阅地址」长这样:
1 | https://jmssub.net/members/getsub.php?service=XXXXXXX&id=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx |
它返回的是一段 Base64 编码的纯节点列表,解码后是若干行 ss://...、vmess://...。
直接丢进客户端能用,但有几个不爽的点:
- 只有节点,没有规则。没有 DNS 防泄漏、没有国内外分流、没有去广告,体验全靠客户端默认。
- 订阅地址又长又敏感,还带着
service/id这种明文凭证,不好记也不好分享给自己的其它设备。 - 想统一配置。我希望所有设备拉同一份「我精心调过的 Mihomo 模板」,节点部分自动从机场同步。
解决思路:写一个边缘函数做中间层 —— 客户端访问我的地址,函数去机场拉原始订阅,
解析出节点,再拼进我自己的一份完整 Mihomo 配置后返回。顺便:
- 用一个好记的自有域名 + Token 控制访问;
- 敏感信息(机场地址、Token)放在 KV 里,不写进代码;
- 改配置、换机场、换 Token 都不用重新发版。
为什么选 Cloudflare Workers:
- 免费额度对个人足够(每天 10 万次请求);
- 全球边缘节点,延迟低;
- 自带 KV 键值存储,存敏感配置正合适;
- 自定义域名免费,且自动签发 HTTPS 证书;
- 完全 Serverless,不用买服务器、不用运维。
二、最终效果
部署完成后,把下面这一条地址填进 Clash Verge / Mihomo / Stash 等客户端的「订阅」即可:
1 | https://sub.example.com/?token=YOUR_TOKEN |
- 带正确
token→ 返回一份完整的 Mihomo YAML(节点 + DNS + 规则); - Token 错误或缺失 → 返回
401 Unauthorized。
三、整体架构与请求流程
1 | ┌─ 客户端 (Clash / Mihomo) |
核心数据流:客户端 → 鉴权 → 读 KV 拿机场地址 → 拉原始订阅 → 解码 → 解析节点 → 渲染模板 → 返回。
敏感配置全部隔离在 KV 中,代码本身不含任何机场信息或 Token。
四、前置条件
| 条件 | 说明 |
|---|---|
| Cloudflare 账号 | 免费即可 |
| 一个已托管在 Cloudflare 的域名 | 域名的 zone 状态必须是 Active(即 NS 已指向 Cloudflare)。任意便宜/免费域名都行 |
| 本地 Node.js | ≥ 18,推荐 20 / 22 |
| 机场订阅地址 | 本文以 JustMySocks 的 getsub.php 地址为例 |
关于域名:在 Cloudflare 控制台「添加站点」,按提示把域名在注册商处的 NS 改成 Cloudflare
分配的两个 nameserver,等 zone 状态变为 Active 即可。本文用example.com作 zone,
子域sub.example.com作为订阅地址。
五、初始化项目
1 | mkdir jms-sub && cd jms-sub |
目标目录结构:
1 | jms-sub/ |
.gitignore(避免把本地状态/密钥提交上去):
1 | node_modules/ |
六、编写 Worker(核心代码讲解)
完整代码见文末 附录 A,这里拆开讲关键部分。
6.1 入口与 Token 鉴权
1 | export default { |
env.SUBSCRIPTION_URL 是后面绑定的 KV 命名空间。这里有个要点:
如果 KV 里没设 TOKEN,则不鉴权、谁都能访问——所以一定要记得写入 TOKEN。
6.2 拉取机场原始订阅(KV 取地址 + Base64 解码)
1 | async function fetchSubscription(env) { |
要点:
- 机场订阅返回的是 Base64,用
atob()解码成多行ss:///vmess://; - 加
User-Agent很重要——不少机场对空 UA 会返回网页/错误而不是订阅; - 支持主备地址(
URL/URL_BACKUP),主地址挂了自动回退。
6.3 解析节点
机场常见两种协议:Shadowsocks 与 VMess。
1 | // ss://Base64(method:password@server:port)#name |
注意:这里按 JustMySocks 的传统 SS 格式(整段
method:pass@host:port都在 Base64 里)解析。
不同机场的ss://可能是 SIP002 格式(ss://Base64(userinfo)@host:port),需相应调整parseSS。
6.4 渲染 Mihomo 模板
把解析出的节点拼进一份你自己的完整配置——这部分是「私货」,可以尽情定制:mixed-port、tun、dns(fake-ip + 防泄漏)、proxy-groups(url-test 自动测速)、rules(GEOSITE/GEOIP 分流、去广告)等。示例还顺手拉取了 Tailscale 的 DERP 节点 IP,
为 *.ts.net 加直连规则(用不到可删)。完整模板见附录 A。
1 | function generateClashYaml(proxies) { |
七、配置 wrangler.jsonc
1 | { |
binding名(SUBSCRIPTION_URL)必须和代码里的env.SUBSCRIPTION_URL完全一致;id占位符在下一步创建 KV 后填入;compatibility_date用近期日期即可。
八、创建 KV 并写入配置
8.1 登录
1 | npx wrangler login # 浏览器授权 |
8.2 创建 KV 命名空间
1 | npx wrangler kv namespace create SUBSCRIPTION_URL |
输出里会给出一个 id,形如:
1 | { "binding": "SUBSCRIPTION_URL", "id": "0123456789abcdef0123456789abcdef" } |
把这个 id 填回 wrangler.jsonc 的 kv_namespaces[0].id。
8.3 写入 Token 与订阅地址(写到远端 KV)
1 | NSID="0123456789abcdef0123456789abcdef" # 换成你的 KV id |
随机生成一个 Token:
1 openssl rand -base64 24 | tr -dc 'A-Za-z0-9' | head -c 32; echo
要点:写远端 KV 一定带 --remote;不带 --remote(或带 --local)写的是本地 dev 的模拟存储。
九、绑定自定义域名
wrangler.jsonc 里已经声明:
1 | "routes": [{ "pattern": "sub.example.com", "custom_domain": true }] |
前提:example.com 这个 zone 已经在你的 Cloudflare 账号里且状态 Active。
满足后,部署时 custom_domain: true 会自动创建 sub.example.com 的路由并签发证书。
暂时没有自定义域名也能用:删掉整个
routes段,部署后用 Cloudflare 自动分配的https://jms-sub.<你的子域>.workers.dev/?token=...访问。
十、部署与验证
1 | npx wrangler deploy |
成功输出里会看到:
1 | Deployed jms-sub triggers |
验证(自定义域名首次可能要等几十秒签发证书):
1 | # 正确 token → 200,返回一大段 YAML |
看到 YAML 开头的 mixed-port: 7890 ... 和错误 token 的 401,即大功告成。
把订阅地址填进客户端即可。
十一、踩坑记录(真实遇到的)
自定义域名返回
Hello World!或别人的内容
该 hostname 之前被另一个 Worker 的路由 / 自定义域名占用了。
去 Cloudflare 控制台 → 旧 Worker → Settings → Domains & Routes,把对应绑定删掉,再重新deploy。dig sub.example.com解析出198.18.x.x
这是本机正在运行的 Clash/Mihomo 的 fake-ip,不是真实解析,属正常现象。
想看真实情况可以换台没开代理的设备,或用curl --resolve指定 Cloudflare 真实 IP。拉取订阅失败 / 返回空
- 给
fetch加User-Agent(很多机场对空 UA 返回网页); - 确认订阅地址直接访问返回的是 Base64(不是 JSON / HTML);
- 配上
URL_BACKUP备用域名兜底。
- 给
Token 不生效,谁都能访问
KV 里没写TOKEN。代码逻辑是「TOKEN为空则不鉴权」,务必写入。kv key put写错了位置
写远端要带--remote;--local写的是wrangler dev的本地模拟存储,部署后读不到。ss://解析不出节点
你的机场可能用 SIP002 格式,需要按 6.3 的提示改写parseSS。
十二、日常维护:换机场 / 换 Token 不用重新发版
配置都在 KV 里,改完即时生效,无需 deploy:
1 | NSID="你的 KV id" |
只有改了 src/index.js(模板 / 解析逻辑)才需要重新 npx wrangler deploy。
排错看实时日志:npx wrangler tail。
十三、安全与成本
- Token 是访问凭证,别写进公开仓库、别截图泄露;想吊销就改 KV 里的
TOKEN。 - Worker 输出的仍是明文 Mihomo 配置(含节点凭证),靠 HTTPS 传输保护,请勿把订阅地址公开分享。
wrangler.jsonc里的 KVid不是机密,但 Token / 机场地址在 KV 里,不要把它们硬编码进代码或提交到 Git。- 成本:Workers 免费版每天 10 万请求、KV 免费版每天 10 万次读,个人用绰绰有余。
附录 A:完整 src/index.js
直接复制即可使用。代码本身不含任何敏感信息——机场地址与 Token 全部来自 KV。
1 | const PROTOCOL_SS_PREFIX = "ss"; |
附录 B:占位符对照表(按你的实际值替换)
| 占位符 | 含义 |
|---|---|
sub.example.com |
你的自定义订阅域名 |
example.com |
已托管在 Cloudflare 的 zone |
YOUR_TOKEN |
访问 Token(随机生成) |
<YOUR_KV_NAMESPACE_ID> / NSID |
wrangler kv namespace create 输出的 KV id |
jmssub.net/...service=XXXXXXX&id=xxxx... |
你的机场订阅地址 |
jms-sub |
Worker 名(随意) |