“Cloudflare OS”这个说法,我一开始是在某个技术社群里看到的,当时还以为 Cloudflare 开源了一个操作系统。后来深入了解才发现,这个词更像是对一种实践的概括:把 Cloudflare 的整套边缘网络能力组合起来,当成一个运行在互联网上的“操作系统”来用。它提供域名解析、内容缓存、边缘计算、对象存储、零信任访问,你只需要把自己的应用接入进去,剩下的网络调度、安全防护、性能优化都由这个“OS”处理。
这篇文章我把自己搭建这套“cloudflare-os”风格个人云平台的完整过程写下来,包括整体架构怎么设计、内网服务如何通过 Tunnel 接入、Workers 和 R2 怎么承担计算与存储、Zero Trust 怎么做统一身份验证,以及我在实际操作中踩过的坑和排查思路。如果你也在搞自托管服务、边缘部署,或者想让自己的服务既有公网入口又不用折腾端口映射,这篇文章应该能给你一份直接抄作业的参考。
1. 整体架构设计:把 Cloudflare 当作一个操作系统来用
1.1 为什么会有“cloudflare-os”这种叫法
按传统理解,操作系统负责管理计算机的硬件和软件资源,提供进程调度、内存管理、文件系统、网络协议栈、用户权限控制。而 Cloudflare 提供的不是装在硬件上的系统,但它把互联网基础设施里的关键能力抽象了出来:HTTP 请求进来,先经过 DNS 解析,再经过 CDN 节点缓存,被 WAF 和防火墙规则过滤,最后路由到源站或 Worker 脚本;存储方面有 KV、D1、R2;访问控制方面有 Zero Trust Access。这些能力组合起来,和操作系统的功能有很强的相似性。
当然,正式文档里没有“Cloudflare OS”这个产品,它更像是一种工程隐喻。我做这套东西的时候,确实把它当成一个“云 OS”来规划:域名解析就是文件系统里的“路径表”,Workers 就是“进程”,R2 和 KV 就是“磁盘”,Zero Trust 策略就是“用户权限”。想清楚这一层,再去排列组件,整个架构就清晰很多,不会被一堆功能模块绕晕。
1.2 架构拆解:控制面与数据面分离
任何一个合格的系统设计都讲究控制面和数据面分离。Cloudflare 天然符合这个模式。控制面是你在 dashboard 上看到的配置中心和 API,它负责下发 DNS 记录、WAF 规则、Tunnel 配置、Worker 路由;数据面是遍布全球的边缘节点,它们负责承载实际的流量请求、缓存静态内容、执行 Worker 代码、回源取数据。
自建服务在这个架构里的位置也很通透:你的 VPS 或家里的小主机只是“源站”,Cloudflare 边缘节点是“前门”。源站不需要暴露公网 IP,也不需要开启任意入站端口,外部请求只会通过边缘节点转发进来。这样就算你源站带宽很小,也能借助边缘节点的缓存和回源优化获得较好的访问体验。
我更愿意把整套系统拆成四个层面来理解,这样后续排错和扩展都有明确归属。
| 层面 | 对应组件 | 承担职责 |
|---|---|---|
| 网络层 | DNS、CDN、Tunnel | 域名解析、内容分发、内网穿透与回源 |
| 计算层 | Workers、Pages Functions | 无服务器脚本、接口聚合、动态路由 |
| 存储层 | R2、KV、D1 | 对象存储、全局键值缓存、关系型数据 |
| 安全层 | Zero Trust、WAF、Bot Management | 身份验证、流量过滤、攻击防护 |
我实际搭建时,就是这个四层骨架。每一层都有明确的取舍,后面会逐个展开。
1.3 组件清单和选型理由
“cloudflare-os”这套方案里,我选择的服务如下:
- Cloudflare DNS:把域名的解析权全部交给 Cloudflare,利用它的 Anycast 网络保证解析速度,同时能直接使用代理模式。
- Cloudflare CDN:静态资源自动缓存,动态请求回源,还能开启 HTTP/3、Brotli 压缩。
- Cloudflare Tunnel:解决内网穿透问题。我有一台旧笔记本放在家里跑服务,它不需要公网 IP,也不需要路由器端口映射,只要运行一个 cloudflared 进程就能把本地服务安全地暴露出去。
- Workers:做 API 网关和请求处理逻辑。它运行在边缘节点,用户访问时不需要先到我的源站,一些转发、鉴权、限流逻辑可以直接在 Worker 里完成。
- R2:存放用户上传的文件、图片和静态数据。R2 的出口流量免费,这个对自托管特别友好。
- KV:存放配置项和需要快速读取的公共数据,比如功能开关、产物版本号。
- Zero Trust Access:给私有服务统一加一道身份验证入口,没有授权的人连页面都看不到。
选这些组件的核心逻辑是:能免费起步、按量付费价格可控、官方文档成熟、社区案例多。比如我曾经想用纯 VPS 自建反向代理来暴露家里的服务,但那样要处理 TLS 证书、DDNS、端口映射、防火墙规则,还没算被扫描和爆破的安全问题。换成 Cloudflare Tunnel 之后,这些麻烦一笔勾销,TLS 证书自动配好,源站 IP 不暴露,身份验证还能统一做,实际用下来省了很多心。
2. 核心环节实现:接入 Cloudflare 网络
2.1 域名与 DNS 设置
先把域名托管进 Cloudflare。这一步是所有服务的基础,因为后面不管是 CDN 加速还是 Tunnel 域名绑定,都要依赖 DNS 记录处于代理状态。
在域名注册商那里,把域名的 NS 记录改成 Cloudflare 分配给你的两个 NS 地址。修改之后需要等几分钟到几小时生效,我一般直接用 dig 命令检查:
dig +short NS yourdomain.com看到输出的 NS 地址已经变成了类似xxx.ns.cloudflare.com,说明托管生效。接着在 Cloudflare Dashboard 的 DNS 页面,把@和www的 A/AAAA 记录先填一遍,指向你源站的真实 IP,并把橙色云朵图标点开,让流量走 Cloudflare 代理。
这里有个小知识点:一旦开启代理模式,访问者的请求先去 Cloudflare 节点,Cloudflare 再去你的源站拉取内容。因此源站 IP 在 DNS 层面不暴露,攻击者无法直接通过 DNS 解析找到你的源站。这个特性对自托管服务来说至关重要,能挡掉大部分直接打源站的扫描流量。
如果你用了 Tunnel,其实不一定需要显式填写 A 记录,Tunnel 会在 Dashboard 上生成 CNAME 记录并指向你的 Tunnel 域名。但保留源站记录也有好处,比如你希望某些路径不走代理、直接回源,或者需要做健康检查时方便管理。
2.2 用 Cloudflare Tunnel 打通内网
Tunnel 是我整套方案里最值得推荐的部分。它做的事情很简单:在源站机器上运行一个轻量客户端,主动向 Cloudflare 边缘建立一条出站连接,然后把指定域名下的流量转发到本地端口。关键在于这条连接是从源站主动发起的,所以不需要公网 IP,不需要改路由器,也不需要担心运营商封端口。
安装和配置的步骤我写过很多次,其中最稳的流程是:
- 安装 cloudflared:
sudo apt update && sudo apt install cloudflared- 用你的 Cloudflare 账号登录:
cloudflared tunnel login这条命令会拉起浏览器,让你选择要授权的域名,之后就生成了一个证书文件,存放在用户目录的.cloudflared目录下。
- 创建隧道:
cloudflared tunnel create my-home-server执行成功后会生成一个随机 ID,后续所有配置都基于这个 ID 来引用。
- 编辑配置文件。我习惯把配置放在
/etc/cloudflared/config.yml,内容大致如下:
tunnel: my-home-server credentials-file: /home/xxx/.cloudflared/<tunnel-id>.json ingress: - hostname: blog.hostname.com service: http://localhost:8080 - hostname: nas.hostname.com service: http://localhost:5000 - hostname: dashboard.hostname.com service: http://localhost:3000 - service: http_status:404注意最后的兜底规则必须存在,否则 cloudflared 会拒绝启动。你可以设置成返回 404,也可以指向一个默认服务。
- 启动隧道并绑定域名:
cloudflared tunnel route dns my-home-server blog.hostname.com这条命令会自动在 DNS 区创建一条 CNAME 记录。然后启动:
cloudflared tunnel run my-home-server如果一切正常,你访问blog.hostname.com,流量就能穿透到本地的 8080 端口。
我实际使用中发现,直接用命令行跑不够稳定,断开会话或者服务器重启后隧道就掉了。所以我加了 systemd 服务来托管它:
sudo cloudflared service install systemctl enable --now cloudflared这样 cloudflared 作为系统服务常驻,开机自启,断线自动重连。让我印象很深的是,有一次运营商晚上断网,家里路由器重启,但第二天早上起来服务还是正常访问,因为 cloudflared 会一直尝试重新建立连接,不需要人工干预。
2.3 边缘网络加速配置
Tunnel 打通之后,访问链路已经是“用户 -> Cloudflare 边缘节点 -> 内网源站”。接下来就要让这个“OS”真正发挥性能优势,也就是把静态内容的缓存配置好。
我在 Cloudflare Dashboard 的 Caching 页面做了几件事:
- 把标准缓存行为设为“标准”,系统会根据源站返回的 Cache-Control 头来判断是否缓存。
- 对图片、CSS、JS 这类静态资源,设置了 Edge Cache TTL,一般设 1 小时到 1 天不等。
- 开启 Brotli 压缩,这样文本类资源体积能再降 20%~30%。
- 开启 HTTP/3,长连接场景下延迟会更低。
你还可以直接用响应头来精细控制缓存行为。比如在 Nginx 配置里给静态目录加:
location /static/ { add_header Cache-Control "public, max-age=86400"; }Cloudflare 边缘节点看到这个响应头,就会把该资源缓存 24 小时。验证缓存是否生效,可以用cf-cache-status响应头,第一次请求是MISS,第二次请求是HIT。
这种方式的好处是,大部分重复请求根本不会到你的源站,等于免费给你的内网服务套了一层全局缓存。我有一个个人相册服务,几百张原图一共将近 3GB,部署在 Cloudflare 后面之后,每天几千次访问里只有不到 5% 的请求会真正回源到家里那台旧主机,绝大部分都被边缘节点直接返回了,源站负载基本可以忽略。
3. 计算与存储层:Workers 与 R2
3.1 用 Workers 做 API 网关
接入网络之后,下一步是让“cloudflare-os”具备动态处理能力。最核心的组件就是 Workers。Worker 本质上是一段运行在边缘节点上的 JavaScript 代码,它不占你的服务器资源,启动速度比传统函数快得多。
我主要拿 Workers 做三件事:请求聚合、边缘鉴权、接口限流。比如原来需要同时请求多个接口的场景,传统做法是在前端串行请求,或者后端写 BFF 层。用 Worker 可以在离用户最近的位置把这些请求合并,减少用户到源站之间的往返。
下面这个 Worker 示例,是我用来做统一入口的:
addEventListener("fetch", (event) => { event.respondWith(handleRequest(event.request)); }); async function handleRequest(request) { const url = new URL(request.url); // 简单的 API Key 校验 if (request.method === "POST" && url.pathname.startsWith("/api/")) { const apiKey = request.headers.get("X-API-Key"); if (apiKey !== "your-secret-key") { return new Response("Unauthorized", { status: 401 }); } } // 合并多个上游请求 if (url.pathname === "/aggregate") { const [userRes, postsRes] = await Promise.all([ fetch("https://your-upstream.com/user"), fetch("https://your-upstream.com/posts"), ]); const user = await userRes.json(); const posts = await postsRes.json(); return new Response(JSON.stringify({ user, posts }), { headers: { "Content-Type": "application/json" }, }); } // 其他请求直接回源 return fetch(request); }部署 Worker 也很简单。你可以直接在 Cloudflare Dashboard 里编辑代码,也可以用 wrangler 命令行。我个人推荐用 wrangler,因为可以本地调试、版本管理,还能和 Git 配合。
npm install -g wrangler wrangler login wrangler deployWorkers 免费版每天有 10 万次请求额度,对大多数个人项目完全够用。而且 Worker 运行在边缘节点,不需要你维护服务器,天然支持全球分布,这是它作为“计算层”的核心价值。
3.2 用 R2 做对象存储
自托管服务总会遇到文件存储问题。以前我自己搭过 MinIO,虽然功能强大,但需要持续维护存储空间、备份策略、访问权限。后来我把新的文件都切到了 Cloudflare R2,因为它的出口流量免费,这一点太关键了。很多对象存储服务最坑的就是流量费用,而 R2 把这块的成本直接抹平,特别适合个人项目。
R2 的用法和 AWS S3 基本一致,支持 S3 兼容 API。我用的 wrangler 可以很方便地配置:
# wrangler.toml name = "cloudflare-os-storage" main = "src/index.js" [[r2_buckets]] binding = "MY_BUCKET" bucket_name = "my-cloudflare-os-bucket"然后在 Worker 里就可以直接操作存储:
export default { async fetch(request, env) { const url = new URL(request.url); const key = url.pathname.slice(1); if (request.method === "PUT") { await env.MY_BUCKET.put(key, request.body); return new Response("Uploaded", { status: 200 }); } const object = await env.MY_BUCKET.get(key); if (!object) return new Response("Not Found", { status: 404 }); return new Response(object.body, { headers: { "Content-Type": object.httpMetadata.contentType }, }); }, };R2 还能绑定自定义域名,配合 Cloudflare CDN 自动缓存,文件访问体验和静态资源一样快。我现在的做法是:用户上传的图片写到 R2,通过 Worker 做图像处理(缩略图、格式转换),输出 URL 带参数即可,省去自己维护图片处理服务的麻烦。
需要注意 R2 的计费单位包括 Class A 操作(写入、读取元数据)和 Class B 操作(读取数据),免费额度内有每月 100 万次读写操作。对个人博客或工具站来说,正常使用几乎不会超。
3.3 KV 与 D1 的取舍
存储层不是只有 R2,还有 KV 和 D1。KV 是全局键值存储,适合放配置、版本号、功能开关这类读多写少的数据。它的读取速度极快,而且数据会在边缘节点做缓存,所以我一般把跟请求处理相关的公共配置放进 KV,让 Worker 在边缘直接读取,不用回源。
举个例子,如果我想给不同用户返回不同主题:
const theme = await env.KV_NAMESPACE.get(`theme:${userId}`, "json");这种低频变更但读得频繁的数据,放在 KV 里再合适不过。
D1 则是基于 SQLite 的关系型数据库,适合有查询需求的结构化数据,比如评论、订单、文章列表。它的特点是简单轻量,不需要后端单独维护数据库服务,一个wrangler d1 create就能建好库,再通过 Worker 查询。
我在一个工具站里同时用到了两类存储:文章的元数据和评论用 D1 管理,图片二进制文件直接放在 R2,站点的功能开关和浏览器指纹黑名单放在 KV。每个数据都有它最合适的容器,这也是整个“cloudflare-os”存储层让人舒服的原因:你不需要像以前一样只抱着一台数据库服务器死磕,按访问模式和数据结构自由切换。
4. 安全接入:Zero Trust 与应用保护
4.1 用 Access 实现身份验证
服务多了之后,最头疼的问题是怎么避免每个服务都单独做一遍登录系统。像 Jellyfin、Grafana、Portainer 这类自托管工具,有些自带用户体系,有些没有,就算有,弱密码被扫出来也是风险。用 Cloudflare Zero Trust 的 Access 功能,可以在所有服务前面统一加一层身份验证。
具体做法是:在 Zero Trust Dashboard 里创建一个 Application,域名选择你要保护的子域,比如grafana.hostname.com,然后配置访问策略。策略可以基于用户邮箱、身份提供商组、甚至端点上是否安装了指定证书。我通常设置成只允许自己的邮箱和备用邮箱登录,登录方式可以是 One-Time PIN 或者 Google/DingTalk 等集成。
这样用户访问任意被保护的服务时,会先跳转到 Cloudflare 的认证页面,通过了才把请求转发到源站。源站本身无需做任何登录改造,Cloudflare 在请求头里注入Cf-Access-Authenticated-User-Email来标识登录用户。应用可以通过这个头做更精细的鉴权,比如管理员页面只有特定邮箱能访问。
这套方式最让我满意的地方是:一个 Access Application 可以覆盖多个域名,并且和现有的用户系统互不冲突。如果你以后换服务,源站 IP 不变,域名不变,安全策略也不变,改动成本几乎为零。
4.2 WAF 与 Bot 防护
有了访问控制,还得对付自动化攻击和扫描机器人。Cloudflare 的 WAF 自带托管规则,可以在安全中心开启。开启之后,常见的 SQL 注入、XSS、路径穿越这些攻击模式会被自动拦截,不需要自己写复杂规则。
但纯靠托管规则不够,因为自托管服务偶尔会碰到误杀。比如我有个服务会在 URL 参数里传 Base64 编码的文本,中间包含了一些看起来像攻击 payload 的内容,被托管规则拦了几次。后来我在 WAF 规则里加了例外:只针对特定路径启用严格的托管规则,其他路径保持宽松。
除了 WAF,我强烈建议开启 Bot Fight Mode 或配置一条简单的速率限制规则。对于没有用户登录态的接口,速率限制尤其有效,能防止脚本批量刷接口。
# 示例:同一 IP 每 10 秒最多访问 20 次Cloudflare Dashboard 的 Rate Limiting 页面里直接配置即可。需要留意的是,也可以结合 Worker 做更细粒度的限制,比如同一用户的请求频率。我通常的做法是双重保护:WAF 挡恶意流量,Worker 里的逻辑做业务级限流。
4.3 安全最佳实践
把整套“cloudflare-os”跑起来之后,我还形成了一套自己的安全清单,分享几个关键点:
- 源站机器的防火墙只允许出站连接,不允许任何入站连接,除了 SSH 管理端口可以限制特定 IP 访问。
- 开启 Cloudflare 的 TLS 加密模式为“完全(严格)”,确保边缘到源站的链路也走 HTTPS。
- Tunnel 连接不进源站的 Docker 网络,而是单独用 systemd 管理,避免云端面板直接影响容器编排。
- 定期检查 Access 的审计日志,确认没有陌生邮箱登录过。
我踩过最大的坑是:有一台旧服务器没做任何保护,源站 IP 直接暴露在公网,结果被扫描器发现了 SSH 弱密码,服务直接被入侵了。后来我强制所有公网服务都走 Cloudflare,源站只保留管理用的白名单 SSH 端口,这类问题基本绝迹。
5. 常见问题与避坑记录
5.1 Tunnel 频繁掉线问题
Tunnel 本身非常稳,但如果你不把它注册成系统服务,长时间挂机很容易在会话中断后无法自动重连。我的建议是使用 systemd 托管,并设置Restart=always。有时候 Windows 版本的 cloudflared 性能不如 Linux 版稳定,所以条件允许的话,尽量用 Linux 小主机跑这些服务。
我还遇到过一种情况:服务器上多个 cloudflared 实例抢同一份凭据文件,日志里反复出现认证错误。排查下来,是因为我之前用tunnel login创建的凭据和后来手动复制配置文件导致冲突。解决方法是统一只保留一个实例,删掉其他进程,再重新启动。
5.2 Workers 免费额度不够怎么办
如果你用 Workers 做 API 网关,免费版的 10 万次/天着实有些紧张。我优化了请求路径,让绝大部分静态内容直接走 CDN,只有真正的动态 API 请求才会命中 Worker。另外,不需要 Worker 处理的路径,在路由表里配置成直接回源,避免无谓的 Worker 调用。
如果还想节省办法,可以在 Worker 里启用 Cache API,对同一个 GET 请求的结果缓存到边缘,这样即使调用很多,实际由 Worker 执行业务逻辑的次数也有限。你可以在 Worker 里读取请求头中的cf-cache-status来判断是否是命中缓存。
5.3 R2 费用增长误解
R2 虽然出口流量免费,但读写操作是计费的。比如代码里循环调用put或者get,每个操作都是 Class A/B。我在第一版代码里把图片的缩略图处理放在了请求链路中,导致每次访问都会对 R2 做一次读操作,虽然免费额度内没产生费用,但后期如果量上去了,会被白白扣掉。
建议的做法是在上传时生成好多个尺寸的缩略图,一次性写入 R2,访问时只需要直接读取对应 key,不要重复计算。
5.4 DNS 代理模式导致本地无法访问
很多自托管玩家都会遇到:开启 Cloudflare 代理之后,在服务器本地通过域名访问自己,访问不通,但在外部却一切正常。这是因为代理模式下,内部请求回到 Cloudflare 边缘节点,再从边缘回源时可能会形成 NAT 反弹环路,或者 DNS 解析指向公网 IP,路由到自己的路由器被挡掉了。
解决办法有三种:一是在本地 hosts 文件把域名直接解析到内网 IP;二是用 Cloudflare 的 Local DNS 或 Split Tunnel 功能,让内部流量走直连;三是如果服务本身只在内网用,就单独配置一个本地域名,不走 Cloudflare。
我个人最常用的是 hosts 文件方案,简单直接。需要注意的是,一旦域名启用了代理模式,源站看到的客户端 IP 是 Cloudflare 边缘节点的 IP,而不是真实用户 IP。如果你需要记录真实 IP,可以在 Nginx 层解析CF-Connecting-IP请求头。
最后再分享一个我在实际使用中的体会:这套“cloudflare-os”方案最值得投入的部分不是某个工具的参数配置,而是你愿意花多少时间把整个基础设施的访问链路真正理解透。刚开始我只是为了把家的的博客服务挂出去,后来慢慢把数据库备份、文件存储、身份验证都挪进同一套体系,才体会到边缘基础设施组合起来的力量。运维上踩过几次坑之后,我现在遇到任何自托管需求,第一反应都是先想想能不能塞进这套“cloudflare-os”架构里,而不是急着去开一台服务器裸奔。你如果也想搭一套属于自己的云操作系统,建议先从 Tunnel 和 Worker 这两个点入手,跑通了再一步步加存储、加安全策略,整个过程会非常有成就感。