把个人站和个人服务一起挂上 Cloudflare:Workers 与 Tunnel 的分工
静态的交给 Workers,长连接的交给 Tunnel,一个域名把两件事都办了。
这个站要同时干两件事:一个是随时能打开的个人主页和简历,另一个是把跑在云服务器上的多人游戏暴露到公网。这两件事性质完全不同——前者是纯静态文件,后者是一个需要长期在线的 Node 进程,还带着 WebSocket。所以我没有用一套方案硬扛,而是拆成两条路走。
静态的部分:Workers 静态资源
主页、简历、项目页加起来只有几个 HTML 文件和几张图片,没有数据库,也没有登录。这种情况下最省事的做法是把整个目录交给 Cloudflare Workers 的静态资源托管,配置文件里声明目录和自定义域名,然后一条命令部署:
{
"name": "wxekre",
"assets": {
"directory": "./网页",
"html_handling": "auto-trailing-slash",
"not_found_handling": "404-page"
},
"routes": [
{ "pattern": "wxekre.cn", "custom_domain": true },
{ "pattern": "www.wxekre.cn", "custom_domain": true }
]
}
改完 HTML 跑一次 wrangler deploy 就全量更新。没有服务器要续费、要打补丁、要担心到期,也不存在「证书过期了整站打不开」这种事。
auto-trailing-slash让/resume直接对应resume.html:链接写起来干净,文件本身还是普通静态文件,编辑起来没有任何门槛。
动态的部分:为什么不能直接把端口暴露出去
游戏服务是 Node.js + Socket.io,长连接、要求低延迟、必须一直在线。最直觉的做法是在云服务器上开个端口,把域名的 A 记录指过去。我一开始就是这么干的,然后撞上了下面这个坑。
坑:.cn 域名 + 大陆服务器
把 game 子域解析到服务器公网 IP 之后,浏览器打开返回的不是游戏,而是一张备案不合规的提示页;换成直接在服务器上用 curl http://127.0.0.1:3000 访问,反而是正常的 200。
这说明被拦的不是端口,而是域名——云厂商是在 HTTP 层按请求里的 Host 头做检查的。只要请求还带着这个域名进到那台服务器,就过不去。
Cloudflare Tunnel:让请求不走入站端口
Tunnel 的思路正好绕开这一点:服务器上的 cloudflared 主动向 Cloudflare 边缘建立几条出站长连接,外部请求先到 Cloudflare 边缘,再由隧道送回服务器本机。整个过程服务器的入站端口一个都不用开,那层基于 Host 的检查自然也不会被触发。
配置本身很短,/etc/cloudflared/config.yml 里写清楚域名和本地服务即可:
tunnel: <tunnel-id>
credentials-file: /etc/cloudflared/<tunnel-id>.json
ingress:
- hostname: game.example.com # 示例;实际已统一为 wxekre.cn/game/
service: http://127.0.0.1:3000
- service: http_status:404
然后把 DNS 里那条指向服务器 IP 的 A 记录换成指向隧道的 CNAME,并且打开代理(橙色云朵):
| 类型 | 名称 | 内容 | 代理状态 |
|---|---|---|---|
| CNAME | game | <tunnel-id>.cfargotunnel.com | 已代理 |
服务用 systemd 托管,加上 Restart=on-failure,服务器重启后隧道会自动重连,不需要人工盯着。
最后的分工
| 用途 | 承载方式 | 地址 |
|---|---|---|
| 个人主页 / 在线简历 | Workers 静态资源 | wxekre.cn |
| 多人实时对战游戏 | Nginx → 本机 3000 | wxekre.cn/game/ |
一句话总结:能静态就静态,必须动态就用隧道,别让任何东西直接裸奔在公网端口上。