Cloudflare Tunnel 将本地服务暴露至公网 - MuxiaoWF跳到主要内容

Cloudflare Tunnel 将本地服务暴露至公网

使用 Cloudflared tunnel 将本机任意 HTTP 服务映射到自有域名,配套 VBS 后台静默启停脚本与通用配置文件。

周日 7月 05 2026
2968 字 · 14 分钟

最近突然想在手机上也能看到电脑上的文件,找了半天都说需要一个公网ip,对于鄙人白嫖党肯定是不太接受的。最后也是成功找到了一个赛博活佛Cloudflare提供的工具。

感谢 xiaoyuboi/cloudflare-tunnel-skill 提供方法方向,可以直接使用这个 skill 导入 Claude 帮你完成。下文仅当记录了。

一、整体架构

流量链路

外部用户 → https://你的域名
→ Cloudflare 边缘节点(HTTPS 终止,CDN 缓存)
→ Cloudflared 加密隧道(QUIC / HTTP2)
→ 本机 127.0.0.1:端口(任意本地 HTTP 服务)

核心思路:本地跑一个 HTTP 服务(文件浏览、API、网站都可以),通过 Cloudflared 隧道暴露到公网,Cloudflare 负责 HTTPS、CDN 和 DDoS 防护。

本文示例

为方便演示,使用 Python 写了一个支持目录导航和前后切换的文件管理器。读者可以替换为任何 HTTP 服务(Node.js、Nginx、网盘程序等)。

tips:ai时代不会的可以直接找ai帮你完成或问ai帮你辅助完成。

我这里使用了固定域名的方式(命名隧道 cloudflared tunnel run <名称>),与临时域名(quick tunnel trycloudflare.com)不同:命名隧道需先完成第三节的初始化(tunnel create + route dns),之后每次启动都用同一个域名。如果你希望每次启动自动获取临时域名,可以使用同目录下的 tunnel_helper.py quick 子命令。本文的 VBS 脚本是按固定域名写法设计的。对于有关Cloudflare域名解析等,可以参考查看之前创建博客的文章。关于没有类似vercel给的DNS配置可以直接跳过,没有问题。

二、前置环境

  • Python 3:用于运行本地服务
  • Cloudflared :下载即安装,无界面。可在命令行执行 cloudflared --version 验证
  • 域名 DNS 已托管到 Cloudflare 并生效
  • 推荐:Cloudflare 面板 SSL/TLS → Full,开启「始终使用 HTTPS」

三、隧道初始化(仅首次)

使用 CMD,依次执行:

Terminal window
:: 登录 Cloudflare 授权(会弹出浏览器)
cloudflared tunnel login
:: 创建隧道,名称自定(这里叫 pictures)
cloudflared tunnel create pictures
:: 绑定子域名到隧道
cloudflared tunnel route dns pictures 你的子域名.你的域名

完成后在 %USERPROFILE%\.cloudflared\ 下会生成隧道凭证 JSON,后续启动隧道时会自动使用,无需上传到 Cloudflare 控制台

四、本地服务

本地需要运行一个 HTTP 服务监听 127.0.0.1,可以是任意程序:

Terminal window
:: 最简单的静态文件服务
py -m http.server 3000 --bind 127.0.0.1 --directory F:\share
:: 或者任何本地 HTTP 服务
node server.js
nginx -c ...

实际使用时推荐自己写一个服务程序,好处是可以添加:

  • Range 请求支持(视频拖拽快进)
  • ETag / 缓存头(减少重复传输)
  • 流式传输(大文件不爆内存)

本文附带的 gallery_server.py 实现了上述优化,同时提供了一个支持目录导航的文件管理器界面。

五、配置文件

将可变参数集中到 config.json,修改端口、目录、域名时无需改动脚本代码:

{
"root_dir": "F:\\share",
"port": 3000,
"bind": "127.0.0.1",
"public_url": "https://你的子域名.你的域名"
}

Python 服务端通过 --get <key> 子命令暴露配置值,VBS 脚本通过 WshShell.Exec 动态读取:

Terminal window
py gallery_server.py --get port
:: 输出: 3000
py gallery_server.py --get running
:: 输出: 1(运行中)或 0(未运行)
py gallery_server.py --kill
:: 通过 PID 文件精确终止进程

服务启动后会在 .cloudflare-tunnel\ 下写入 server.pid,用于状态检测和精确终止,避免仅靠端口查杀的不确定性。

六、启动脚本 StartAll.vbs

这是固定域名(命名隧道)的写法。 脚本通过 cloudflared tunnel run pictures 启动预先创建好的命名隧道,使用 --config 指定项目内的 pictures.yml 配置文件,域名固定不变。如果需要每次启动自动获取 trycloudflare.com 临时域名,请使用 tunnel_helper.py quick 子命令。

保存时注意编码选 ANSI(GBK),否则中文弹窗会乱码。

