Cloudflare Telegram Web Proxy 部署教程:Pages 主程序 + Worker 转发 + 自动优选 DNS 全流程

Cloudflare Telegram Web Proxy 部署教程:Pages 主程序 + Worker 转发 + 自动优选 DNS 全流程
luoli前言
cftgwebproxy 是一个开源的 Telegram Web Proxy 部署方案:用 Cloudflare 的免费资源把 TG 的 MTProto 代理跑起来,客户端填一个自己的域名就能连。
- 项目地址:https://github.com/xinyitang3/cftgwebproxy
- 开源协议:MIT
- 技术栈:Cloudflare Pages(主程序)+ Workers(转发层、DNS 定时任务)+ Cloudflare DNS API
- 上游:协议逻辑来自 ToiCF/CF-Workers-TGProxy
它和我之前写过的单文件 Worker 代理方案不太一样,最大的特点是拆成三层:
| 组件 | 跑在哪 | 职责 |
|---|---|---|
telegram_webproxy.js |
Cloudflare Pages(须改名 _worker.js) |
真正的代理主程序,负责 MTProto 帧协议与连接 Telegram 各 DC |
webproxy-relay.js |
Cloudflare Workers | 纯转发层,让你的优选域名能接到 Pages 主程序上 |
domain-dns.js |
Cloudflare Workers + Cron | 定时把优选 IP 同步到域名的 A 记录 |
为什么要这么拆?项目 README 给了两个理由,都挺实在:
- 避开 1101 / 522:Worker 直接长时间挂着 WebSocket 代理,容易触发 Worker 异常(1101)和源站超时(522),把主程序放 Pages 能显著降低这类报错;
- 额度分开算:按作者的说法,Pages 和 Worker 的请求额度是分开计算的,拆开后总容量更大。
最终链路是这样:
1 | Telegram 客户端 |
本文依据仓库 README 与三个脚本的源码整理,环境变量规则、接口路径、校验逻辑均以源码为准。部署时请以仓库最新提交为准。
准备工作
开干之前,把这几样东西备齐:
| 项目 | 说明 |
|---|---|
| Cloudflare 账号 | 建议准备 2 个:一个放 Pages 主程序,另一个放 Worker 转发层(README 的建议做法,能进一步分散风控与额度) |
| 一个托管在 Cloudflare 的域名 | 用来做「优选域名」。关键限制:这个域名所在的 Zone 必须和 Worker 在同一个 Cloudflare 账号下,否则加不了路由 |
| 优选 IP 源 | 一个返回 IP:Port 文本列表的接口(每行一条,可带 # 注释)。用于第三层的自动 DNS |
| 本地环境 | 只用面板操作的话什么都不用装;想用命令生成密钥,需要 openssl |
先生成两个密钥,后面两个组件都要用:
1 | # BOOT:建议 ≥16 位随机字符串 |
环境变量的两条硬规则(源码里写死的校验,填错直接 500):
SECRET:必须是 32 位小写十六进制(0-9a-f),或者dd+ 32 位十六进制(共 34 位)BOOT:任意非空随机字符串,建议 16 位以上
关于
dd前缀:它是 MTProxy 的可选模式,作用是启用随机填充(Random Padding),把包长随机化。填的时候必须带上dd两个字面字符,共 34 位;不带前缀就是标准的 32 位 secret。客户端里要填和这里完全一致的字符串。
第一步:部署 Pages 主程序
这一层是整个方案的核心。
1.1 新建 Pages 项目
Cloudflare 控制台 → Workers 和 Pages → 创建 → Pages → 上传资产(直接上传,最省事)。
1.2 文件必须改名成 _worker.js
这是最容易踩的坑:把 telegram_webproxy.js 重命名为 _worker.js,放在上传目录的根目录。
原因:Pages 的 Functions 高级模式只认根目录下这个固定文件名,名字不对就完全不会执行你的代码,页面只会返回静态 404 或空白页。
1 | 上传目录/ |
1.3 配置两个加密变量
项目 → 设置 → 变量和机密(Environment variables),两个都建议用**加密(Secret)**方式添加:
| 变量名 | 类型 | 值 |
|---|---|---|
BOOT |
机密 | 上面 openssl rand -hex 24 的输出 |
SECRET |
机密 | 上面 openssl rand -hex 16 的输出(可加 dd 前缀) |
保存后重新部署一次,让变量生效。
1.4 验证主程序
部署完成后访问:
1 | https://<项目名>.pages.dev/health |
返回下面这行就说明主程序活了:
1 | {"ok":true} |
如果这里就是 500,别急着往下做——先回去检查 BOOT / SECRET 的格式(最常见就是 SECRET 长度不对或包含大写字母)。
1.5 主程序到底做了什么(看懂再调)
顺手把源码里的关键设计说一下,排障时很有用:
- 对外接口只有四个:
/(桥接页)、/health、/api/v1/session、/api/v1/ws,其他路径一律 404 /api/v1/session:用BOOT派生的 Bearer 令牌做鉴权,POST 一段二进制握手数据,成功后通过响应头X-Session-Token返回会话令牌/api/v1/ws:真正的数据通道,要求Upgrade: websocket,且子协议必须形如tproxy-lane-v1.<令牌>.<通道号>;Origin若不等于自身 Origin 会被拒绝- 帧协议:8 字节帧头(类型 + 会话 ID + 长度),单帧上限 1 MB,单条消息最多 4096 帧;每个令牌最多 128 条并发通道、累计 4096 个通道号
- 安全头:返回的桥接页带
sandbox的 CSP,且frame-ancestors只允许http://127.0.0.1:*——这是给 Telegram Desktop 的本地页面做嵌入用的
所以:直接用浏览器打开 https://你的域名/ 看到一个几乎空白的 “Connection” 页是正常的。 那个页面的完整版只有在带上正确签名的 ?bridge= 参数时才会渲染,而签名由转发层按 SECRET 计算,客户端会通过本地页自动完成——不需要你手动拼。
第二步:部署 Worker 转发层
这一层的唯一任务是:把你的域名请求原样转给 Pages 主程序,并把返回内容里的地址改写回你的域名。
2.1 新建 Worker
切换到第二个 Cloudflare 账号(如果你按建议分了两个),Workers 和 Pages → 创建 Worker,把 webproxy-relay.js 的内容整体粘贴进去,部署。
2.2 配置变量
| 变量名 | 类型 | 值 |
|---|---|---|
HOST |
文本(明文) | Pages 主程序的默认域名,形如 xxxx.pages.dev,不要带 https://、不要带路径、不要填自定义域名 |
SECRET |
机密 | 必须和 Pages 主程序的 SECRET 完全一致 |
HOST 填错的典型症状是:域名访问全 404 或 502。代码里会自动剥掉协议头和端口,但填自定义域名仍然会错——它要求的是 *.pages.dev 那个原始域名。
2.3 绑定路由
Worker → 设置 → 域和路由 → 添加路由,把你的优选域名挂上去:
1 | 你的域名/* ← 对应根域名 @ |
两条都加,覆盖面最全。注意:该域名的 Zone 必须和这个 Worker 在同一账号,否则界面根本不会让你选这个 Zone,这是 Cloudflare 的限制,不是项目的问题。
2.4 验证转发
1 | curl https://你的域名/health |
能通说明「域名 → Worker → Pages」这条链已经打通了。
2.5 转发层做的三件细活
源码里有几个处理值得知道,出问题时能对上号:
- 重签桥接令牌:客户端带的
?bridge=是按「当前域名」用 HMAC-SHA256 算的,转发层会校验并在转发时改写成按pages.dev域名计算的版本,这样桥接页在自定义域名下也能正常渲染; - 改写回跳地址:遇到 3xx 重定向,把
Location里的pages.dev换回你的域名; - 改写 HTML:对
text/html响应做字符串替换,把https://页域名与wss://页域名全部换回你的域名,同时清掉Content-Length、Content-Encoding、ETag等会因为改写而失效的头。
它还严格校验 WebSocket 的 Origin:只有等于 https://你的域名 才放行,否则直接 404。所以用其他域名或裸 IP 去连是连不上的,这是设计如此。
第三步:让优选 IP 自动更新(可选但强烈推荐)
Cloudflare 的 Anycast 会让你落到就近节点,但不同 IP 段在你的网络环境下质量差很多。这一层用 Cron 定时把「测得快的 IP」写进 A 记录,避免手动维护。
3.1 建一个有 DNS 编辑权限的 Token
Cloudflare 控制台 → 我的个人资料 → API 令牌 → 创建令牌,模板选「编辑区域 DNS」:
- 权限:
Zone→DNS→Edit - 区域资源:只选你的优选域名这一个 Zone(最小权限)
- 创建后把令牌复制保存
顺手记下这个 Zone 的 Zone ID(域名概览页右下角)。
3.2 新建 Worker 并改三个常量
再建一个 Worker,粘贴 domain-dns.js,改开头这几个常量:
1 | // 优选 IP 源:返回文本,每行 IP:Port,可带 # 注释 |
3.3 配置变量与 Cron
| 变量名 | 类型 | 值 |
|---|---|---|
ZONE_ID |
文本 | 你的优选域名 Zone ID |
API_TOKEN |
机密 | 上一步创建的 DNS 编辑令牌 |
然后在 Worker → 触发器 → Cron 触发器 里加一条,例如每 30 分钟:
1 | */30 * * * * |
免费计划同样支持 Cron 触发器,但每次触发都会计入请求次数,别设得过于频繁(比如每分钟一次完全没必要,优选 IP 的变动频率远低于此)。
3.4 这层脚本的逻辑(便于自查)
- 拉取 IP 源文本,只保留端口为 443 的条目(其他端口直接忽略)
- 对
@和*两条记录:分页读出当前所有 A 记录 → 删掉不在目标列表里的 → 补上缺的 - 新记录 TTL 设为 60 秒,改动后一分钟内全网生效
- 会容忍 Cloudflare 的「记录已存在 / 内容重复」类错误码,不会因为并发重复创建而整体失败
3.5 验证
- Worker 的 日志 里能看到每次 Cron 的执行结果(同步了哪些 IP、有没有报错)
- 本地查一下记录:
1 | nslookup 你的域名 1.1.1.1 |
返回的 IP 应该和你的优选 IP 源里 443 那批对得上。
第四步:客户端配置
到这一步,服务端已经就绪。客户端只需要三个字段:
| 字段 | 填什么 |
|---|---|
| 服务器 / Server | 你的优选域名(例如 example.com) |
| 端口 / Port | 443 |
| 密钥 / Secret | 和 Pages 主程序一致的 SECRET;如果用了 dd 前缀,必须连 dd 一起填满 34 位 |
Telegram Desktop(Windows / macOS / Linux)
设置 → 高级 → 连接类型 → 代理 → 添加代理 → 类型选 MTProto → 填入上面三项 → 保存 → 选中该代理。
Telegram Android / iOS
设置 → 数据和存储 → 代理(部分版本在「聊天设置 → 代理」)→ 添加代理 → MTProto → 填入三项 → 启用。
判断是否真的连上了
- 客户端代理列表里该条目显示已连接 / Connected
- 打开任意频道能正常收发消息
- 服务端侧可以访问
https://你的域名/health确认主程序存活
如果客户端一直转圈:先确认 https://你的域名/health 返回 {"ok":true},再检查客户端里的 secret 有没有少一位或多一位(尤其 dd 前缀很容易漏)。
额度与性能
| 项目 | 说明 |
|---|---|
| Workers 免费额度 | 每天 10 万次请求,日常个人使用远远够 |
| Pages 免费额度 | 同样有每日请求上限;作者说明与 Worker 分开计算 |
| Cron | 每次触发算一次请求,频率别拉满 |
| WebSocket | 长连接本身就是低请求数、高流量模型,比短轮询省额度 |
几个实测向的优化建议:
- 优选 IP 源的质量决定了 90% 的体验。换几个源对比延迟再定,别用来源不明的列表
- Cron 频率 30–60 分钟足够,IP 失效通常不会在几分钟内发生
- 如果你只是自己用,只加
@根域名那条记录也行,少一条记录少一次 API 调用 - 出现 1101 / 522 时,优先检查是不是有人在猛拉流量,或 Workers 侧并发太高;主程序留在 Pages 是这套方案的核心思路,不要改动
常见故障排查
/health 返回 500
BOOT 或 SECRET 缺失、格式不对。重点查 SECRET:必须 32 位小写十六进制,或 dd + 32 位;有大写字母、长度 31/33 都会失败。
访问域名全部 404
三种可能,按顺序排查:
- Worker 路由没加(或缺
*通配那条) - 域名 Zone 与 Worker 不在同一 Cloudflare 账号
HOST填错了(必须是最原始的*.pages.dev,不是自定义域名)
页面能开,但 Telegram 一直连不上
- Worker 里
SECRET与 Pages 里不一致 —— 这是最常见原因 - 客户端 secret 填错(
dd前缀漏填或重复) - WebSocket 被中间层拦了:转发层要求
Origin严格等于https://你的域名
从浏览器打开 https://你的域名/ 是一片空白
正常现象。桥接页需要合法签名的 ?bridge= 参数才会完整渲染,客户端会自动处理,不需要手动拼。
DNS 不自动更新
API_TOKEN权限不足(必须含该 Zone 的 DNS Edit)ZONE_ID填错- Cron 没启用,或最近一次执行报错(看 Worker 日志)
- IP 源返回的条目里没有
:443,脚本会全部过滤掉
偶尔 1101 / 522
1101 是 Worker 运行时抛异常,522 是源站超时。两个都属于「Worker 直接扛代理」的固有问题,这也是本方案把主程序放 Pages 的原因。降低并发、别把域名公开给大量陌生人使用,能明显缓解。
想换 SECRET 怎么办
必须同时改两处(Pages 主程序 + Worker 转发层),改完重新部署,然后通知所有客户端更新 secret。只改一边会导致全部连不上。
安全与注意事项
SECRET就是这台上网的钥匙:拿到「域名 + secret」的任何人都在白嫖你的代理,不要发到公开群组或仓库里API_TOKEN只给最小权限:只勾选该 Zone 的 DNS 编辑,别用全局 API Key- 三个组件建议分账号部署,一处泄露不至于全线崩塌
- 密钥用「机密(Secret)」类型存,不要写死在代码里再上传
- 本项目用于合法的通信与学习用途,请自行遵守所在地法律法规以及相关平台的服务条款
小结
把整套流程压缩成一张清单:
1 | ① Pages:telegram_webproxy.js → 改名 _worker.js → 传根目录 |
这套方案最值得学的地方,其实是它「用 Pages 扛长连接、用 Worker 只做转发」的工程取舍——1101/522 这类边缘平台的老问题,靠架构拆分绕过去,比在单文件里加一堆重试优雅得多。想自己搭一套 TG 代理的话,按上面四步走,半小时能跑通。
参考资料
- cftgwebproxy 项目仓库(GitHub)
- 上游协议实现:ToiCF/CF-Workers-TGProxy
- Cloudflare Pages Functions 高级模式(_worker.js)
- Cloudflare Workers 路由配置
- Cloudflare Workers Cron 触发器
- Cloudflare API:DNS 记录管理
- Telegram MTProto 代理说明












