CodeGym /Courses /ChatGPT Apps /Local run, HTTPS tunnel, and availability for ChatGPT

Local run, HTTPS tunnel, and availability for ChatGPT

ChatGPT Apps
Level 2 , Lesson 2
Available

1. localhost — it’s only for you, not for ChatGPT

Let’s start with the main cognitive dissonance of this module. You open http://localhost:3000 in your browser, everything works great, Next.js smiles, the widget renders. It feels logical: “Well, since I have a URL, let’s just give it to ChatGPT.”

The problem is that localhost is not “a magical domain of my machine on the internet.” It’s a special name that always points to the same machine where the browser or client is running. Your laptop is talking to itself. OpenAI’s servers, where ChatGPT runs, can also access localhost… but their own — inside the data center. And your Next.js is happily hidden behind a home router, NAT, and possibly a corporate VPN.

In this lecture we will:

  • figure out why localhost is not accessible to ChatGPT;
  • set up an HTTPS tunnel via cloudflared from localhost:3000 to a public URL;
  • connect that URL in ChatGPT Dev Mode;
  • verify the “code → tunnel → ChatGPT” chain with a simple widget change and discuss common pitfalls.

Two simple facts follow from this:

  1. ChatGPT doesn’t know where your laptop is.
  2. Even if it did, it still can’t contact it directly — inbound connections are closed.

In addition, ChatGPT fundamentally works only with public HTTPS endpoints (entry points): you need a normal domain and a TLS certificate. For security reasons, OpenAI’s servers don’t call arbitrary HTTP addresses without encryption, so you need exactly an HTTPS domain with a valid certificate. Simply exposing http://my-external-IP:3000 is no longer an option.

Therefore, we need an intermediary — a service that:

  1. Lives on the internet with a proper HTTPS domain.
  2. Can safely forward requests from that domain to your localhost:3000.

That’s an HTTPS tunnel.

2. What an HTTPS tunnel is: an intuitive model

Stripping away the scary words, a tunnel is a service that gives you a temporary (or permanent) public URL and forwards all requests from there to your local port. In networking terms, it’s essentially a reverse proxy server that maintains an outbound connection to the cloud.

An intuitive analogy: you’re sitting behind a closed door (a home router), and the tunnel is a courier standing outside with a sign “all mail here,” who occasionally runs to you through the back door and hands you the letters.

The request path looks roughly like this:

sequenceDiagram
    participant ChatGPT as ChatGPT (cloud)
    participant Tunnel as HTTPS tunnel
(Cloudflare / ngrok) participant Dev as Your dev server
(localhost:3000) ChatGPT->>Tunnel: HTTPS request to https://xyz.trycloudflare.com Tunnel->>Dev: HTTP request to http://localhost:3000 Dev-->>Tunnel: Next.js response Tunnel-->>ChatGPT: HTTPS response

Key points here are as follows.

First, you are the initiator of the connection to the tunnel service. A utility (cloudflared, ngrok, etc.) itself establishes an outbound connection to the cloud. This is almost always allowed even behind NAT/firewalls.

Second, the tunnel service gives you an HTTPS domain with a valid certificate, so you don’t need to set up a self‑signed TLS certificate manually.

Third, to ChatGPT your App looks like a regular web service with an HTTPS domain. It has no idea that the traffic then goes off to someone’s laptop.

3. What tunnels exist and what we’ll use in the course

In the web development ecosystem, there are several popular solutions for this task:

  • ngrok — the classic, for a long time the de facto standard for “how to expose local to the outside.”
  • Cloudflare Tunnel (cloudflared) — a modern free solution from Cloudflare, gives a domain like *.trycloudflare.com even without registration; you can also attach your own domain if you want.
  • LocalTunnel — minimal magic, just an npm package that issues a temporary HTTPS URL like https://something.loca.lt.

They all solve the same problem: give a local server a public HTTPS domain that suits ChatGPT.

To keep focus in the course, we’ll take Cloudflare Tunnel with the cloudflared utility as the “primary” tool. The reasons are simple: no registration needed for quick tunnels, you get proper HTTPS, and it’s easy to launch with a single command.

