CodeGym /课程 /ChatGPT Apps /走向成熟的隧道:稳定的 dev‑URL 与 Dev Mode 更新

走向成熟的隧道:稳定的 dev‑URL 与 Dev Mode 更新

ChatGPT Apps
第 7 级 , 课程 2
可用

1. “随机”隧道的问题

当你第一次运行 ngrok http 3000 或者快速创建 Cloudflare Quick Tunnel 时,感觉就像魔法:啪的一下——你的 http://localhost:3000 变成了 https://random-1234.tunnelprovider.com。把这个 URL 复制到 ChatGPT 的 Dev Mode,GPT 就会开心地加载你的 App。

然后你重启了隧道……又得到一个新域名。ChatGPT 中 Dev 应用设置里的旧 URL 突然变成了“坏链路”,GPT 很诚实地写着 “App unavailable”,你又去设置里改 URL,点 Save,等它同步,然后对整套栈默默生无可恋。

一次性的“晚上玩玩”还能忍。但当你:

  • 每天都在迭代 App;
  • 想把中间版本给同事/经理看看;
  • 同时还要开 staging 和 production,

为每个新的随机 URL 去重连 Dev Mode 就变成了纯粹的折磨。

此外,如果你已经在应用里加了依赖域名的东西(比如 OAuth redirect URI 或 webhook),每一个新 URL 也会把它们一起搞坏。于是出现连锁反应:换了隧道——你就得去改 App 配置、OAuth 提供方的 redirect‑URL,以及 webhook 接收端的设置。

由此得出本讲的核心观点:稳定的 dev‑URL 不是奢侈品,而是开发者心理健康的守护工具。

Insight

ChatGPT 对你的应用有非常严格的超时限制,这很容易被低估。MCP 工具调用有时间上限:最多 2 分钟——之后平台就直接视为调用失败,即使你的服务器还在处理。

对于应用注册(Store 或 Dev Mode)则更严格:ChatGPT 在读取清单、资源与工具描述时大约只给 20 秒。如果在这段时间内你的 MCP 服务器还没初始化完成、没返回 tools/resources 等,App 的注册就会超时失败。

建议:所有耗时的初始化都应当发生在你去 Dev Mode 或 Store 之前。例如预热数据库连接、加载大型配置、构建懒加载缓存——这些最好提前完成,比如先通过 MCP Jam 或一段内部脚本“打”一次服务器。从平台视角看,MCP 服务器必须是“热”的,几秒内响应,而不是在注册期间“醒来”。

2. 什么是“成熟级”隧道

先明确,“成熟级”隧道与课程最开始你用的那种有什么不同。

早期模式(模块 2)大致是这样:

# ngrok 示例
ngrok http 3000
# 得到: https://random-abc123.ngrok-free.app

你把这个一次性 URL 填进 Dev Mode。下次启动 ngrok,URL 已经变了,ChatGPT 的配置也就过期了。

在“成熟级”的做法里,你会:

  • 拥有一个静态子域(来自隧道提供方,或使用你自己的域名);
  • 同一个域名总是被转发到你的 localhost:3000
  • 你可以重启隧道、机器、路由器,但 URL 始终不变。

例如,这样的静态子域可用在:

  • ngrok —— 账号层面的免费静态域名;
  • Cloudflare Tunnel —— 通过命名隧道并绑定到你自己的域名。

而 ChatGPT 的 Dev 应用恰好只配置了这一个 URL,从此不再打扰你。

从形式上讲,我们“成熟级”隧道的要求是:

  • 稳定且唯一的公共 HTTPS 域名;
  • 有效的 TLS 证书(由提供方代办);
  • 一个配置,表达“凡是到达 https://dev.yourdomain.com 的请求,都转发到 http://localhost:3000”;
  • 可选——最起码的安全措施(别把 URL 晒到 StackOverflow 就行)。

3. 配置稳定的 dev‑URL:以 Cloudflare Tunnel 为例

本课程推荐把 Cloudflare Tunnel 作为主力工具,因为它在开发与更严肃的场景里都很好用。你在模块 2 已经见过基础配置,现在把它“拧紧”到一个永久的 dev‑URL。