Option Explicit
Dim WshShell, fso
Set WshShell = CreateObject("WScript.Shell")
Set fso = CreateObject("Scripting.FileSystemObject")
Dim BasePath, port, publicUrl, configPath, cloudflaredExe
BasePath = fso.GetParentFolderName(WScript.ScriptFullName)
configPath = BasePath & "\.cloudflare-tunnel\pictures.yml"
' 从配置文件读取参数
Function GetConfig(key)
Dim ex, val
Set ex = WshShell.Exec("py """ & BasePath & "\gallery_server.py"" --get " & key)
val = ex.StdOut.ReadLine()
Set ex = Nothing
GetConfig = val
End Function
port = GetConfig("port")
publicUrl = GetConfig("public_url")
cloudflaredExe = GetConfig("cloudflared_path")
If cloudflaredExe = "" Then cloudflaredExe = "cloudflared"
' Step 1: 查杀残留进程
WshShell.Run "cmd /c for /f ""tokens=5"" %a in ('netstat -ano ^| findstr /R ""TCP.*:" & port & "[ ].*LISTENING""') do taskkill /f /pid %a /t >nul 2>&1", 0, True
WshShell.Run "taskkill /f /im cloudflared.exe /t >nul 2>&1", 0, True
WScript.Sleep 1200
' Step 2: 启动本地服务(后台静默,无窗口)
WshShell.Run "py """ & BasePath & "\gallery_server.py""", 0, False
' Step 3: 等待服务就绪(轮询 PID 文件 + 进程存活检测,最长 15 秒)
Dim maxWait, waited, running
maxWait = 15
waited = 0
Do While waited < maxWait
WScript.Sleep 1000
waited = waited + 1
running = GetConfig("running")
If running = "1" Then Exit Do
Loop
If running <> "1" Then
WshShell.Popup "Gallery server 启动超时(" & maxWait & " 秒)", 10, "启动失败", 16
WScript.Quit 1
End If
' Step 4: 启动命名隧道(固定域名,读取项目内的 pictures.yml 配置)
WshShell.Run """" & cloudflaredExe & """ --config """ & configPath & """ --no-autoupdate --protocol quic tunnel run pictures", 0, False
' Step 5: 弹窗提示
Dim msgText
msgText = "本地服务 & Cloudflare 隧道已后台运行" & vbCrLf & _
"访问地址:" & publicUrl & vbCrLf & _
"关闭请运行 StopAll.vbs"
WshShell.Popup msgText, 8, "启动成功", 64
Set WshShell = Nothing
Set fso = Nothing

启动流程

步骤动作说明
Step 1查杀残留进程精确匹配端口号边界([ ] 防止 :3000 误杀 :30001),同时清理上次的 Cloudflared
Step 2启动 Python 服务后台静默运行,写入 server.pid
Step 3等待服务就绪每秒通过 --get running 检测进程存活,超时则弹窗报错退出
Step 4启动命名隧道使用 --config 指定项目内 pictures.yml--no-autoupdate 禁止自动更新
Step 5弹窗提示显示访问地址,8 秒后自动关闭

关键参数说明

参数含义
TCP.*:PORT[ ]正则精确匹配端口边界,[ ] 确保 :3000 不会匹配到 :30001,兼容 IPv4/IPv6
tokens=5提取 netstat -ano 输出第 5 列(PID)。netstat 输出列序固定,IPv4/IPv6 格式一致
%acmd /c 下 FOR 变量必须单百分号,%%a 仅用于 .bat 文件
^|转义管道符,让外层 cmd 将其传递给内层
0, False隐藏窗口 + 不等待返回(后台运行)
/ttaskkill 连带子进程一起关闭
--config指定隧道配置文件,命名隧道必加,否则 Cloudflared 不会读取项目内的 ingress 规则
--no-autoupdate禁止 Cloudflared 自动更新,避免后台下载占用带宽
--protocol quicCloudflared 使用 QUIC 连接边缘节点,比 HTTP2 延迟更低

七、停止脚本 StopAll.vbs

Option Explicit
Dim WshShell, fso
Set WshShell = CreateObject("WScript.Shell")
Set fso = CreateObject("Scripting.FileSystemObject")
Dim BasePath, port, exec
BasePath = fso.GetParentFolderName(WScript.ScriptFullName)
' 从配置文件读取端口
Set exec = WshShell.Exec("py """ & BasePath & "\gallery_server.py"" --get port")
port = exec.StdOut.ReadLine()
Set exec = Nothing
' Step 1: 通过 PID 文件精确终止 gallery_server(优先,不会误杀)
WshShell.Run "py """ & BasePath & "\gallery_server.py"" --kill", 0, True
' Step 2: 兜底 —— 按端口查杀残留(防 PID 文件被意外删除)
WshShell.Run "cmd /c for /f ""tokens=5"" %a in ('netstat -ano ^| findstr /R ""TCP.*:" & port & "[ ].*LISTENING""') do taskkill /f /pid %a /t >nul 2>&1", 0, True
' Step 3: 关闭所有 Cloudflared 隧道进程
WshShell.Run "taskkill /f /im cloudflared.exe /t >nul 2>&1", 0, True
WScript.Sleep 400
WshShell.Popup "服务已全部关闭", 3, "停止成功", 64
Set WshShell = Nothing
Set fso = Nothing

