给网站加上订阅网址转换
背景
最近给自己的 secnone.com 顺手加了一个订阅网址转换服务,地址放在 sub.secnone.com。
缘起很简单,我之前用 V2rayU, 但是不迭代了,只能改用 clash verge. 结果有些免费订阅源直接丢给 Clash Verge 并不好用,要么格式不认,要么里面混了 VLESS, Hysteria2, Trojan 这些协议。我需求就是把一个原始订阅 URL 转成 Clash 可以直接导入的 YAML。
最开始我想得比较复杂,准备在 secnone-server 这个 Express app 里加一个 /proxy-sub route,先把订阅拉下来,做 Base64 decode, protocol filter, dedupe, limit,然后再交给 subconverter。后来发现压根不需要。
开始的方案
旧方案是这样的:
Clash Verge
-> https://sub.secnone.com/sub?target=clash&url=...
-> nginx
-> Docker subconverter
-> 原始订阅源
subconverter 本身是一个挺经典的工具,我先用 Docker 跑在服务器上, 然后用 nginx 把公网的 https://sub.secnone.com/sub 反代到本地的 127.0.0.1:25500。
这个对一些旧格式订阅是能工作的,但有些源处理不了, 直接走 subconverter 经常返回报错:
No nodes were found!
问题主要出在 subconverter 对 plain text subscription, Base64 wrapper 和新协议支持不够。
中间绕了一下
我一度准备给我的网站 Express 加一层 proxy-sub:
subconverter
-> secnone-server /proxy-sub
-> fetch 原始订阅
-> decode
-> filter
-> 返回纯文本节点
这个方案有点费力不讨好,为了一个订阅转换功能,把 SSRF 防护、redirect 校验、timeout、response size limit 都塞进主站 Express,维护很麻烦。
换成 Sublink Worker
后来试了 Sublink Worker,发现它可以直接处理 HTTP/HTTPS subscriptions,也支持 VLESS, Hysteria2, Trojan。就它了。
最后架构变成:
Clash Verge
-> https://sub.secnone.com/clash?config=...
-> nginx
-> Docker Sublink Worker
-> 原始订阅源
接口为:
https://sub.secnone.com/clash?config=<encoded_url>
我用的是 Docker Compose。因为这个服务会开放给大家用,生产配置里和机器相关的细节就不完整贴了,关键点是:worker 只监听本机端口,外面统一走 nginx HTTPS,Redis 只作为 worker 的缓存依赖。
services:
worker:
image: ghcr.io/7sageer/sublink-worker:latest
ports:
- "127.0.0.1:8787:8787"
environment:
REDIS_HOST: redis
REDIS_PORT: 6379
depends_on:
- redis
restart: unless-stopped
redis:
image: redis:7-alpine
command: ["redis-server", "/usr/local/etc/redis/redis.conf"]
volumes:
- redis-data:/data
restart: unless-stopped
volumes:
redis-data:
端口只绑定在 127.0.0.1:8787,公网不直接暴露 Docker 端口,外面只走 nginx HTTPS。这样主站 Express 不需要关心订阅转换逻辑,sub.secnone.com 只负责这一类转换请求。
nginx 配置
sub.secnone.com 的 HTTPS server 里核心反代类似于:
server {
server_name sub.secnone.com;
location / {
proxy_pass http://127.0.0.1:8787;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# HTTPS 证书部分交给 certbot 生成和维护。
}
一个小坑是不能只配:
location = /sub {
proxy_pass http://127.0.0.1:8787;
}
因为 Sublink Worker 的接口是 /clash, /surge, /singbox 这些 path。location = /sub 只会匹配 /sub,新的接口根本不会进去。所以这里要用 location / 反代整个 sub.secnone.com。
最终使用方式
网站已经配置好,未来大家配置梯子的 url 直接用如下即可:
https://sub.secnone.com/clash?config=<原始订阅 URL 做 URL encode>
