What is a localhost tunnel, and is it safe?
A tunnel gives one port on your computer a public address, over a connection your computer opens. How it works, and what it does and doesn't expose.
By Mads Sauer. Published .
A localhost tunnel is a small program that gives one port on your computer, such as a dev server on localhost:3000, a public https address. It works by opening a connection from your computer out to a server on the internet, which passes each visitor's request back down that connection to your port.
It is safe in one sense: it opens nothing on your router, and it stops when you stop it. It is not a lock, though. Anyone who has the address reaches whatever that port serves, debug pages included.
How a tunnel works
Anywhere
A visitor's browser
Opens the link you sent, on any network
Their https request
On the internet
ouicu's edge
Finds the open connection for that name
One WebSocket, opened by your computer
Each request on a stream of its own
Your computer
ouicu share
Connects each stream to the one port you named
Your dev server
Answers on localhost, as usual
- You run the tunnel's program and name a port. It connects out to the tunnel service, as a browser connects to a website, and keeps the connection open.
- The service gives you a public address and ties it to that connection.
- A visitor opens the address. Their request reaches the service, which sends it down the open connection.
- The program hands the request to your port, and sends the answer back the same way.
Because your computer opens the connection, it passes your router and firewall the way any web traffic does. There is no port to forward and no public IP address to have, which is why a tunnel works from home Wi-Fi, an office network or a phone's hotspot. Microsoft's dev tunnels say the same of theirs: “Tunneling requires outbound connections to be made to the service hosted in Azure. No inbound connections are required to use the service.”
A tunnel, port forwarding or a reverse proxy
| Who connects to whom | What you set up | Reachable | |
|---|---|---|---|
| Port forwarding | Visitors connect in to your router, which passes them to your computer | A rule on the router, a firewall opening, and an address that may change | Until you remove the rule |
| A reverse proxy, such as nginx | Visitors connect to a server, which passes them to an app on that server or its network | A server with a public address, DNS and a certificate | While the server runs |
| A tunnel | Your computer connects out; visitors connect to the tunnel service | Nothing but the program | While the program runs |
nginx describes itself plainly: “nginx ("engine x") is an HTTP web server, reverse proxy, content cache, load balancer, TCP/UDP proxy server, and mail proxy server.” It runs where a site is hosted, and can't reach a laptop behind a home router on its own. A tunnel solves that last part, so the two are not rivals: some setups put a tunnel in front of nginx.
Inside one tunnel: ouicu
ouicu is a tunnel of this kind. This is what happens after ouicu share 3000, from its source code:
- The command line tool opens a WebSocket to
wss://tunnel.ouicu.com/v1/connect, with your key in the request. To your network, it is an ordinary outgoing https connection. - Inside it runs a multiplexer, yamux, so many requests share the one connection. ouicu's edge opens a new stream for each request a visitor makes. A WebSocket, such as your dev server's hot reload, keeps its stream for as long as it stays open.
- The tool connects each stream to
127.0.0.1:3000, or to[::1]:3000when that fails, and copies the bytes both ways without changing them. It connects nowhere else: not to another port, and not to another machine on your network. - When nothing listens on the port, the tool answers by itself, and visitors see a page saying the site isn't running yet.
- For each request, the edge sends the tool one line: the method, the path, the status and the time it took. Your terminal prints them, so you see every request as it happens.
- If the connection drops, the tool reconnects at the same address, waiting a little longer between tries.
What your dev server sees
The edge changes a few headers on the way in, so a dev server that guards against unknown addresses answers as it does for you:
| Header | What ouicu sends | Why |
|---|---|---|
Host | localhost:3000 | Dev servers such as Vite only answer hosts they know, and they know localhost |
X-Forwarded-Host | calm-otter-4821.ouicu.app | The public address, for code that builds links |
X-Forwarded-Proto | https | The visitor's connection is https |
X-Forwarded-For | The visitor's IP address | Who asked, since every connection to your server comes from the tool on your own computer |
Origin | http://localhost:3000, on WebSocket upgrades and Next.js dev requests only | So hot reload passes origin checks. A form post keeps the public origin, and its checks still run |
A redirect your server sends to http://localhost:3000 reaches the visitor as the public address. On the way out, the edge passes only what web pages are made of: no video, audio or downloads, and nothing over 50 MB. Every page also gets a small Report button.
What a tunnel exposes, and what it doesn't
A tunnel doesn't open your computer. It opens one port, for as long as it runs, and your files, your other ports and the rest of your network stay where they were. That one port, though, is open to anyone with the address, and dev servers are built for one trusted person: you.
- Debug pages. Flask's server listens only on your computer by default because “in debugging mode a user of the application can execute arbitrary Python code on your computer.” Its docs go on: “The debugger allows executing arbitrary Python code from the browser. It is protected by a pin, but still represents a major security risk.” Django, with
DEBUGon, shows error pages with “a lot of metadata about your environment, such as all the currently defined Django settings”. - Trust in localhost. To your server, every visitor through a tunnel connects from your own computer. An admin page that lets in anyone from
127.0.0.1, or a seeded account with a default password, is open to them too. - The address itself. A random address is hard to guess, but links get forwarded, pasted into chats and kept in browser histories.
- The service in between. An https tunnel ends the encryption at its edge, which reads each request to route it. ouicu keeps a log of requests to previews for a short time, as its privacy policy says. Send nothing through a tunnel that you wouldn't send through a hosting company.
Sharing more safely
- Share the dev server you mean to show, with its debugger off, and nothing else on that port. Django's own rule holds for tunnels as well: “Never deploy a site into production with DEBUG turned on.”
- Put a password on the link, or let in only the people you invite by email.
- Stop sharing when you're done. Press Ctrl-C and the address stops working. On Free, a share also ends after 2 hours.
- Watch the request lines in your terminal. A path you didn't expect is worth a look.
Why some tunnels show a warning page first
Phishing. A tunnel address on a well-known domain can carry a fake sign-in page from anyone's laptop, so tunnel services put a page in front of visitors. ngrok added one in June 2022, to stop “bad actors using ngrok to host phishing attacks for unsuspecting users”.
“To deter phishing attacks, ngrok shows an interstitial page in front of all HTML browser traffic on the free tier.”
Once a visitor clicks through, ngrok remembers it for 7 days. Microsoft's dev tunnels work alike: “When connecting to a web-forwarding url for the first time, users are presented with an interstitial anti-phishing page.” localtunnel's page, “Tunnel Website Ahead!”, asks visitors for a tunnel password: the public IP address of the person sharing, which they look up at loca.lt/mytunnelpassword.
ouicu shows its own notice on Free, on page loads only, once a week in each browser for each share:
A preview is a site still being worked on. It's running on someone's computer right now and reaches you through ouicu, which doesn't check what's on it.Fetches, API calls and webhooks go straight through. On Hobby and Pro, there is no notice, and the Report button stays on every plan. The edge also looks at pages as they pass for a password or card field under the name of a bank or a big brand. That blocks nothing: it flags the share for a person at ouicu to look at.
Questions
Is ngrok safe to use?
As a tunnel, yes: its agent connects out, and it serves only what you point it at. The risks are the ones on this page, true of any tunnel: what your port serves, and who has the link. Its free plan puts a warning page in front of visitors, which any paid plan removes.
Is localtunnel safe?
It works the same way, and its client is open source. Its latest release is 2.0.2, from September 2021. To let someone past its password page you give them your public IP address, and its options include no password or sign-in of your own.
Which is better, nginx or ngrok?
They do different jobs. nginx serves and routes traffic on a server that the internet can reach. ngrok, like ouicu, makes a computer that the internet can't reach answer at a public address. See the comparison above.
Can someone reach my other ports or files?
Not through ouicu: the tool connects only to the port you named, on your own computer. Whatever that port serves is reachable, including any file your dev server is willing to send.
Do I need to open a port on my router?
No. The connection goes out from your computer, so the router and firewall stay as they are.
Try it
Share the port your dev server runs on, and stop with Ctrl-C:
ouicu share 3000Add a password, so only the people you tell can open it (Hobby and Pro):
ouicu share 3000 --passwordOr let in only people you name, by email (Pro). The step-by-step is in how to share localhost with someone.
Sources
Prices, defaults and quotes about other products, and the day each was last checked at its source.
- Dev tunnels security, Microsoft Learn. Checked .
- nginx, nginx.org. Checked .
- Quickstart, Flask docs. Checked .
- Settings, Django docs. Checked .
- Fighting Abuse on the ngrok Platform, ngrok blog. Checked .
- Free plan limits, ngrok docs. Checked .
- Issue 663: bypass-tunnel-reminder Http Header not working, localtunnel on GitHub. Checked .
- Issue 648: IP password is not being accepted, localtunnel on GitHub. Checked .
- localtunnel, npm. Checked .