停止采用三层保障:

优先级方式说明
1--kill(PID 文件)精确终止,不会误杀其他 Python 进程
2端口查杀(netstat)兜底方案,防止 PID 文件被意外删除后无法停止
3进程名查杀(taskkill /im)关闭所有 Cloudflared .exe

理论上运行后的文件夹下文件

理论上运行后的文件夹下文件

八、隧道配置文件 pictures.yml

命名隧道需要配置文件指定 ingress 规则(哪个域名 → 哪个本地地址):

tunnel: pictures
credentials-file: C:\Users\你的用户名\.cloudflared\<tunnel-id>.json
ingress:
- hostname: 你的子域名.你的域名
service: http://127.0.0.1:3000
- service: http_status:404

credentials-file 是第三节 cloudflared tunnel create 生成的凭证文件路径。如果不指定,Cloudflared 会尝试自动查找,但显式指定更可靠。

本文附带了一个 Python 实现的文件管理器,可作为本地服务的起点或直接使用:

功能:

  • 目录导航(面包屑路径 + ”..” 返回上级)
  • 显示所有文件类型(文件夹、图片、视频、文档等)
  • 文件夹排在前面,点击进入子目录
  • 媒体文件(图片/视频)点击进入全屏查看器,支持上/下一张切换,右上角 X 关闭
  • 非媒体文件点击直接下载,Content-Disposition 头确保浏览器以正确文件名和扩展名保存
  • 键盘 切换、Esc 返回列表
  • 移动端滑动切换
  • 相邻文件自动预加载(prefetch),切换几乎无等待

运维特性:

  • PID 文件:启动时写入 .cloudflare-tunnel\server.pid,关闭时自动清理
  • --get running:检测进程是否存活(Windows 上通过 OpenProcess 查询句柄)
  • --kill:通过 PID 文件精确终止进程,StopAll.vbs 优先使用此方式
  • 端口占用保护:bind 失败时打印明确错误信息并提示 --kill 命令

性能优化:

  • HTTP Range 请求(206 Partial Content):视频可拖拽快进
  • ETag + Last-Modified:浏览器缓存校验,未改动文件返回 304
  • 流式传输:64KB 分块读取,不将整个文件加载到内存
  • Cache-Control:媒体文件缓存 1 小时

自定义 favicon:favicon.ico 放到脚本同目录下,自动生效,无需改代码。

通用性: gallery_server.py 只是一个示例本地服务。替换为任何监听 127.0.0.1:端口 的 HTTP 程序,VBS 脚本和隧道配置无需改变。

十、常见问题

1. StopAll.vbs 无法关闭进程

症状: 运行 StopAll.vbs 后,Python 进程和 Cloudflared 仍在运行。

根因: 网上流传的 tokens=-1 是无效的 for /f 语法(CMD 不支持负索引取最后一列),会被静默回退为 tokens=1,提取到的是 TCP 而非 PID。同时 %%a 仅在 .bat 文件中有效,cmd /c 命令行模式必须用 %a

修复: tokens=5 + %a。(上文脚本已使用正确写法)

2. VBS 中文弹窗显示乱码

根因: 文件编码为 UTF-8,但 VBScript 引擎按系统 ANSI 代码页(中文 Windows 为 GBK)读取。

修复: 用记事本另存为 ANSI 编码,或用 Python 转码:

with open('xxx.vbs', 'r', encoding='utf-8') as f:
content = f.read()
with open('xxx.vbs', 'w', encoding='gbk') as f:
f.write(content)

3. Cloudflared 报错 “failed to request quick Tunnel”

通常是网络问题或 Cloudflared 版本过旧,升级到最新版即可。

4. 如何更换本地服务程序

修改 StartAll.vbs 中启动本地服务的那一行,将 py gallery_server.py 替换为你的启动命令即可。端口和域名在 config.json 中统一管理。

5. 如何添加其他端口 / 多服务

config.json 中增加字段,在 StartAll.vbs 中通过 GetConfig() 读取,复制端口查杀和服务启动的两段代码即可。Cloudflared 支持通过配置文件同时暴露多个服务。

十一、安全说明

  • --bind 127.0.0.1 确保本地服务仅本机可访问,局域网其他设备无法直连
  • Python http.server 无上传接口,PUT/DELETE 等方法均不可用,只读安全
  • --directory 锁定根目录,无法通过 ../ 跨目录读取
  • Cloudflare 提供免费 DDoS 防护和 SSL 证书
  • 如需更细粒度控制(IP 白名单、速率限制),在 Cloudflare 面板 WAF 中配置

感谢您的阅读!如果可以,给俺点些关注吧~

Cloudflare Tunnel 将本地服务暴露至公网

周日 7月 05 2026
2968 · 14 分钟
封面
示例歌曲
示例艺术家
封面
示例歌曲
示例艺术家
0:00 / 0:00