That said, if you’re already an ngrok fan — no problem. Commands will be slightly different, but the concept is identical: ngrok http 3000 instead of cloudflared tunnel --url http://localhost:3000.

To make things easier to navigate, let’s summarize the tools in a small table.

Tool Registration required? URL format Key pros Key cons
Cloudflare Tunnel No
https://*.trycloudflare.com
Quick start, valid HTTPS URL changes on each run
ngrok Yes
https://*.ngrok.app
Huge docs, ecosystem Free URL also rotates
LocalTunnel No
https://*.loca.lt
npm install, minimal magic Unstable domains, fewer features

In this lecture, we’ll focus on Cloudflare Tunnel in “quick one‑off tunnel” mode. That’s more than enough to hook your Next.js up to ChatGPT in Dev Mode.

4. Verify your local Next.js is alive

Before tunneling anything out, you must make sure that the local server is actually running. Otherwise, you’ll be debugging the tunnel while the real problem is that Next.js simply isn’t started.

The usual sequence:

# from the project root with the Apps SDK template
npm install      # if you haven't yet
npm run dev      # start the Next.js dev server

By default, Next.js 16 will come up on http://localhost:3000 (if the port isn’t taken). In the terminal you’ll see something like:

ready - started server on 0.0.0.0:3000, url: http://localhost:3000

Open http://localhost:3000 in a browser and make sure the template page loads. This is your “local lab.” If something doesn’t work here (build error, TypeScript complains, port is busy) — fix that first, then move on to the tunnel.

5. Launch Cloudflare Tunnel: from localhost to public HTTPS

Let’s get to the fun part — make it so that anyone on the internet (including ChatGPT) can open your Next.js via an HTTPS URL.

Installing cloudflared

The installation method depends on your OS. The simplest path for macOS is via Homebrew:

brew install cloudflare/cloudflare/cloudflared

On Windows and Linux you can download a ready binary or use a package manager, as recommended by Cloudflare’s documentation (links are in the module’s additional materials).

Verify the installation with:

cloudflared --version

If the utility isn’t found, check your PATH or restart the terminal.

Quick one‑off tunnel

Our goal now is a minimal working tunnel, with no account, domain, or complex configs. For this, cloudflared has a quick tunnel mode that gives you a URL on the trycloudflare.com domain.

With npm run dev running, execute in another terminal:

cloudflared tunnel --url http://localhost:3000

Remember a simple rule: HTTPS outside, HTTP inside. cloudflared gives you an external HTTPS domain, but it talks to your localhost:3000 over plain HTTP.

After a short log you’ll see a line like:

INF +-------------------------------------------------------------+
INF |  Your quick Tunnel has been created!                        |
INF |  https://giftgenius-1234.trycloudflare.com                  |
INF +-------------------------------------------------------------+

This https://giftgenius-1234.trycloudflare.com is the new public address of your local application. The tunnel accepts HTTPS requests on that domain and forwards them to http://localhost:3000.

A couple of important notes.

First, the terminal with cloudflared must stay open as long as you need the tunnel. Once you close it (or press Ctrl+C), the tunnel goes down and the URL stops working.

Second, with each quick tunnel run the URL may be new. For our training development, that’s fine: the goal of this module is simply to give ChatGPT access to your local Next.js via any working HTTPS address. But it means you’ll sometimes need to update the URL in ChatGPT Dev Mode. In module 7 we’ll return to tunnels and set up a proper stable dev domain so you don’t have to chase addresses.

Test the tunnel like a normal website

Before wiring all this into ChatGPT, let’s just verify the tunnel is open from the internet.

  1. Open the obtained https://...trycloudflare.com in a browser.
  2. You should see the same UI as at http://localhost:3000.
  3. In the console where npm run dev is running, you’ll see new requests — meaning Next.js is indeed serving the external call.

