OVERSEER.LOG
READING MODE
← 返回日志

// NETWORK / CLOUDFLARE TUNNEL

从零搭建 Cloudflare Tunnel:把家里的小服务器安全地挂上网

没有公网 IP、没有开放入站 443,也可以让一台 2GB 的 Alpine 小机器拥有稳定的 HTTPS 站点。 这条链路的关键不是端口映射,而是一条从内网主动向外建立的隧道。

00 / 为什么不直接端口映射

最直觉的做法是:路由器上把 443 转发到服务器,DNS 指向公网 IP。 但家用宽带通常没有固定 IPv4,部分运营商还会封掉 80/443 入站; 而 IPv6 前缀又会周期性变化。每次地址一变,DNS、证书、防火墙都要跟着修。

Cloudflare Tunnel 把方向反了过来:由服务器主动向 Cloudflare 边缘建立长连接, 外部请求先到 Cloudflare,再顺着这条连接回到源站。 源站不需要监听公网端口,也不需要知道自己的公网地址。

01 / 准备的东西

先确认域名确实由 Cloudflare 解析。如果 dig NS 还返回旧服务商, 后面的授权和 CNAME 都会出问题。

02 / 创建隧道

在服务器上登录 Cloudflare 账号,创建一条命名隧道,并让 Cloudflare 自动写入 DNS:

cloudflared tunnel login
cloudflared tunnel create blog-alpine
cloudflared tunnel route dns blog-alpine blog.example.com

# 记下输出里的 Tunnel ID,稍后写进配置
cloudflared tunnel list

登录会生成 cert.pem,创建隧道会生成同名的凭据 JSON。 两者都放在 ~/.cloudflared/ 下,权限应当只有 root 可读。

03 / 写 ingress 配置

新建 /etc/cloudflared/config.yml。ingress 的顺序很重要: 匹配到的 hostname 走对应服务,最后一条兜底返回 404。

tunnel: <TUNNEL_ID>
credentials-file: /root/.cloudflared/<TUNNEL_ID>.json

ingress:
  - hostname: blog.example.com
    service: https://127.0.0.1:8443
    originRequest:
      noTLSVerify: true
  - service: http_status:404
为什么要 noTLSVerify?
这里回源仍然走 HTTPS,但证书是源站自签或 Let's Encrypt 的域名证书。 cloudflared 到源站是本机回环,开启 noTLSVerify 可以避免证书链校验失败。 如果你回源用 http://127.0.0.1:8080,就不需要这一项。

04 / 让 nginx 接住流量

源站 nginx 只监听回环的 8443,公网 443 依然关闭。 这样即使有人扫到服务器 IP,也进不来。

server {
    listen 127.0.0.1:8443 ssl;
    server_name blog.example.com;

    ssl_certificate     /etc/nginx/ssl/blog.example.com.crt;
    ssl_certificate_key /etc/nginx/ssl/blog.example.com.key;

    root /var/www/blog;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }
}

改完配置先 nginx -t,再 rc-service nginx reload。 不要直接 restart,避免把正在服务的连接全部掐断。

05 / 把它变成服务

OpenRC 下用 supervise-daemon 守护 cloudflared, 进程退出后会自动拉起,服务器重启也能自己回来。

rc-service cloudflared start
rc-update add cloudflared default

# 看状态和日志
rc-service cloudflared status
tail -f /var/log/cloudflared.err

06 / 验证链路

从外部机器依次验证 DNS、边缘和源站,出问题时能快速定位是哪一段断了:

Resolve-DnsName blog.example.com -Server 1.1.1.1
Test-NetConnection blog.example.com -Port 443
curl.exe --noproxy "*" -I https://blog.example.com/

在服务器本地则可以直接绕过 Cloudflare,单独确认 nginx:

curl -k -I https://127.0.0.1:8443/
rc-service cloudflared status
cloudflared tunnel info blog-alpine

07 / 踩过的坑

隧道解决的是「怎么让外部访问到」,不解决「该不该公开」。 每加一条 ingress,都先问一遍:这个页面真的需要公网吗?

到这一步,网站已经可以通过 Cloudflare 的 443 访问,源站入站端口保持关闭。 后面要做的,是把证书续期、日志轮转和监控告警补齐,让它能安静地跑很久。