Guides

How to expose localhost

Learn how to expose localhost with Tunnee, share a local app over HTTPS, test webhooks, open local APIs from devices, and keep tunnels safe.

On this page

Sometimes localhost is too local.

Maybe you are testing a webhook, opening your laptop app from your phone, sharing a work-in-progress page with a teammate, or giving an OAuth provider a callback URL it can actually reach.

Tunnee gives your local server a public HTTPS URL and forwards requests back to the port running on your machine.

When this is useful

Use a localhost tunnel when something outside your laptop needs to reach something running on your laptop.

Common cases:

  • testing webhooks from Stripe, GitHub, Telegram, Discord, or another service
  • trying an OAuth redirect flow before the app is deployed
  • opening a local website from your phone or tablet
  • sharing a half-finished feature with a teammate
  • demoing a local build without pushing it to staging
  • testing an API client against your local backend

You do not need to deploy, change DNS, configure your router, or open firewall ports. Keep your app running locally, open a Tunnee tunnel, and use the public URL Tunnee gives you.

What you need

Before opening a tunnel, make sure you have:

  • a local app running on your machine
  • the port number for that app, like 3000, 5173, 8000, or 8080
  • the Tunnee CLI installed and logged in

If you have not installed Tunnee yet, start with the install guide, then run:

tunnee start

That handles account setup and login in one flow.

Start your local app

First, start whatever you want to expose.

For example, a React, Next.js, Vite, Rails, Laravel, Django, FastAPI, Express, or Go server might print something like this:

Local: http://localhost:3000

The important part is the port number. In this example, the port is 3000.

Open the local URL in your browser before creating the tunnel:

http://localhost:3000

If it does not work locally, it will not work through a tunnel either.

Find the right port

The port is the number after localhost: in your local URL.

Here are a few common defaults:

  • Vite: 5173
  • Next.js: 3000
  • Astro: 4321
  • Rails: 3000
  • Laravel: 8000
  • Django: 8000
  • FastAPI: 8000
  • Express: often 3000 or 8080

Do not worry if your app uses something else. Use the port your app prints in the terminal.

Open the tunnel

In another terminal, run:

tunnee open --port 3000

Replace 3000 with the port your app is using.

Tunnee will print a public URL and begin forwarding traffic to your local app. It will look something like this:

https://your-tunnel.tunnee.dev

Open that URL from another browser, send it to your phone, or paste it into the service that needs to reach your local app.

Use the public URL

The public URL replaces the local origin, but the path stays the same.

If your local page is:

http://localhost:3000/pricing

the public version is:

https://your-tunnel.tunnee.dev/pricing

If your local webhook endpoint is:

http://localhost:3000/api/webhooks/stripe

the public webhook URL is:

https://your-tunnel.tunnee.dev/api/webhooks/stripe

That detail matters. Many webhook and OAuth setups need the full URL, not just the tunnel domain.

Keep the tunnel running

Leave the tunnee open command running while you test.

Your local app and the tunnel are two separate processes:

  • stop your app, and the public URL has nothing useful to forward to
  • stop Tunnee, and the public URL stops reaching your machine

Most of the time, you will keep two terminal tabs open:

npm run dev
tunnee open --port 3000

Use whatever command starts your app. The Tunnee command only needs the port.

When you are done, stop the tunnel with Ctrl+C.

Use a custom subdomain

If you want a nicer URL, request a subdomain:

tunnee open --port 3000 --subdomain my-demo

If the name is available, Tunnee will use it for the tunnel.

Custom subdomains are handy when a third-party service makes you save a callback or webhook URL and you do not want to update it every time you restart your tunnel.

For example, you can give a webhook provider this once:

https://my-demo.tunnee.dev/api/webhooks/events

Then keep using:

tunnee open --port 3000 --subdomain my-demo

A tunnel URL is a public URL. Anyone with the link can try to open it while the tunnel is running.

For quick testing, that may be fine. For anything more sensitive, add protection:

tunnee open --port 3000 --password secret123

You can also make temporary tunnels expire automatically on paid plans:

tunnee open --port 3000 --ttl 2h

Use these when you are sharing unfinished admin screens, private demos, customer-specific test pages, or anything that should not be casually opened by someone else.

What not to expose

Be careful with local services that were never meant to be public.

Avoid exposing:

  • database admin tools
  • local dashboards with production secrets
  • debug consoles
  • services with no authentication and real customer data
  • internal tools that can change billing, users, permissions, or infrastructure

If you need to share something sensitive, add app-level authentication and use Tunnee access controls like password protection and short TTLs.

Common examples

Expose a frontend dev server:

tunnee open --port 5173

Expose a local API:

tunnee open --port 8080

Expose a Next.js app:

tunnee open --port 3000

Expose a local webhook endpoint:

tunnee open --port 4242

Then give the webhook provider a URL like:

https://your-tunnel.tunnee.dev/webhooks/events

The path after the domain should match the route in your app.

Test from another device

Tunnels are useful when localhost works on your laptop but not on your phone.

Start your local app, open a tunnel, then open the Tunnee URL on the device:

https://your-tunnel.tunnee.dev

This is useful for checking real mobile browsers, testing deep links, or trying a native app against a local API.

If your mobile app points to an API base URL, use the tunnel URL as that base URL while testing:

https://your-tunnel.tunnee.dev

Keep the tunnel open while the device is using it.

Troubleshooting

If the public URL does not load, check the boring stuff first. It is usually one of these.

The local app is not running

Open the local URL directly:

http://localhost:3000

If that fails, restart your app before looking at Tunnee.

The port is wrong

If your app says it is running on localhost:5173, this will not work:

tunnee open --port 3000

Use the port your app actually printed:

tunnee open --port 5173

The app only listens on a different host

Most local dev servers listen on localhost by default, which is fine.

If your framework has custom host binding settings, make sure the service is reachable from your own machine at localhost:<port> or 127.0.0.1:<port>.

The URL path is missing

Tunnee forwards the full request path.

If your local route is:

http://localhost:3000/api/webhooks/stripe

then the public version is:

https://your-tunnel.tunnee.dev/api/webhooks/stripe

Do not paste only the domain into a webhook provider if your app expects a specific path.

You restarted the tunnel

One-off tunnel URLs can change when you stop and start them. If you pasted the old URL into another service, update it with the new one.

For a stable name, use a custom subdomain:

tunnee open --port 3000 --subdomain my-demo

The third-party service rejects the URL

Some services are picky about callback and webhook URLs.

Check that:

  • the URL starts with https://
  • you pasted the full path, not just the domain
  • the service is not still using an old tunnel URL
  • your local route accepts the HTTP method the service sends, like POST for webhooks
  • your app is not redirecting the request somewhere unexpected

Some apps build absolute URLs from a local config value, so a page loaded through the tunnel might still link back to localhost.

If that happens, set your app’s public base URL, site URL, callback URL, or API URL setting to the Tunnee URL while testing.

The exact variable depends on your app, but it often looks like one of these:

APP_URL=https://your-tunnel.tunnee.dev
NEXT_PUBLIC_APP_URL=https://your-tunnel.tunnee.dev
PUBLIC_SITE_URL=https://your-tunnel.tunnee.dev

A quick safety checklist

Before sharing a tunnel URL, ask:

  • is this the exact local app I meant to expose?
  • does the app have auth if it shows private data?
  • am I exposing an admin panel, database UI, or debug console by accident?
  • should this tunnel have a password or short TTL?
  • did I stop the tunnel when I was done?

Tunnee exposes only the local port you choose, not your whole machine. Still, treat the URL like any other public link while it is active.

Next steps

Related docs

Made by Basic Shapes in Europe.