假设我们的示例应用叫 GiftGenius,我们希望有稳定的 URL giftgenius-dev.yourdomain.com

最小化步骤(简化,不依赖 Cloudflare 的 UI):

  1. 把你的域名接入到 Cloudflare 账号(一次性,通过他们的面板)。
  2. 在本地安装 cloudflared 并登录。
brew install cloudflare/cloudflare/cloudflared   # macOS
cloudflared login                                # 会打开浏览器进行授权

3. 创建一个命名隧道:

cloudflared tunnel create giftgenius-dev

4. 在 ~/.cloudflared/config.yml 中配置路由:

tunnel: giftgenius-dev
credentials-file: /Users/you/.cloudflared/giftgenius-dev.json

ingress:
  - hostname: giftgenius-dev.yourdomain.com
    service: http://localhost:3000  # 我们的 Next.js 开发服务器
  - service: http_status:404

5. 启动隧道:

cloudflared tunnel run giftgenius-dev

现在,只要 npm run devcloudflared tunnel run 在运行,你的本地 Next.js 就能通过稳定的 URL https://giftgenius-dev.yourdomain.com 访问。你应当在 ChatGPT 的 Dev Mode 设置中填写的就是这个地址。

这与我们的应用如何契合

如果你在浏览器里打开你在 ChatGPT 里为 Dev 应用填写的 URL:

https://giftgenius-dev.yourdomain.com/mcp

你会看到一个响应(错误)——类似这样:

{"jsonrpc":"2.0","error":{"code":-32000,"message":"Method not allowed."},"id":null}

这完全正常,因为 /mcp 端点并不接受 GET 请求。应用的其他部分——小部件、MCP 端点 /mcp、API 路由——也都走同一个隧道,你不需要每次记住一个新域名。

4. 备选方案:ngrok 的静态子域

如果你已经习惯 ngrok,也可以用相同思路把它“升级”为 static domain。从 2023 年开始,ngrok 即使在免费方案也允许绑定一个静态子域,例如 myapp-dev.ngrok-free.app

最简配置:

# ~/.config/ngrok/ngrok.yml
authtoken: <你的令牌>
tunnels:
  giftgenius-dev:
    addr: 3000
    proto: http
    domain: giftgenius-dev.ngrok-free.app

启动:

ngrok start giftgenius-dev

最终 https://giftgenius-dev.ngrok-free.app 会是一个固定不变的 URL,你把它作为应用的基础 URL 提供给 ChatGPT Dev Mode 即可。

理念相同:

  • 不再有“随机”地址;
  • 只更改隧道的内部状态(运行/未运行),域名不变;
  • 无需重连 Dev Mode。

Cloudflare 与 ngrok 在这方面更像不同口味的冰淇淋。有些人喜欢自有域名与对 DNS 的“精细”掌控(Cloudflare),另一些人偏爱“写个 YAML 就能跑”(ngrok)。对本课程而言两种方式都可行,关键是——稳定的 URL

5. 示意图:ChatGPT Dev Mode ↔ 隧道 ↔ 本地栈

为了稍微形式化一下流程,我们画一张图。

flowchart TD
    ChatGPT["ChatGPT (Dev Mode)"]
    AppCfg["Dev App (配置: https://giftgenius-dev...)"]
    Tunnel["Cloudflare/ngrok 隧道 (giftgenius-dev...)"]
    Next["Next.js 开发服务器 localhost:3000 + MCP 处理器"]

    ChatGPT --> AppCfg
    AppCfg -->|"配置中指定 https://giftgenius-dev.../.well-known/openai-app"| Tunnel
    Tunnel -->|"HTTPS → HTTP 代理"| Next

ChatGPT 从来不知道你笔记本上到底跑了什么。对它而言只有一个 HTTPS 端点。这个端点背后到底是 Vercel、本地隧道还是 Kubernetes,则是你的事。而在本讲中,我们关注的是该 HTTPS 端点在本地开发中的稳定性。

接下来要做的是:让我们的应用内部也把这个地址作为统一的“真相来源”,而不是在硬编码字符串里七零八落——下一节就讲这个。