If the page doesn’t open or shows an error, first check:

  • Whether npm run dev is running.
  • Whether you got the local URL right when starting the tunnel (http://localhost:3000, not https:// and not port 3001).
  • Whether anything is blocking outbound connections (rare, but possible in strict corporate networks).

6. Wire this URL into ChatGPT Dev Mode

Now we have everything to connect the chain:

ChatGPT (cloud) → your HTTPS tunnel → local Next.js.

You’ve already seen the Dev Mode UI in the previous lecture; now we’ll just repeat the same steps with a real HTTPS URL, not a theoretical one.

The general sequence of actions in ChatGPT is as follows.

First, open ChatGPT in a browser and go to the developers section (usually something like “Developer,” “Apps,” “My apps” — the exact names may change as the UI updates).

Create a new app or edit an existing dev app if you’ve already created one.

In the field where the URL of your App is required, specify the tunnel root address, for example:

https://giftgenius-1234.trycloudflare.com/mcp

The entry point is our /route/mcp.ts. When connecting, ChatGPT will start from there and then fetch all the necessary information. The template README may state a different path if you have multiple apps; but for now assume that the tunnel’s root URL plus /mcp is what you need.

Save the app configuration. At this point ChatGPT makes several requests to your application through the tunnel:

  • Reads the App manifest (metadata, tools, and so on).
  • Checks MCP endpoint availability.
  • Fetches the list of all tools and resources.
  • Caches the HTML code of all widgets(!)

If everything is fine, you’ll see your application in the list of Dev Apps. If something is broken (invalid manifest, server not responding, tunnel is down), ChatGPT will show an error like “App unavailable” or something similar.

Important: ChatGPT will use that same HTTPS tunnel URL both for calling tools (MCP) and for loading the widget and static assets. In the next section we’ll look at these two roles separately.

7. How requests flow now: the two roles of your tunnel

It’s important to clearly understand what exactly ChatGPT does with this URL. In the Apps SDK architecture there are two main entry points: the MCP endpoint and the UI widget.

Simplified, the chain looks like this:

flowchart LR
    ChatGPT["ChatGPT (model)"] 
    subgraph Internet
        Tunnel[HTTPS tunnel
giftgenius-1234.trycloudflare.com] end Local["Next.js dev server http://localhost:3000"] ChatGPT -- HTTP(S) requests to /mcp --> Tunnel ChatGPT -- Iframe load /widget --> Tunnel Tunnel --> Local

The tunnel effectively has two main roles:

  • Role 1: MCP endpoint (tools). When the model decides to call a tool, it makes an HTTP POST to the MCP endpoint (in the template this is the app/mcp/route.ts route in Next.js) on the same tunnel domain.
  • Role 2: UI widget and static assets. When the model decides to show a widget, it embeds an iframe with your URL (usually /widget or whatever is specified in the manifest), and the load also goes through the tunnel.

The tunnel isn’t “for just one thing”; it’s a single door into your local App: UI, MCP, static — all of it goes through the same public HTTPS domain.

8. Practice: verify the “code → tunnel → ChatGPT” chain

To be sure this really works and isn’t just a nice diagram, perform a minimal practical scenario.

First, run npm run dev and make sure http://localhost:3000 opens in your browser.

Second, run cloudflared tunnel --url http://localhost:3000 and obtain a public HTTPS URL. Test it in another browser or even on another device (for example, on your phone over mobile internet) — this way you’ll know for sure that requests are going over the internet, not just looping on your machine.

Third, open ChatGPT, go to Dev Mode, and make sure your App is connected to that URL. In the Composer in a ChatGPT chat, select your App, start a conversation, and see whether ChatGPT embeds the widget and loads your UI.

To clearly see that this is your code, tweak something very simple in the widget, for example the title:

// app/widget/page.tsx (example)
'use client';

export default function GiftGeniusWidget() {
  return <h1>GiftGenius via the tunnel 🚇</h1>;
}

After saving the file:

  1. Wait for Next.js to finish fast refresh,
  2. Go to the section in ChatGPT where you added your app and refresh it,
  3. Open/refresh the session with the App in ChatGPT,
  4. Send a new prompt to ChatGPT asking it to render your widget,
  5. Make sure the new title text is visible inside ChatGPT.

That’s the little moment of truth: you just changed code on your machine, and the change appeared in ChatGPT’s cloud UI through the tunnel.

9. A bit about security and “what exactly you exposed”

Any tunnel is not a toy — it’s a real public entry point to your machine. In our training scenario we only expose localhost:3000, where the Next.js application runs. This is relatively safe if:

  • that port isn’t used for anything else; and
  • you don’t have a “monster app” that for some reason bundles a DB admin panel, phpMyAdmin, and four more demo services.

A couple of important practical rules.

A tunnel is a development tool, not production. We deliberately use it in Dev Mode, not for real users and certainly not for taking payments.

Avoid running any administrative panels, passwordless databases, and similar stuff on this same port (3000). Anything that responds on that port becomes visible from the internet while the tunnel is alive.

Don’t share your trycloudflare.com URL indiscriminately. Yes, the odds that someone will actively scan it while you’re doing a training project are low. But the habit of “we drop a dev server link anywhere” can bite you in production.

Later, when we get to Vercel and production environments, we’ll use proper hosting with stable domains and production‑grade security, and the tunnel will remain a purely dev tool.

10. Common mistakes when working with local runs and the tunnel

So, we already have a running local Next.js, a working HTTPS tunnel, and Dev Mode connected in ChatGPT. To wrap up — a few common mistakes that almost everyone hits in the first iterations, and how to quickly diagnose them.

Error #1: trying to use http://localhost:3000 directly in ChatGPT.
Sometimes beginners just paste this URL into the Dev Mode config and wonder why ChatGPT says it can’t reach the App. Reminder: localhost means “myself” for whoever is making the request. For ChatGPT that’s OpenAI’s servers, not your laptop. You won’t see any special logs on your side because the requests never reach your machine.

Error #2: starting a tunnel to a non‑existent or wrong port.
A common scenario: you used to run npm run dev on port 3000, the server has already stopped, but in the second terminal out of habit you start cloudflared tunnel --url http://localhost:3000. Cloudflare gives you a nice HTTPS domain, but when you open it — error. Diagnosis is simple: the local server isn’t alive. Always check http://localhost:3000 in a browser first, then enable the tunnel.

Error #3: mixing up http:// and https:// when starting the tunnel.
The tunnel itself gives you HTTPS on the outside, but it must talk to the local server over HTTP, for example http://localhost:3000. Trying to specify https://localhost:3000 often leads to weird internal TLS errors or just unreachability. Remember the rule: HTTPS outside, HTTP inside.

Error #4: closing the terminal with the tunnel while actively testing in ChatGPT.
Another classic: everything is configured, the App works in ChatGPT, then you accidentally close the terminal window with cloudflared. Ten minutes later you return to ChatGPT and see “App unavailable.” The reason is simple: the URL remained in the App settings, but the tunnel itself is off. Remember a simple rule: while testing the App in Dev Mode, keep the terminal with the tunnel running.

Error #5: unintentionally using a new URL after restarting the tunnel.
In quick tunnel mode, Cloudflare gives a new *.trycloudflare.com each run. If you stopped and started cloudflared again while ChatGPT still has the old URL, ChatGPT will dutifully go there and hit timeouts or a different service. Whenever the tunnel URL changes, update it in Dev Mode settings. We’ll later discuss how to get a stable dev domain so you don’t have to chase URLs.

Error #6: exposing extra or risky services on the same port.
Sometimes developers, for convenience, run on port 3000 not only Next.js but also various “service” panels: a debug panel, an experimental unauthenticated API, and so on. As soon as you expose that port through the tunnel, all those things become accessible from the outside. For a training project, nothing terrible may happen, but this habit in real projects greatly increases the risk of leaks and compromise. Always keep in mind: everything that responds on the port specified in the tunnel faces the internet.

1
Task
ChatGPT Apps, level 2, lesson 2
Locked
Dev Mode URL panel (baseURL + /mcp)
Dev Mode URL panel (baseURL + /mcp)
1
Task
ChatGPT Apps, level 2, lesson 2
Locked
Node script “one-time tunnel → update .env.local”
Node script “one-time tunnel → update .env.local”
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION