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, or8080 - 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
3000or8080
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 Protect the link
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
POSTfor webhooks - your app is not redirecting the request somewhere unexpected
Your app generates localhost links
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
- Read the open a tunnel quickstart
- Learn more about tunnel commands
- Review the security notes