6. 环境变量与代码里的 baseURL

为了让一切稳定可控,建议在 Next.js 里定义一次“应用对外的基础 URL”,后续所有地方都依赖它。

例如,在我们的 GiftGenius 应用中,在 app/lib/config.ts 里这样定义:

// app/lib/config.ts
export const baseUrl =
  process.env.NEXT_PUBLIC_APP_URL ?? "http://localhost:3000"; // 回退

export const mcpEndpoint = `${baseUrl}/mcp`;  // MCP 服务器的 URL

而在开发时的 .env.local 里填写:

NEXT_PUBLIC_APP_URL=https://giftgenius-dev.yourdomain.com

这样:

  • 在小部件与任何链接中,你总是使用 baseUrl
  • 对 ChatGPT Dev Mode 与浏览器而言,行为一致;
  • 如果明天你迁到 Vercel 的 staging,域名变为 https://giftgenius-staging.vercel.app, 只需修改环境变量即可。

这对以下场景尤其重要:

  • 回调 URL(例如 OAuth、webhook 处理器);
  • 你在小部件中给用户展示的链接(通过 openExternal 的“在浏览器中打开”按钮);
  • 应用逻辑中的任何绝对 URL。

此处我们只谈 dev‑URL,但“baseUrl 的单一事实来源”这一架构思想同样适用于 staging/production。

7. 在 ChatGPT Dev Mode 中更新 URL

好,我们已经拥有一个漂亮且稳定的域名。那在 Dev Mode 里如何使用它?

逻辑如下:

  1. 在 Dev 应用设置里,你只需填写一次根 URL:https://giftgenius-dev.yourdomain.com/
  2. ChatGPT 会据此拉取清单(.well-known/openai-app), 后续也基于同样的根路径访问 MCP(/mcp)、静态资源等。
  3. 若你只改动代码(React 小部件、MCP 处理、样式),完全不需要修改 URL。 只要隧道在运行、Next.js 服务器有响应即可。
  4. 若你更改了域名本身(少见,比如从 ngrok 换到 Cloudflare),则需要进入 Dev Mode 修改一次 endpoint。

某些情况下 ChatGPT 会缓存清单,清单中的变更可能不会立刻生效。Dev Mode 界面通常有“Reload configuration / Refresh App”之类按钮;最糟情况下,断开再用同一个 URL 重新连接也能解决。

重点:只要你不改 URL,Dev Mode 就会自动“拾取”新代码版本。对 App 而言最重要的触发器是域名,而不是 commit‑hash。

8. 在 Dev Mode 中切换 dev / staging / prod

稳定的 dev 域名只是第一步。为了避免随着项目增长在一堆 URL 中迷失,最好及早想清楚 dev 隧道如何融入你的整体环境(dev/staging/prod)与 Dev Mode。尽管 staging 和 prod 更像下一讲关于 Vercel 的主题,但 Dev Mode 现在就能配合多个环境。

下面这张表有助于理解:

环境 基础 URL 代码运行位置
Local
https://giftgenius-dev.yourdomain.com
本地 Next.js + 通过隧道的 MCP
Staging
https://giftgenius-staging.vercel.app
Vercel 预览 / staging 部署
Prod
https://giftgenius.vercel.app
Vercel 生产环境

和 Dev Mode 配合有两种方式。

第一种——只用一个 Dev 应用,但你会不时在它的设置里切换 URL 来测试 staging 或 prod(需要非常小心)。这种方式早期勉强可用,但很容易混淆:今天你测的是本地,明天是 staging,后天忘了切回来,结果通过 Dev 应用把请求发到了生产。

第二种——更健康:多个 Dev 应用,每个都明确绑定到一个环境:

  • GiftGenius Devgiftgenius-dev.yourdomain.com
  • GiftGenius Staginggiftgenius-staging.vercel.app
  • GiftGenius(正式版,通过 Store)→ giftgenius.vercel.app

本讲我们按部就班,先把 dev‑URL 整理好。下一讲你将看到如何把 Vercel 与预览部署自然地纳入 staging/production。

9. 团队协作:多开发者与一个隧道

当只有一个 dev 域名和一个独行开发者时,隧道是你的私人好伙伴。但一旦团队加入项目,隧道与环境就会产生交叉,此时务必要避免“抢一个子域”的混战。

设想两个开发者决定共用一个静态子域,比如 giftgenius-dev.ngrok-free.app。两人都在本地运行 ngrok start giftgenius-dev。最好的结果是其中一个隧道直接起不来(域名冲突);最坏的情况是你们轮流“顶掉”彼此的会话,ChatGPT 时而连到甲、时而连到乙。

这里有几种策略。

最简单的是个人 dev 域名

  • alex.dev.giftgenius.app
  • maria.dev.giftgenius.app

并在 ChatGPT 中为每人建一个 Dev 应用,例如 GiftGenius Dev (Alex)GiftGenius Dev (Maria)。这样大家各自开发互不干扰。

更“团队化”的路线——共享的 staging 端点:

  • 每位开发者都有个人 dev 隧道(用于本地调试);
  • 再配一个 Vercel 上的 staging,合并 feature 分支后部署,全体通过 GiftGenius Staging 这个 Dev 应用来验证。

现实团队里常见的流程:

  • 功能在本地诞生,并通过个人隧道调试;
  • pull request 合并后,大家通过 staging 一起测试(无需隧道,直接访问 Vercel URL)。

10. dev 隧道的安全性(简短且不偏执)

隧道是把你的本地服务器暴露到互联网的便捷方法。而我们都知道,互联网主要由机器人、扫描器和喜欢检查你是否忘了 admin/admin 密码的人构成。

在开发阶段就该记住的基础事项:

  • 隧道会为该端口上所有服务提供外部访问;别顺手把数据库控制台、phpMyAdmin 或“我没设密码的测试 CRM”也挂上去;
  • 不要把隧道 URL 公开贴在开源仓库或群聊里;
  • 不工作时关闭隧道(笔记本也偶尔关一关——它也需要休息)。

更严肃的措施,如 Basic Auth、校验特定请求头或在 URL 中使用 token,我们会在安全模块里讲。现在只需记住一点:隧道是开发工具,不是加固过的服务器。生产流量会跑在像 Vercel 这样的正规托管上,我们将在下一讲深入。

11. 实操:为我们的应用配置稳定的 dev‑URL

现在从理论与注意事项回到实践:把上述内容落地到我们的 Next.js(Apps SDK 模板)示例应用上。

假设项目结构如下:

apps/
  web/          # Next.js 应用 + 小部件
  mcp-server/   # (可选)独立 MCP,或在 web 中提供 /mcp 处理器

实际中你可以把 MCP 直接放在 Next.js 中,比如 app/api/mcp/route.ts,原则是一样的。

步骤 1:修改 .env.local

把稳定的 dev 隧道 URL 填进去:

NEXT_PUBLIC_APP_URL=https://giftgenius-dev.yourdomain.com

在开发代码中我们已经通过 baseUrl 使用了该变量(见上文)。如果还没有用——现在就该抽取出来。

步骤 2:启动开发服务器与隧道

cd apps/web
npm run dev          # Next.js 在 localhost:3000

# 另一个终端
cloudflared tunnel run giftgenius-dev

在浏览器中检查 https://giftgenius-dev.yourdomain.com 是否能打开并显示你的 App。

步骤 3:在 ChatGPT Dev Mode 中连接

在 ChatGPT 的开发者界面中:

  • 创建或编辑 GiftGenius Dev
  • 在 URL/Endpoint 字段填写 https://giftgenius-dev.yourdomain.com/
  • 保存。

之后 ChatGPT 会访问 /.well-known/openai-app 获取清单,然后基于该域名启动你的 App。

此后你可以:

  • 修改小部件代码、MCP 处理逻辑、样式;
  • 重启 npm run dev
  • 重启 cloudflared tunnel run giftgenius-dev

同时,只要域名不变,你再也不用去改 Dev 应用设置

12. 代码中的呈现:以 openExternal 为例

为了与前面写过的内容呼应,我们在小部件中加一个“在浏览器中打开完整界面”的按钮,它同样使用稳定的 dev‑URL。

