有趣的翻墙模式————Workers

最近在利用 Oracle 搭建代理节点的时候,发现了一种有趣的翻墙方式:利用 Cloudflare 的 Workers 进行反向代理,觉得挺神奇的,探索了一下 Workers。

何为 Workers

Cloudflare Workers 是 Cloudflare 提供的一项服务,它允许在 Cloudflare 的全球网络边缘运行 JavaScript(以及其他兼容 WebAssembly 的语言)代码。这意味着你可以编写代码,它会在接近用户的位置执行,从而降低延迟并提高性能。Workers 特别适合构建各种网络应用和 API,以及修改现有的网站请求和响应。

不过这里要先泼一盆冷水:Workers 本身不是一个可以随便拨号出去的 VPS。它更像是 Cloudflare 边缘上的一个函数,真正访问互联网的那台机器还是你的 Oracle VPS。Workers 在这个场景里做的事情,是先把客户端的请求接住,再按你写的逻辑转发给 Oracle 上的服务。

所以整条链路其实是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
Client
|
| wss://proxy.example.com/your-secret-path
v
Cloudflare Worker
|
| fetch(http://oracle-ip:port)
v
Oracle VPS(Xray / Nginx)
|
v
Internet

这也是为什么它能“翻墙”:客户端先连到 Cloudflare 的域名和网络,Worker 再把流量转发到 Oracle,最后由 Oracle 出去访问目标网站。它没有改变“出口在 Oracle”这个事实。Oracle 的节点挂了,Worker 写得再花也只能返回一个错误页。

还有一个很重要的边界:Workers 适合 HTTP/HTTPS 和 WebSocket,不适合拿来硬扛任意 TCP/UDP。 我现在这个场景是 WebSocket 类节点,所以可以用 fetch() 把 Upgrade 请求原样转给源站;如果你用的是裸 TCP、Hysteria、WireGuard 这类东西,就不是写个 Worker 能解决的,Worker 不是 Spectrum。

创建 Workers

创建方式还是两种:一种用 CLI,一种在 Cloudflare 的 Dashboard 里点出来。原文里的 Cil 是手滑,正确是 CLI。

方式一:用 CLI 创建

先确认本机有 Node.js,然后在终端里执行:

1
2
3
4
5
npm create cloudflare@latest -- workers-proxy
cd workers-proxy
npx wrangler login
npx wrangler dev
npx wrangler deploy

第一次执行 wrangler login 的时候,浏览器会弹出来让你授权 Cloudflare 账号。wrangler dev 是在本地跑一个开发服务器,wrangler deploy 才是真正把 Worker 推上去。

创建完项目后,核心文件一般是 src/index.js,配置文件是 wrangler.jsonc。我的配置大概长这样:

1
2
3
4
5
6
7
8
9
10
{
"$schema": "./node_modules/wrangler/config-schema.json",
"name": "workers-proxy",
"main": "src/index.js",
"compatibility_date": "2026-10-31",
"vars": {
"UPSTREAM": "http://203.0.113.10:8080",
"PROXY_PATH": "/your-secret-path"
}
}

这里的 203.0.113.10 只是文档里的示例地址,实际要换成你的 Oracle 地址。UPSTREAM 建议只写 协议 + IP + 端口,不要把路径也塞进去;路径由后面的 Worker 代码原样保留下来。PROXY_PATH 是我给客户端用的入口路径,最好别写 /proxy 或 /ws 这种一眼就能猜到的值,虽然它不算真正的认证,但至少能少一点扫描噪音。

方式二:用 Dashboard 创建

不想碰 CLI 也完全可以,Dashboard 的流程大概是:

  1. 进入 Cloudflare 的 Workers & Pages;
  2. 选择 Create,创建一个 Worker;
  3. 进入代码编辑器,把后面的代码贴进去;
  4. 在 Settings 里的 Variables and Secrets 添加 UPSTREAM 和 PROXY_PATH;
  5. 点击 Deploy。

Dashboard 适合第一次试水,CLI 适合后面反复改代码、看日志和做版本管理。我自己的习惯是先用 Dashboard 跑通一个最小版本,确认链路没问题,再把它搬到命令行项目里。

反向代理的核心代码

这个场景不需要什么大而全的框架,核心就是把请求的目标地址换掉,然后交给 fetch():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
export default {
async fetch(request, env) {
const incoming = new URL(request.url);
const proxyPath = env.PROXY_PATH || "/ws";

if (!env.UPSTREAM) {
return new Response("UPSTREAM is not configured", { status: 500 });
}

if (incoming.pathname !== proxyPath) {
return new Response("Not Found", { status: 404 });
}

const upstream = new URL(env.UPSTREAM);
const target = new URL(request.url);

target.protocol = upstream.protocol;
target.hostname = upstream.hostname;
target.port = upstream.port;

return fetch(new Request(target, request));
},
};

这段代码其实只做了三件事:

  • 检查请求路径是不是我指定的入口;
  • 把请求的 host 换成 Oracle 的 host;
  • 用原来的 request 构造一个新的 request,并交给 fetch()。

我特意没有从 query 参数里读取目标地址,因为那样会把一个私人 Worker 变成开放代理,别人随便传一个 URL 就能用你的 Cloudflare 账号去访问任意网站。反向代理和开放代理的差别,往往就在这一行代码里。

new Request(target, request) 这种写法会尽量保留原来的 method、headers 和 body,WebSocket 升级需要的 Upgrade 头也会跟着走。这里不要把请求重建成一个普通的 GET,也不要把响应先 .text() 掉;你一旦把 Upgrade 或响应流拆开,WebSocket 很可能就接不上了。

如果客户端会把路径带成 /your-secret-path/something,把判断改成 startsWith(proxyPath) 就行。不过如果你不打算接受子路径,还是用精确匹配更稳,免得随手多暴露一些入口。

Oracle 侧要准备什么

Worker 只负责转发,真正的服务还得在 Oracle 上跑起来。

假设你的 Xray 监听在 127.0.0.1:10000,前面用 Nginx 接 WebSocket,那么 Nginx 里至少要有一段类似这样的配置:

1
2
3
4
5
6
7
location = /your-secret-path {
proxy_pass http://127.0.0.1:10000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}

这里最容易漏的是两行:

1
2
3
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

少了它们,普通 HTTP 请求可能看起来没问题,但 WebSocket 握手会失败。很多“Worker 配置没问题,客户端就是连不上”的情况,最后都栽在源站 Nginx 没有把 Upgrade 传下去。

如果 Worker 的 UPSTREAM 写的是 https://...,源站证书必须是 Cloudflare 能验证的证书,自签证书一般不要拿来省事。想先用最简单的路径跑通,可以先用 http://Oracle-IP:8080 这种内部转发方式,等链路稳定了再加 TLS。

这里还得补一个安全上的细节:Worker 不是透明的 TCP 隧道。 客户端到 Cloudflare 的 TLS 会在 Cloudflare 边缘被拆开,然后 Worker 再向 Oracle 发起一条新的连接。如果 UPSTREAM 用 HTTP,那 Cloudflare 到 Oracle 这一段就是明文;就算换成 HTTPS,也不等于客户端和 Oracle 之间天然端到端加密。协议本身有加密的话问题不大,如果特别在意这一层,就别把 Workers 当成“什么都看不见的纯转发”。

客户端如果是 VLESS + WebSocket + TLS,配置大概是:

配置项 值
地址 proxy.example.com
端口 443
传输方式 ws
路径 /your-secret-path
TLS 开启

注意,客户端填的是 Worker 的域名,不是 Oracle 的 IP。Oracle 的地址只应该出现在 Worker 的 UPSTREAM 变量里。

绑定自定义域名

workers.dev 的地址可以先拿来测试,但它不一定适合长期使用:有些网络环境会屏蔽这个域名,而且一串随机子域名也不太好记。更稳的做法是给 Worker 绑定一个自己的子域名,比如:

1
proxy.example.com

在 Dashboard 里进入 Worker 的 Settings → Domains & Routes → Add → Custom Domain,填入子域名即可。Cloudflare 会自动创建对应的 DNS 记录和证书,不需要你自己去申请一张证书再上传。

这里有个很容易绕进去的坑:**UPSTREAM 不要填成 Worker 自己的域名**。比如你把 proxy.example.com 绑给了 Worker,结果 UPSTREAM 也写 https://proxy.example.com,那 Worker 发出去的请求又会回到自己,轻则 522,重则直接绕进循环。源站地址要么写 Oracle 的公网 IP,要么写一个没有挂到这个 Worker 上的独立域名。

测试和排错

最简单的测试方式不是拿浏览器点,而是直接用客户端或 curl 看链路到底断在哪一层。普通 HTTP 请求返回 400、404 不一定代表失败,因为 Xray 可能本来就不接受这种请求;关键是看 Worker 有没有把请求送到源站。

现象 优先检查
404 PROXY_PATH 和客户端路径是否完全一致
500 Worker 的 UPSTREAM 变量是否配置了
521 / 522 Oracle IP、端口、防火墙是否能从 Cloudflare 访问
525 UPSTREAM 用了 HTTPS,但源站证书有问题
WebSocket 握手失败 Nginx 是否转发 Upgrade / Connection
能连但很慢 多了一层 Cloudflare 到 Oracle 的链路,未必比直连快

我一开始看到 404 的时候还以为代码写错了,后来发现是客户端里的路径多了个斜杠;又试过一次 522,结果是 Oracle 的安全组没有放行端口。Cloudflare 的错误码其实已经把问题指得挺明白了,先别急着改代码,顺着链路一段一段看就行。

额度和边界

按照 Cloudflare 目前的文档,Workers Free 计划是每天 100,000 个请求,每次调用有 10ms 的 CPU 时间额度;WebSocket 握手算一次请求,后续通过这个连接传输的消息不再单独计请求。Paid 计划则是 5 美元一个月起步,包含 10,000,000 个请求。这个额度对个人折腾够用,但不要把它当成大流量视频隧道。

还有一个很现实的点:套一层 Workers 不等于加速。 Client 到 Cloudflare、Cloudflare 到 Oracle、Oracle 到目标网站,这三段都是真实网络。如果 Oracle 本身离你很远,或者 Cloudflare 回源绕路,最终的延迟可能比直连还高。它最大的价值是换一个入口、隐藏源站地址、利用 Cloudflare 的边缘网络,而不是凭空变快。

最后提醒一句,PROXY_PATH 只是把入口藏得深一点,不是认证。真正保护节点的仍然是 Xray 的 UUID / 密码、源站防火墙和访问控制。也尽量不要把公共 Worker 写成能访问任意 URL 的开放代理,Cloudflare 的使用条款和滥用政策不是摆设。自己用、限好流量、别把入口到处发,已经能避开大部分没必要的麻烦。

写在最后

我一开始觉得 Workers 反向代理挺神奇,好像 Cloudflare 凭空给我变了一台代理服务器出来。把代码写完之后才发现,核心逻辑真的就这么几行:接住请求、改掉目标地址、fetch 出去。真正让人有“哇”的感觉的,不是代码本身,而是 Cloudflare 已经把边缘网络、TLS、WebSocket 这些东西铺好了,我只需要在中间接一段自己的逻辑。

当然,它也不是万能钥匙。节点本身要先跑通,源站要允许 Cloudflare 访问,客户端要用 WebSocket 这类能过 Workers 的传输方式。把这些前提想清楚之后,再决定要不要多套这一层,通常比一上来就折腾配置要省时间。

嗯嗯,这篇就先到这里。下次如果再把节点、Nginx 和 Worker 的日志串起来看,应该还会有不少可以补充的坑。

参考资料