localhostly is a free ngrok alternative
If you are leaving ngrok, here is what you get instead: one command and one public HTTPS URL for your local app. Run localhostly 3000 and port 3000 answers at https://brave-amber-fox.localhostly.iblabs.io, with no port opened on your router. The account is free and asks only for an e-mail address: 2 tunnels, 1000 requests per minute and 25 GiB a month.
Get your token Install localhostly
$ localhostly 3000 https://brave-amber-fox.localhostly.iblabs.io → http://127.0.0.1:3000 Ctrl+C to stop. Requests: 21:14:03 GET / 200 12ms 21:14:05 POST /api/webhook 201 31ms
The free account in numbers
The free account is the only account: there is no paid plan and no card. The traffic limit counts both directions and the month turns on the 1st, in UTC. Past a limit, visitors see a page that says which limit was reached and when the tunnel works again.
- 2 tunnels open at once
- 1000 requests per minute
- 25 GiB of traffic per month, both directions
- $0 a month: no paid plan and no card
- 429 past a limit, with a
Retry-Afterheader
One command, one public URL
The localhostly agent on your computer opens one WebSocket connection to the localhostly server; when someone opens your URL, the server reads the name in the address, sends the request down that connection, and the agent replays it on your local port and streams the answer back. HTTP requests and WebSocket connections pass.
- Get a token. Enter your e-mail in the sign-up form. The token appears on the sign-up page; there is no confirmation e-mail.
- Save it.
localhostly login <token>stores it in~/.localhostly/config.json, readable only by your user. - Open the tunnel.
localhostly 3000. The terminal prints the public URL and one line per request: time, method, path, status and milliseconds.
Your app receives the visitor's method, path, headers and body, plus X-Forwarded-For, X-Forwarded-Proto: https and X-Forwarded-Host. To send a different address and Host to your app, add --host myapp.test:3000. The name is drawn at random from 3 English words, adjective, color and animal, and nobody picks it.
Webhooks reach your app directly
Webhook calls are not browsers, so they skip the warning page and reach your app directly. Paste https://<name>.localhostly.iblabs.io/<your path> into the provider's webhook settings, and each call shows in your terminal with method, path, status and time.
If the agent loses its connection, it reconnects by itself and, within 10 minutes, gets the same URL back: the name stays reserved for that long, so a short drop does not mean registering the webhook again.
Streaming responses and big uploads pass through
Requests travel in pieces of up to 64 KiB in both directions, so a body never sits whole in memory. A 20 MiB upload goes through with peak memory under 100 MiB in the measured run, and a 20 MiB download reaches a slow visitor the same way.
Server-Sent Events arrive while your app writes them: in a measured stream, events written at 0, 3 and 6 seconds reached the visitor at 0.6, 3.6 and 6.6 seconds. Your app has 60 seconds to start answering; once the response starts there is no time limit, which is why long downloads pass. WebSocket connections from the visitor reach your app, and the upgrade completes only after your app accepts it, with the subprotocol it chose.
Lock the tunnel with a password or an IP list
Add --password and every visitor must enter it; without a value, localhostly creates a 12-character password and shows it only in your terminal. Your app never receives the visitor's Authorization header.
Add --allow with IP addresses or ranges, IPv4 or IPv6, up to 64: --allow 203.0.113.7,10.0.0.0/8. Any other address gets a 403 page that shows it, and a WebSocket from it gets 403 too. With --password as well, a visitor needs both.
- Browsers see a warning page on their first visit saying the site runs on someone's own computer. API clients skip it, and so do tunnels with
--allow. - The server logs method, path without the query string, status, time, tunnel name and account. It never logs request bodies or cookies.
- Your token is stored on our side only as a SHA-256 hash.
- HTTPS ends at the localhostly load balancer, so the localhostly server sees and relays your traffic. It is not end-to-end encryption.
Keep it running as a system service
localhostly service install 3000 runs the tunnel as a system service: systemd on Linux, launchd on macOS, a Windows service. It reconnects by itself, waiting 1 second and doubling up to 60 seconds, and asks for the same name after a restart. localhostly service status checks it and localhostly service uninstall removes it.
Install
localhostly runs on Node.js 24 or newer.
-
Try it without installing:
npx localhostly 3000 -
Install it once:
npm i -g localhostly
After that, save your token with localhostly login <token> and localhostly 3000 opens the tunnel.
Get your token
Sign-up asks for an e-mail address and nothing else. The token appears on the sign-up page, with no confirmation e-mail, and localhostly login <token> saves it on your computer.