假设我们有一个名为 GiftWidget 的 React 小部件组件:

// app/components/GiftWidget.tsx
"use client";

import { baseUrl } from "../lib/config"; // 从环境变量获取 baseUrl

export function GiftWidget() {
  const handleOpenFull = () => {
    window.openai.openExternal({
      // 在新标签页中打开应用页面
      url: `${baseUrl}/full`,
      label: "打开完整界面",
    });
  };

  return (
    <div>
      <button onClick={handleOpenFull}>
        完整模式
      </button>
    </div>
  );
}

如果 NEXT_PUBLIC_APP_URL 指向隧道,那么:

  • 在本地开发时会打开 https://giftgenius-dev.yourdomain.com/full
  • 部署到 staging 后会是 https://giftgenius-staging.vercel.app/full
  • 在 prod 则是正式域名。

再次强调——域名的单一事实来源:切换环境,不必改代码。

13. 迷你策略:如何“成熟地”看待隧道

将一切收拢为简单心智模型,可以认为:

  • 隧道只是连接你的笔记本与稳定公共域名之间的一根临时导线
  • ChatGPT Dev Mode 只知道域名,不关心代码物理上在哪里运行;
  • 域名改得越少,你在 ChatGPT 与各类 OAuth 提供方设置里浪费的时间就越少;
  • dev 隧道只是你整体环境地图中的一条记录,旁边还有 staging(Vercel 预览)与 prod(Vercel 生产)。

下一讲将展示如何用 Vercel 的正式托管替换这根导线,并把它与 Git 分支、预览部署和生产环境串联起来。

14. 使用“成熟级”隧道的常见错误

错误 №1:“我已经配置了静态域名,但还是继续用随机 URL”。
有时开发者明明已经配好了漂亮的 giftgenius-dev.yourdomain.com,却习惯性地直接跑 ngrok http 3000。结果是 ChatGPT 看着一个域名,代码却跑在另一个后面。如果你已经有了稳定的 dev‑URL——只用它,并通过配置(命名隧道/配置文件)来启动隧道。

错误 №2:在代码里硬编码 localhost:3000
常见于在 React 组件或 MCP 处理器中写 fetch("http://localhost:3000/api/...")。本地似乎还能跑,但在 Dev Mode,尤其是 staging/prod 上立刻崩。务必把基础 URL 抽到配置中(baseUrlNEXT_PUBLIC_APP_URL),凡是需要绝对链接的地方都使用它。

错误 №3:总是在 Dev Mode 中改 URL,而不是配置稳定隧道。
如果你发现自己在想“算了,我再改一次设置里的 URL 吧”,——这就是警钟。给 ngrok/Cloudflare 设一个静态子域只需 10–15 分钟,但能在开发过程中帮你省下成倍的时间。

错误 №4:没有规则地共用一个团队静态域名。
两个开发者、一个域名 giftgenius-dev.ngrok-free.app,两人想什么时候开隧道就什么时候开。结果是隧道冲突、Dev Mode 的响应“神秘”消失,调试变成“我这儿明明好好的”。团队要么每人一个 dev 域名,要么使用托管在真实主机上的统一 staging 域名。

错误 №5:把隧道当作“准生产”。
有人会想:“既然我通过隧道有了稳定的 HTTPS‑URL,不如让真实用户/支付也走它吧。”这会带来痛苦:笔记本关机——应用就没了;网络掉线——一样;安全性也最多是象征性的。隧道是 dev 工具。处理生产流量应使用 Vercel 等成熟基础设施,我们将在下一讲到达那里。

错误 №6:环境变量与 Dev Mode 设置不同步。
经常有人在 .env.local 里改了 NEXT_PUBLIC_APP_URL,却忘了在 Dev Mode 中更新 URL(或反过来)。结果小部件生成的链接指向一个域名,而 ChatGPT 请求的是另一个。维护一张简单的“环境 ↔ 域名 ↔ ChatGPT 中的 App”对照表,变更时同步更新——比猜现在哪个 URL 才是真的要省心得多。

评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION