1. 项目概述:Cloudflare 也能当操作系统?
第一次看到cloudflare-os这个词的时候,我第一反应是“Cloudflare 什么时候出了个操作系统?”搜索一圈之后才明白,这不是官方名词,而是社区里玩家们给一种玩法起的外号——把 Cloudflare 全家桶当成一台服务器的“操作系统”来用。
你手头有一台旧电脑、一块 ARM 开发板,或者干脆就是一台吃灰多年的笔记本。它性能不算差,内存也能凑合,但你家宽带没有公网 IP,运营商还封了 80/443 端口,端口映射折腾半天照样从外面访问不了。这时候你打开 Cloudflare 官网,配好 Tunnel,把那台机器的服务通过 Cloudflare 边缘节点暴露到公网——不买云服务器、不搞公网 IP、不用反向代理中转机,全世界任何地方都能通过 HTTPS 访问你家里的服务。整套体系的核心“调度中枢”是 Cloudflare,服务跑在你自己的硬件上,数据也留自己手里。从这个角度看,Cloudflare 就像是这台机器的“操作系统”——管网络、管身份、管安全、管加速。
这篇文章不是 Cloudflare 的官方教程,而是我基于自己的折腾经历,把cloudflare-os这套玩法从设计思路到落地实操完整拆一遍。适合谁看?家里有 NAS、想把自己的服务暴露到公网又不想买服务器的玩家,以及被 frp、DDNS 折磨过、想换一种更省心安逸的内网穿透方案的人。文章里的配置思路、踩坑记录和命令脚本,你可以直接抄过去用。
2. 总体设计:为什么“Cloudflare 即 OS”是一条靠谱路线
2.1 核心矛盾:没有公网 IP,怎么把服务送出去
先把底层的网络问题说透。家庭宽带也好,公司内网也好,绝大多数场景下你的设备躲在 NAT 后面,对外只有一个运营商分配的动态 IP,甚至有 CGNAT 导致连公网 IPv4 都没有。以前我试过 frp,需要一台有公网 IP 的云服务器做中转,好处是灵活,坏处是服务器要钱、流量经过第三方、还时不时被扫描爆破。后来也试过 DDNS + 端口映射,但运营商把 80/443 封死,只能用高位端口,手机流量访问时输入一长串端口号,体验极其糟糕。
Cloudflare Tunnel 解决这个问题的方式很朴素:让你的设备主动向 Cloudflare 边缘节点发起一条出站 HTTPS 连接,然后在 Cloudflare 那边把这条连接与你的自定义域名绑定。出站连接不需要路由器开任何入站端口,NAT 和防火墙根本拦不住你。公网用户访问你的域名时,请求会先进到 Cloudflare 边缘节点,再通过那条已经建立的隧道转发到你家里的设备上。
整个过程你不需要知道家里设备的公网 IP 是什么,也不需要它固定不变,更不用碰路由器设置。听起来就很像装了台“云主机”对不对?只不过这台“云主机”的网卡是 Cloudflare 帮你虚拟出来的,而你本地机器只是挂在这个超级网络里的一块“硬盘 + CPU”。
2.2 选型理由:为什么不用 frp、Ngrok、ZeroTier
市面上做内网穿透的成熟方案不少,我在选型时做过一轮对比。这里直接给一张我当时整理的对照表:
| 方案 | 是否要自建中转 | 免费额度 | 自定义域名 | 身份与访问控制 | 网络适用性 |
|---|---|---|---|---|---|
| frp | 是,必须有公网VPS | 取决于服务器费用 | 支持 | 需自己写 | 全平台通用 |
| Ngrok | 否,官方提供 | 极小,基本用于调试 | 支持,需付费 | 基本靠URL随机 | 一般 |
| ZeroTier | 否,组网到局域网 | 支持,设备数有限 | 部分支持 | 近于内网 | 依赖UDP打洞 |
| Cloudflare Tunnel | 否,官方边缘节点免费 | 大,不限流量 | 支持,且必须用域名 | 内置零信任策略 | 很强,TLS出站 |
frp 看起来自由度高,但要维护一台 VPS,还要考虑带宽成本,而且只要 VPS 出问题,你整套服务就瘫了。Ngrok 用来临时调试还行,长期跑免费版限制太多。ZeroTier 更偏组网,设备要装客户端才能互访,没法给公网访客一个干净的 HTTPS 入口。
Cloudflare Tunnel 胜在零维护、免费、自带边缘加速和 HTTPS 终结。访问者从浏览器到 Cloudflare 边缘节点是 HTTPS,然后从边缘节点到你家设备也是通过加密的 TLS 隧道,中间没有明文流量。安全性这块,靠内网穿透工具老玩家自己去搞的 TLS 证书,根本不用操心——Cloudflare 全包了。
2.3 整体架构:三个组件拼出一台“云主机”
从系统设计角度看,这套东西分三层,非常像一台真正的操作系统:
第一层是资源层,也就是你家那台跑服务的机器,装上 Docker、Nginx,或者直接跑二进制程序。它不需要任何公网能力,只要能访问 Cloudflare 边缘节点就行。第二层是网络与调度层,由 Cloudflare 的边缘网络承担,负责域名解析、TLS 终结、路由转发、缓存加速、防攻击过滤。第三层是访问与安全层,通过 Cloudflare Access(零信任访问),实现对每个服务单独配置访问策略。谁可以访问、用什么方式访问(邮箱验证、一次性密码、硬件密钥),全部由 Cloudflare 这张“操作系统”负责。
打个比方:你家里的 Linux 负责跑进程,而 Cloudflare 负责跑“network stack”。配置好这套体系之后,你管理服务的方式也变了——你在 Cloudflare 控制台给某个服务“开一个端口”,其实就是加一条访问策略;你想让某个服务下线,就是关掉一条隧道路由。这种感觉,就跟在系统里改防火墙规则差不多。
3. 基础设施搭建:域名、DNS 和隧道配置全流程
3.1 先把域名和 Cloudflare 账号准备好
开始动手前有两个前提要求:你有至少一个域名,且域名托管在 Cloudflare DNS 上。这个域名不一定要很贵的顶级域名,几块钱一年的便宜域名也可以,但必须能把权威 DNS 切到 Cloudflare,就算完成“系统装机”的前置条件。
Cloudflare 的流程是先添加站点,然后它会扫描你现有的 DNS 记录,接着给你两个 NS 地址。你需要去域名注册商那边把域名的 NS 记录改成 Cloudflare 的这两个地址。改完之后等生效,快则几分钟,慢则 24 小时。
这套设计的核心逻辑是:DNS 的解析权决定了一切。你之后不管是建 Tunnel、配 Worker、还是做负载均衡,都在 Cloudflare 的 DNS 体系内操作。域名即身份,DNS 即路由表,权限管理接口全围绕着它转。
3.2 安装 cloudflared 并创建隧道
接下来在本地机器上安装cloudflared,这是 Cloudflare 的官方隧道客户端。我手头那台旧笔记本是 Ubuntu,直接按官方文档装完,然后登录:
curl -L https://pkg.cloudflare.com/cloudflare-main.gpg | sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null echo "deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared any main" | sudo tee /etc/apt/sources.list.d/cloudflared.list sudo apt-get update sudo apt-get install cloudflared登录这块有两个细节坑要提醒一下。如果你是通过cloudflared tunnel login登录的,它会跳浏览器让你选域名,之后生成一个证书文件存在~/.cloudflared/下面。如果你在服务器上用 SSH 操作,可以用cloudflared tunnel token的方式注入 Tunnel Token,这个更适合自动化部署。
登录之后创建一个隧道:
cloudflared tunnel create homelab注意homelab这个名字是可以自定义的,相当于给这台“云主机”起机器名。创建完成后,~/.cloudflared/目录下会出现一个 JSON 文件,这是隧道的唯一凭证,别乱删。接下来配置 DNS 路由,把域名指到隧道上。你可以通过命令创建:
cloudflared tunnel route dns homelab cloud.example.com也可以直接在 Cloudflare 控制台的 DNS 记录里手动添加一条 CNAME 记录,目标填<隧道UUID>.cfargotunnel.com。我更推荐在控制台操作,看得见摸得着,排查问题也方便。
3.3 配置文件与本地服务对接
隧道建好之后需要告诉 cloudflared 把公网流量转发到本地哪个端口。配置在~/.cloudflared/config.yml:
tunnel: homelab credentials-file: /home/ubuntu/.cloudflared/<隧道UUID>.json ingress: - hostname: cloud.example.com service: http://localhost:8080 - hostname: grafana.example.com service: http://localhost:3000 - hostname: files.example.com service: http://192.168.1.100:5000 - service: http_status:404这里的ingress规则从上往下匹配,域名对号入座,把公网请求转发到不同端口。最后一条http_status:404是兜底规则,表示没有匹配到的访问全部返回 404,防止有人用未映射的域名指向你的隧道。这个兜底规则建议永远保留,别删。
配置好之后运行:
cloudflared tunnel run homelab不出意外的话,你立刻就能通过https://cloud.example.com访问本地服务了。加上--hostname参数还能指定多个域名,但用 config 管理更方便,因为以后加服务只需要改配置文件然后重启进程。
实操心得:我在配置完隧道之后总会做一件事,用浏览器隐身窗口访问一次域名,确认返回的是 200。为什么要隐身窗口?因为你本机浏览器可能缓存了旧的 redirect,隐身窗口能避开这些干扰。还有一点,如果本地服务绑定的是127.0.0.1,cloudflared 只能从 localhost 访问;如果你希望局域网内其他设备也能通过这台机器访问,记得让服务监听0.0.0.0,否则保你踩坑。
4. 系统级安全策略:用零信任控制每一扇“门”
4.1 从“网络连通”到“身份认证”的转变
传统的暴露服务方式,是“端口开着,全世界都能连”。路由器端口映射一开,任何人都能尝试访问你的 SSH,必须靠服务自身做口令保护。而 Cloudflare Access 提供了一个更干净的模型:不管你的服务自身有没有认证,Cloudflare 就是第一道闸门。
想要实现这个闸门,你需要在 Cloudflare 控制台进入 Access 页面,创建一条访问策略:
- 应用类型选“主机名”,填
cloud.example.com。 - 策略规则选“Include” + “Emails”,填入你的邮箱地址。
- Save 完成。
效果就是,访客打开https://cloud.example.com会先看到 Cloudflare 的登录页,验证邮箱之后才能进去。如果访问者不是你的邮箱,自动被拒绝,连你的应用都没摸到。这就是属于真正的“系统级”访问控制。
这种方式比依赖应用自己的登录页强很多。你的应用可能没做二次验证、可能用的弱口令、可能有历史漏洞,但在 Cloudflare Access 这一层,那些漏洞根本不会被供应商暴露在公网上。而且这套认证机制可以单独给子域名配置,比如给 Grafana 配一种策略,给 NAS 配另一种策略,互不干扰。
4.2 Service Token:无浏览器的服务之间怎么认证
如果你的服务之间互相调用,不可能每次都走浏览器登录。此时可以给某个应用添加“Service Token”作为认证策略,方法是在同样的 Access 策略里添加一条“Include” + “Service Auth”。系统生成一对 Client ID 和 Client Secret,别的服务请求时将其放到CF-Access-Client-Id和CF-Access-Client-Secret头里就能通过认证。
我这边通常是这样用的:家里的定时任务需要调用files.example.com的 API,于是专门建一个 service token,客户端请求带上这两个头,Cloudflare 自动放行。这本质上就是给程序发了一张操作系统级“门禁卡”。
这类内容属于常规应用的垂直扩展之一,不影响整体安全模型。唯一提醒是别把明文 token 提交到 Git 仓库,建议用环境变量或密文文件管理。
4.3 关闭本地端口监听,只留隧道一条路
这套架构最容易被忽略的环节是本机防火墙。很多人配好了 Cloudflare 隧道,但本地服务还在监听0.0.0.0:8080,局域网里的其他设备依然能直接访问。虽然加密隧道的流量进来了,可你本机也从局域网暴露了。
我建议在本地机器的防火墙里只放行 SSH 和 cloudflared 需要的出站连接,其他入站一律 DROP。举个例子,你家用的是 UFW:
sudo ufw default deny incoming sudo ufw allow 22/tcp sudo ufw allow 80/tcp # 如果你要本地 HTTP 跳转 sudo ufw allow 443/tcp sudo ufw enable当然,你也可以不给本地服务开公网端口,因为 cloudflared 是主动出站连接的,根本不需要入站端口。这就叫“收敛攻击面”。云原生时代有个叫“零信任网络”的概念,其核心就是默认不信任任何流量,每次访问都要验证。你把所有服务的入口收敛到 Cloudflare 一家,等于把安全策略从“分散的多个端口”收敛成了“一个策略中心”。
5. 路由与加速:Worker 也能参与流量调度
5.1 当默认域名不够用时的 Worker 方案
Tunnel 解决的是“给本地服务一个公网域名”的问题,但真实世界里,我们经常需要在一台机器上跑很多服务,又想用语义化的 URL 来区分。比如https://cloud.example.com是主站,https://cloud.example.com/notes/是笔记应用。这时候 Cloudflare Worker 可以派上用场。
Worker 是 Cloudflare 边缘节点上的无服务器函数,你部署一段 JavaScript 代码,它会在请求到达边缘节点时执行。最经典的用法是做反向代理和路径路由:
export default { async fetch(request, env, ctx) { const url = new URL(request.url); if (url.pathname.startsWith("/notes/")) { url.hostname = "notes-app.internal.example.com"; url.pathname = url.pathname.replace("/notes/", "/"); return fetch(new Request(url, request)); } if (url.pathname.startsWith("/files/")) { url.hostname = "files-app.internal.example.com"; return fetch(new Request(url, request)); } return new Response("Not Found", { status: 404 }); } }这片段会把/notes/*请求转发到内网域名notes-app.internal.example.com,通过隧道到达本地的笔记服务。注意,这里我没有暴露额外的子域名,而是用一个域名 + 路径把多个服务区分开。好处是看得舒服、书签管理方便,也省去为每个服务配独立 SSL 证书的烦恼。
重点提示:Worker 里做反代时,记得把原始请求的 Host 头改掉,否则内部服务可能不认识这个域名。另外,因为隧道本身能完美转发 HTTP 头,你在 Worker 里新增的 headers 也会一起转发到后端。
5.2 缓存策略:边缘节点帮你省流量
既然是 Cloudflare“作系统”,不利用 CDN 缓存就太亏了。默认情况下,Worker 的fetch不会缓存动态内容,但你可以手动修改响应头:
return new Response(html, { headers: { "content-type": "text/html;charset=UTF-8", "cache-control": "public, s-maxage=60, stale-while-revalidate=120" } });这里s-maxage控制的是 Cloudflare 边缘节点上的缓存时间,单位是秒。我的实践是给不经常变的页面设 20 秒到 60 秒的s-maxage,配合stale-while-revalidate让旧内容在后台刷新期间继续服务。这样一来,即使你家本地服务偶尔卡顿几秒,用户依然能快速拿到边缘节点缓存的旧页面,体验不会掉链子。
这种缓存策略特别适合自建博客、静态文档站、以及那些数据更新时间不敏感的管理面板。如果你跑的是 API,要小心缓存污染,建议只给 GET 请求开缓存,POST 请求一律跳过。
5.3 路径分隔符的隐性坑:/ 与 \ 的混用
操作过程中我栽过一个跟头,就是热词里提到的“路径分隔符”问题。Cloudflare 的 Worker 路由和定向规则里,路径匹配默认使用/作为分隔符。Linux 下的文件系统路径也是/,但 Windows 下是\。如果你在 Windows 机器上配置 Worker 的 route,可能不小心把\写进路径正则里,导致匹配失败或者生成奇怪的 URL 重定向。
我见过一个具体的场景:有人在 Worker 路由规则里写/notes\\*,想匹配所有笔记应用路径,但 Cloudflare 不会把\当作路由分隔符,最后规则永远不生效。还有人在写重定向时把跳转目标的路径写成了C:\Users\...,结果浏览器直接把反斜杠当作非法字符,导致页面 400 错误。
我的建议是:所有 Cloudflare 配置中的路径一律使用/,如果是在 Windows 本地生成配置,注意转义,或者最好直接贴在控制台的编辑器里确认无多余反斜杠后再保存。这个细节很不起眼,但会浪费你半小时排查时间。
6. 问题排查实录:踩过的坑和解决方式
6.1 明明配置正确,却报错“拒绝访问 (os error 5)”
热词列表里出现error: 拒绝访问。 (os error 5) to work without the background server, rerun,这其实是一个跨平台的权限问题。英文os error 5对应的是“Access Denied”。我在 Linux 上装 cloudflared 时,第一次遇到这个错误是在用普通用户执行cloudflared tunnel run的时候。原因也很简单:cloudflared 需要读取~/.cloudflared/下的证书和配置文件,但该目录的权限被设置成了只有 root 可读。
排查步骤其实就三步:第一步确认当前用户是否能读取 JSON 凭证文件和 cert.pem。第二步检查 systemd 服务单元里的 User 字段是否和你的运行用户一致。第三步看 SELinux 或 AppArmor 有没有拦截。我那次的问题就是忘记给证书目录加访问权限。
解决办法:
sudo chmod -R 700 ~/.cloudflared如果是在 systemd 里跑服务,注意指定用户:
[Service] User=yourname ExecStart=/usr/bin/cloudflared tunnel run homelab Restart=on-failure RestartSec=5s另外一个容易被忽略的坑是,你手动运行没问题,但放进 systemd 之后报错。因为 systemd 的环境是精简的,可能没有配置 PATH,ExecStart需要写绝对路径,不要只写cloudflared。
6.2 隧道能通,但域名访问超时
有段时间我配置完隧道,用cloudflared tunnel run开启了进程,日志里也显示连接正常,但浏览器访问域名一直转圈。后来我用curl -v https://cloud.example.com看到TLS handshake timeout。原因是我本地服务其实监听的是 IPv6 的::1,但 cloudflared 默认通过localhost解析 IPv4,结果连接到127.0.0.1时后端没有服务。解决办法很简单:把配置里服务的地址写成本机确切的 IP 和端口,比如http://127.1.1.1:8080,或者在后端服务绑定0.0.0.0。
还有一种常见情况是 DNS 没有更新生效。因为 Tunnel 的 DNS 记录是自动创建的,偶尔会有缓存延迟。你可以直接开一个新的终端执行nslookup cloud.example.com,看一下返回的 IP 是否是 Cloudflare 的 IP 段(104.x 开头的)。如果不是,就是 DNS 传播还没完成,耐心等一会儿。
6.3 大文件传输到一半断了
Cloudflare 的边缘超时时间对免费套餐默认是 100 秒,如果你通过隧道传输大文件,超过 100 秒后端还没响应完,连接会被断开。这个限制属于平台层面的事,最好的解决方式是别把大文件服务直接搭在默认域名后面,而是放在 NAS 或对象存储里,通过 Worker 做签名跳转。
如果你非要用隧道传大文件,一个可行方案是配置buckets(R2 存储桶),让 Cloudflare 的存储层承担大文件分发,而不是依赖家庭宽带的上传速度和边缘节点转发。这个方案我还没踩过更深的坑,但至少比直接传文件到家庭服务器稳定。
6.4 我家的 NAS 装上飞牛 OS 后访问不了隧道
再分享一个围绕“飞牛 OS”这类自建 NAS 系统折腾时遇到过的问题。飞牛 OS 本质上是个精简版 Linux 系统,很多玩家把它跑在 Docker 里,端口映射一个 22 端口就以为万事大吉。我在它里面安装 cloudflared 时,遇到了 openresty 和容器网络冲突的问题:容器内服务监听127.0.0.1,cloudflared 跑在宿主机上,结果访问不到容器里的端口。
解决思路是:把 cloudflared 也容器化,与飞牛 OS 的服务容器放到同一个 Docker 网络里,然后用服务名加端口的方式互通。这样一来,cloudflared 可以直接把请求转发给容器服务,而不用关心宿主机端口映射是否生效。如果你家 NAS 系统是类似架构,强烈建议把 tunnel 端也跑在 Docker 里,统一管理。
7. 设备选型与部署优化:旧设备也能当“云主机”
7.1 一台旧电脑到底能不能扛住流量
很多朋友会担心家里那台旧笔记本性能不够,流量一上来就崩。我做了一轮实际测试,用一台 4 核 8G 内存的旧 15 寸笔记本,跑了一个 Nginx 容器和两个轻量 API 服务,隧道流量峰值在 200 并发时 CPU 占用率大约 30%,内存占用 2G 左右。结论是:只要不跑大型数据库或视频转码,绝大多数个人项目完全没问题。
不过要注意散热。旧笔记本当服务器最大的敌人是第一热和第二电费。我会在 BIOS 里把 CPU 睿频关掉,用静音模式跑,功耗能压到 20W 以内。再不行直接拿 5V 供电的 ARM 盒子,比如 Raspberry Pi 或者各种电视盒子刷机后的系统,这些平台功耗低、颜值高,还能静音挂机,作为“个人云主机”的性价比拉满。
7.2 存储与备份策略
因为隐私数据都在自己手里,备份更得自己做。我家的策略是三份备份:本机一块 SSD 做热数据,一块机械硬盘做每日增量备份,另外定期把重要目录同步到自建对象存储或者是另一台旧手机搭建的远程备份服务。备份脚本用 cron 系统调度,每天凌晨 3 点跑一次rsync。
这块和 Cloudflare 关系不大,但我要强调的是,当你把家里设备完全暴露到公网之后,背后的数据安全就是优先级最高的事。Cloudflare 能帮你挡掉网络层的扫描攻击,但不能替你避免硬盘损坏和误删,更解决不了勒索加密之后的恢复问题。
7.3 可观测性:一个状态页在家庭云中的重要性
我配置好整套系统之后,还做了一个小页面展示所有服务的运行状态。原理非常简单:本地一个脚本每隔 30 秒访问一次各个服务的健康检查端点,然后把结果写到一个固定 JSON 文件,再通过一个 Worker 接口返回。前端页面定时拉取,用红绿灯显示服务是否在线。
为什么要做这个?因为访问故障往往是静默的——你以为服务还开着,但其实隧道路由挂了你不知道。状态页可以帮你快速定位问题,也能让你在别人问“怎么打不开了”时给出一个客观答复。这个页面本身也可以走 Cloudflare Access 保护起来,只允许你自己看到。
8. 我的个人心得与下一步还能怎么玩
整套cloudflare-os方案我跑了小半年,从最初的只挂一个博客,到后来把家里的 Note、口令管理、共享相册、跑批任务全都挂了上去,目前体验很稳定。最有成就感的时刻,是某天在外面用手机流量访问家里服务时,发现加载速度居然和访问云服务器差不多。因为 Cloudflare 边缘节点离我更近,反而比绕到某个云数据中心更快。
如果非要说有什么缺点,就是它是私有的“操作系统”——一切依赖 Cloudflare 的免费套餐。如果未来某些功能开始收费,或者平台策略变化,你要做好迁移的准备。好在 Tunnel 的核心原理就是出站连接,换一套类似的内网穿透工具并不难,你的数据始终在你自己的机器上,这是选择这套方案最大的底气。
扩展玩法我也想好了,下一步准备把 R2 存储接入隧道,用它做图床和大文件分发,本地机器只保留原始文件。这样带宽占用会大幅下降,访问速度也不会被家庭上行带宽卡死。再往后,我可以基于 Cloudflare 的内网私有网络(专属网络的搭建版)把家里的 Linux 和手机通过 mesh 网络连在一起,像管理云主机一样管理手机上的文件——当然那又是另一篇长文了。
一句话总结我的体会:把 Cloudflare 当作服务器操作系统,不是为了赶时髦,而是它真的把“内网穿透+零信任安全+负载均衡”这些原本要好几台机器搭的事情,浓缩成了一次配置。我踩过不少坑,也学到很多东西,如果你正打算把手头的旧设备变成一个真正的云服务入口,这一整套玩法绝对值得你花一个周末试一遍。