You’ve probably used ngrok to expose your local server. It feels like magic. One command, a public URL, and suddenly your localhost is accessible from anywhere. But here’s the secret that most developers never learn: ngrok is literally just SSH remote port forwarding with a reverse proxy in front. And once you understand that, you can build the same thing yourself — with more control, better security, and zero recurring costs.
I spent years treating ngrok as a black box. Type ngrok http 3000, get a URL, show it to a client. It worked. But every time I used it, something nagged at me: what happens to my data? The answer: it goes through ngrok’s servers. And while they’re probably fine, I don’t want to rely on a third party for something as fundamental as network access.
So I decided to tear apart the magic. I bought a $5 VPS, installed SSH, and started experimenting. It took me about 30 minutes to replicate the core ngrok functionality. Here’s what I learned.
What ngrok Is Actually Doing
At its heart, ngrok does two things: it creates a remote port forwarding tunnel, and it puts a reverse proxy (like Nginx or Caddy) in front of that tunnel to handle HTTPS, subdomains, and routing. That’s it. The remote port forwarding is a standard SSH feature that’s been around for decades. SSH has been able to do this since the 1990s. You just didn’t know.
When you run ssh -R 8080:localhost:3000 user@your-server, you’re telling your server: any traffic that hits port 8080 on the server, forward it through the SSH connection to my local machine on port 3000. That’s exactly what ngrok does — the only difference is that ngrok adds a nice dashboard, custom domains, and HTTPS out of the box.
The GatewayPorts Gotcha
Here’s where most people get stuck. If you run the command above, you’ll find that the tunnel only listens on localhost on the server. That means you can curl it from the server itself, but not from the internet. The fix is a single SSH config option: GatewayPorts yes on the server. This tells SSH to bind the forwarded port to all interfaces (0.0.0.0), making it accessible from the outside. One line. One config change. That’s the difference between a working tunnel and a frustrating hour of debugging.
Without it, you’re effectively building a tunnel that only you can see — like a secret passage that leads to a locked door. The GatewayPorts setting unlocks that door.
Why You Should Build Your Own
I’m not saying ngrok is bad. It’s great for demos, quick tests, and situations where you don’t want to manage infrastructure. But if you’re relying on it for ongoing development, staging environments, or self-hosted services, you’re paying for something you can trivially replicate. ngrok costs $10/month for basic features. A $5 VPS from Linode or DigitalOcean can run dozens of tunnels, with no per-tunnel limits, no data caps, and no privacy concerns.
Plus, you get full control. Want to add custom domains? Point your DNS. Want HTTPS? Install Caddy (it auto-requests certificates). Want to log every connection? SSH already logs everything. The learning curve is minimal — you already know SSH, you just didn’t know it could do this.
The Real-World Setup
Here’s the exact setup I use. On my VPS (Debian, $5/month), I enable GatewayPorts in /etc/ssh/sshd_config. I run a Caddy reverse proxy that proxies incoming requests to localhost:8080 (or whatever port I choose). Then, from my local machine, I run: ssh -R 8080:localhost:3000 -N user@my-server. The -N flag tells SSH not to open a shell — just forward the port. That’s it. I now have a persistent, encrypted tunnel that my friends can access via https://my-custom-domain.com.
If you need multiple tunnels, you can run multiple SSH commands with different ports. Or use a wrapper script. Or use autossh to keep the connection alive. The possibilities are endless because you’re not limited by a vendor’s API.
But What About Security?
Here’s the contrarian truth: your own SSH tunnel is often more secure than ngrok. With ngrok, you’re trusting their infrastructure to handle your traffic. With your own VPS, you control the firewall, the SSH keys, the logging, and the reverse proxy. You can set up AllowUsers to restrict who can SSH in. You can use Match Address to limit which IPs can access the forwarded port. You can even use ssh -L (local forwarding) to create a reverse tunnel for inbound access — though that’s a separate topic.
The point is: the moment you stop treating ngrok as magic and start treating it as SSH + reverse proxy, you become a better engineer. You understand the stack. You can debug it. You can customize it. And you can do it all without paying a middleman.
I’m not saying you should never use ngrok. But next time you reach for it, ask yourself: *do I really need this, or can I just SSH?* The answer will surprise you.
FAQ
Q: Is it safe to use SSH tunnels for production traffic?
A: Yes, if you configure it properly. Use key-based authentication, disable password login, restrict allowed users, and consider using autossh to maintain the tunnel. The encryption is the same as any SSH session — strong enough for most use cases.
Q: What's the practical implication for a developer who uses ngrok daily?
A: You can save money and gain flexibility. Instead of paying ngrok for a single tunnel, spin up a $5 VPS and run multiple tunnels. You'll also learn how the underlying technology works, which makes you more capable when something breaks.
Q: But ngrok offers HTTPS and custom subdomains easily — isn't that worth the price?
A: HTTPS is trivial with tools like Caddy or Let's Encrypt. Custom subdomains are just DNS records. The convenience ngrok provides is real, but it's a thin wrapper over standard tools. If you need to scale or have sensitive data, the DIY approach is often superior.