Share localhost from Mac: ngrok and alternatives
Someone needs to see what’s running on your localhost, or a service needs to call it. A tunnel gives your dev server a public HTTPS address in one command. Here’s which one to use, and how to get past the “host not allowed” error that usually follows.
By Anmol Sethi · Updated
In short
The quickest free option is a Cloudflare quick tunnel: brew install cloudflared, then cloudflared tunnel --url http://localhost:3000 prints a public trycloudflare.com link, with no account. ngrok (ngrok http 3000) needs a free account but can keep the same address. Either way, allow the tunnel’s host in your dev server, or it answers with a “host not allowed” error.
1. Quickest: a Cloudflare quick tunnel
brew install cloudflared
cloudflared tunnel --url http://localhost:3000Your quick Tunnel has been created! Visit it at (it may take some time to be reachable):
https://quiet-river-example.trycloudflare.comSend that link to anyone. It works until you press Ctrl-C, and you get a new random address each time. No account, no sign-up. Cloudflare means quick tunnels for testing, not for running a site; for a fixed address, set up a named tunnel with a Cloudflare account and your own domain.
Rather click than type? Portlivo shows every port with its project and frees it in one click. Try it free for 7 days →
2. ngrok
brew install ngrok
ngrok config add-authtoken <token> # once, from your ngrok dashboard
ngrok http 3000ngrok needs a free account. In return you get a web inspector at http://localhost:4040 that shows every request and can replay it, which is handy for webhooks, and the free plan includes a fixed domain, so a callback URL you register once keeps working.
On the free plan, people opening the link in a browser first see an ngrok warning page they have to click through. API calls with an ngrok-skip-browser-warning header skip it.
Every option at a glance
| Tool | Account | Address | Start it |
|---|---|---|---|
| Cloudflare quick tunnel | None | Random, new each run | cloudflared tunnel --url http://localhost:3000 |
| ngrok | Free account | Can stay the same | ngrok http 3000 |
| Tailscale Funnel | Tailscale account | Your Mac’s ts.net name | tailscale funnel 3000 |
| VS Code port forwarding | GitHub or Microsoft | Private unless you make it public | Ports panel → Forward a Port |
| localtunnel | None | Random | npx localtunnel --port 3000 |
| localhost.run | None | Random | ssh -R 80:localhost:3000 nokey@localhost.run |
- Tailscale Funnel suits you if Tailscale is already on your Mac: the address stays the same. Without Funnel,
tailscale serveshares only with devices on your own tailnet. - VS Code forwards ports from the Ports panel. Links need a sign-in until you set the port’s visibility to Public, which is safer for showing work to a teammate.
- localtunnel shows visitors a password page first; the password is your public IP, which
curl ifconfig.meprints.
Testing Stripe webhooks? stripe listen --forward-to localhost:3000/api/webhooks forwards events to your Mac with no tunnel at all. Other providers with a CLI often have the same.
3. Fix “host not allowed” errors
Dev servers reject requests for host names they don’t recognise, to block DNS rebinding attacks. Through a tunnel, the host is the tunnel’s, so the page fails even though the tunnel works. Allow the tunnel’s domain:
| Error | Fix |
|---|---|
Vite: Blocked request. This host ("….trycloudflare.com") is not allowed. | server: { allowedHosts: [".trycloudflare.com"] } in vite.config.js |
webpack dev server: Invalid Host header | devServer: { allowedHosts: "all" } |
Rails: Blocked hosts: ….ngrok-free.app | config.hosts << ".ngrok-free.app" in config/environments/development.rb |
Django: DisallowedHost at / | Add ".trycloudflare.com" to ALLOWED_HOSTS |
Next.js: a warning about cross-origin requests to /_next | allowedDevOrigins: ["*.trycloudflare.com"] in next.config.js |
A leading dot (or *.) allows every subdomain, so a new random quick-tunnel address keeps working. Use the domain of the tunnel you actually use: .ngrok-free.app, .ts.net and so on.
Other things that break
- Absolute localhost URLs. The page loads but API calls, images or redirects point at
http://localhost:…, which on the visitor’s machine is their own computer. Use relative paths, or proxy the API through your dev server as in the phone guide. - OAuth sign-in. Google, GitHub and others only redirect to callback URLs you registered, so a random tunnel address fails. Use a fixed address (ngrok’s free domain, Tailscale, a named Cloudflare tunnel) and register it once.
- Cookies. A cookie set for
localhostisn’t sent to the tunnel’s domain, so you’re signed out there. Sign in again through the tunnel.
Anyone with the link reaches your dev server, debug pages and error traces included. Stop the tunnel when you’re done, and don’t share a server with DEBUG or a debugger console turned on.
The one-click way: Portlivo
Portlivo shares any dev server from its menu: Share via cloudflared starts a quick tunnel (or ngrok, if that’s what you have), copies the public link and marks the server with a globe. Stop Sharing ends it, and quitting Portlivo ends them all, so no tunnel stays open by accident. See Share and QR.
Questions people ask
- What’s the best free alternative to ngrok?
- A Cloudflare quick tunnel:
cloudflared tunnel --url http://localhost:3000gives a public HTTPS link with no account. It changes every run, so use ngrok’s free domain or Tailscale Funnel when you need the same address. - Why does Vite say “This host is not allowed”?
- Vite blocks unknown host names. Add the tunnel’s domain to
server.allowedHosts, for example[".trycloudflare.com"]. - Is sharing localhost safe?
- Anyone with the link can reach that one server while the tunnel runs. Share it only as long as needed, and not with debug consoles enabled.
- Do I need a tunnel to test webhooks locally?
- Not always. Stripe’s CLI forwards events to localhost with
stripe listen --forward-to. Otherwise a tunnel with a fixed address saves re-registering the URL.
Try Portlivo free for 7 days. Every feature, no account.
Download for MacSend to my MacStill have a question?
Write to us — a real developer reads every email and replies personally.
Snipora