The "expose localhost" problem hasn't changed in a decade — you have a server on 127.0.0.1:3000 and something on the internet (a webhook provider, a mobile app, a stakeholder review) needs to reach it. What's changed is which option is easiest in 2026.
Option 1: a tunnel service (90% of cases)
The default. Run one command, get a public HTTPS URL. The agent on your laptop dials a long-lived TLS connection to the service; incoming requests travel it.
Picks:
- lrok — reserved subdomain on the free plan, $9/mo flat for unlimited. Real Let's Encrypt certs, no interstitial warning, request inspector. (install)
- ngrok — the original. Free tier is restrictive; paid plans are tiered.
- Loophole — serious tunnel platform with multi-region edges; reserved subdomains are paid-only.
- Expose — tight Laravel/artisan integration; the natural pick if your stack is PHP.
- Cloudflare Tunnel — free, requires a domain on Cloudflare DNS. Setup is heavier; once configured, very stable.
- localtunnel — free, no signup. URL rotates; not for webhooks.
- bore — open-source Rust reverse-tunnel you self-host; no hosted service, but zero infrastructure cost if you already have a VPS.
Use a tunnel when you need: webhook testing, OAuth callbacks during dev, mobile-app real-device testing, sharing a preview with someone non-technical.
Option 2: port-forward + dynamic DNS (the dinosaur option)
Forward a port on your home router, register a dyndns hostname. You'll spend a Saturday on it; you'll get something that works only on your home network.
Use this when: you specifically need an L4 path with no L7 proxy in between, you're already running a server with public IP, and the dynamic DNS update lag is acceptable.
This isn't the path for "I want to test a Stripe webhook in 30 seconds."
Option 3: deploy to a preview environment
Vercel, Netlify, Render, Fly all have preview deploys triggered by a PR. Push the branch; you get a public URL.
Use this when: the iteration loop is slow enough that build time doesn't bother you, the testing is for stakeholders rather than yourself, and you don't need a server-side handler hitting your local DB.
It's the wrong tool for "I'm iterating on a webhook handler at 3-second cycle time."
Option 4: ssh -R reverse tunnel
If you have any server with a public IP and sshd running, you can:
$ ssh -R 8080:localhost:3000 user@your-server.com -N
Now your-server.com:8080 forwards to your laptop's :3000. Useful for one-off debugging where you already have ssh access. Pain points: the ssh server has to allow GatewayPorts, you're terminating TLS yourself if you need it, and there's no inspector.
What I'd pick in 2026
For most webhook / OAuth / mobile-testing work: a tunnel service. Specifically lrok if you want a reserved free-plan subdomain that survives restarts; Cloudflare Tunnel if you already live in Cloudflare and have a domain there; ngrok if you've already got a Pro subscription.
For preview deploys: keep them for PR review and prod-parity testing, not for the inner dev loop.
For ssh -R: it's a great trick to know but rarely the cleanest path.
The 30-second start with lrok
$ curl -fsSL https://lrok.io/install.sh | sh
$ lrok login
$ lrok http 3000
Forwarding https://violet-mole.lrok.io → http://127.0.0.1:3000
That's it. Walkthroughs for specific stacks: Next.js, Django, Rails, Go, + 15 more.