Cloudflare Tunnel 路径转发踩坑与三种解决方案(免费vs收费)

先说一下我踩坑的问题

CF Tunnel 的 ingress 规则里,path 匹配会把完整路径透传给后端。

配置长这样:

yml 复制代码
ingress:
  - hostname: mydomain.com
    path: /appPath
    service: http://localhost:8080

访问 https://mydomain.com/appPath 时,后端 Flask 收到的路径是 /appPath,不是 /。但 Flask 路由只写了 @app.route('/'),直接 404。

这个问题不是个例。我的每个项目路径不一样,不可能为了适配 CF 去改代码。

这就很头铁,我就必须找一个一劳永逸的方法(然鹅好像没有


方案1:子域名代替路径(免费)

核心思路:不同服务分配不同子域名,不共用主域名+路径。

改造前

访问地址 目标 端口
https://mydomain.com/myPath 应用 8080
https://mydomain.com/ 主站 5080

改造后

访问地址 目标 端口
https://myPath.mydomain.com/ 应用 8080
https://mydomain.com/ 主站 5080

后端收到的路径就是 /,不用改任何代码。

要注意,这不是myPath前后换了换位置,而是数据流传递路由的问题。

操作步骤

1. Cloudflare DNS 加子域名记录

Dashboard → 域名 → DNS → Records → Add Record:

字段
Type CNAME
Name myPath
Target mydomain.com
Proxy 开启
TTL Auto

2. 改 cloudflared 配置

yml 复制代码
ingress:
  - hostname: myPath.mydomain.com
    service: http://localhost:8080
  - hostname: mydomain.com
    service: http://localhost:5080
  - service: http_status:404

3. 重启 cloudflared

powershell 复制代码
# 先噶掉已有的服务,关闭隧道
taskkill /F /IM cloudflared.exe

# 重新启动服务或命令行运行,打开隧道
cloudflared tunnel --config config.yml run my-domain-bot

4. 验证

  • http://localhost:8080 → 正常
  • https://myPath.mydomain.com/ → 正常
  • https://mydomain.com/ → 正常

为什么有效

  • DNS 层:子域名 CNAME 指向主域名,CF 自动处理 SSL
  • Tunnel 层:hostname 匹配子域名,请求完整转发
  • 后端层:路径就是 /,Flask 路由直接命中

对比其他方式

方案 问题
路径匹配 path: /myPath 路径不剥离,后端收到 /myPath
Origin Rules 改写 Free 计划不支持
子域名 ✅ 免费,不改代码,配置一次管用

注意

  • DNS 记录 1-2 分钟生效
  • 旧链接 mydomain.com/myPath 需要更新为 myPath.mydomain.com
  • 子域名数量没有限制,后续加 api.xxxadmin.xxx 继续加 CNAME 就行

方案2:本地 Nginx 反向代理(免费,需维护)

思路:在本地起一个 Nginx,让 cloudflared 只指向 Nginx,由 Nginx 负责路径剥离和转发。

适合已经在用 Nginx 的场景,多一层代理但逻辑清晰。

Nginx 配置

nginx 复制代码
server {
    listen 8080;

    # 应用服务:剥离 /myPath 前缀
    location /myPath/ {
        proxy_pass http://localhost:8080/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

    # 主站:直接透传
    location / {
        proxy_pass http://localhost:5080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

cloudflared 配置

不再用 path 匹配,全部指向 Nginx:

yml 复制代码
ingress:
  - hostname: mydomain.com
    service: http://localhost:8080
  - service: http_status:404

请求流向

复制代码
用户 → https://mydomain.com/myPath/foo
       ↓
cloudflared → http://localhost:8080/myPath/foo
       ↓
Nginx 剥离 /myPath → http://localhost:8080/foo
       ↓
Flask 收到 /foo → 正常匹配

优缺点

优点 缺点
完全免费 需要维护 Nginx
路径规则灵活,想怎么改怎么改 本地多一个服务要保活
不依赖 CF 付费功能 Nginx 挂了全部不通

方案3:cloudflared 自定义转发规则(免费,支持正则)

这个容易和 Origin Rules 搞混,分开说。

3a. Origin Rules(面板功能,付费)

Cloudflare 面板里的 Origin Rules 可以重写路径,但 仅 Pro 及以上计划可用,Free 用户用不了。

操作:Dashboard → Rules → Origin Rules → Create rule

字段
When Hostname equals mydomain.com AND URI Path starts with /myPath
Then Rewrite URI → Path → Static → /

缺点:只能静态替换,/myPath/foo 会变成 /foo,但如果路径里有动态部分,处理不了。


3b. cloudflared 自定义转发规则(免费,推荐)

cloudflared 本身支持 Custom Forwarding Rules,可以用正则表达式动态剥离路径前缀。Free 计划可用,不需要付费。

config.yml 里加:

yml 复制代码
custom_forwarding_rules:
  - hostname: mydomain.com
    forward:
      - path: ^/myPath(/.*)?$
        destination: http://localhost:8080
        rewrite: /$1

或者更简洁的写法,直接在 ingress 里用 path 配合 rewrite(cloudflared 2024+ 版本支持):

yml 复制代码
ingress:
  - hostname: mydomain.com
    path: /myPath
    service: http://localhost:8080
    originRequest:
      transform:
        - method: regex_replace
          pattern: ^/myPath(/?.*)$
          replacement: $1

请求流向

复制代码
用户 → https://mydomain.com/myPath/foo
       ↓
cloudflared 正则匹配,剥离 /myPath
       ↓
后端收到 /foo → Flask 正常匹配

优缺点

优点 缺点
免费,不需要付费 Plan 需要编辑配置文件,正则写错会全挂
正则替换,动态路径也能处理 cloudflared 版本要够新(2024+)
不用改后端代码 调试不如面板直观

我选的方案1

子域名方案最省事:免费、不改代码、不用维护额外服务、不依赖付费功能。唯一的代价是要改一下访问链接,但长远来看最干净。