How to Use SSH Tunnels and Port Forwarding (Reach Private Services Safely)

Several tutorials bound admin pages to 127.0.0.1 so they can't be reached from the network. The way you then reach them is an SSH tunnel: your SSH connection carries a port from the server to your laptop, encrypted, with nothing opened anywhere. Once you know the three flags, you'll stop exposing things "just for a minute".
Quick reference
| Want to | Command (on your laptop) |
|---|---|
| Server's localhost:8384 on my localhost:8384 | ssh -L 8384:127.0.0.1:8384 john@server |
| A device behind the server on my laptop | ssh -L 8443:192.168.1.1:443 john@server |
| Browse the web as if from home | ssh -D 1080 john@server then set browser SOCKS proxy to localhost:1080 |
| Expose my laptop's port on the server | ssh -R 9000:127.0.0.1:3000 john@server |
| Tunnel only, no shell | add -N |
| Keep it alive | autossh or a systemd service |
1. Local forwarding (-L): bring a server port here
ssh -L 8384:127.0.0.1:8384 john@192.168.1.50
Read it as: listen on my port 8384 and send it to 127.0.0.1:8384 as seen from the server. Now http://localhost:8384 on your laptop is Syncthing's admin page. The same works for Portainer (9443), wg-easy (51821), Nginx Proxy Manager (81), a database (3306) — anything bound to localhost on the server.
Add -N if you only want the tunnel and no shell prompt, and -f to put it in the background:
ssh -fN -L 9443:127.0.0.1:9443 john@192.168.1.50
2. Reach other devices at home through the server
The destination doesn't have to be the server itself:
ssh -L 8443:192.168.1.1:443 john@192.168.1.50
https://localhost:8443 is now your router's admin page, from anywhere you can SSH to the server. Printer web pages, a NAS, a camera — same trick.
3. Dynamic forwarding (-D): a SOCKS proxy
ssh -D 1080 john@192.168.1.50
Then in Firefox: Settings → Network → Manual proxy → SOCKS Host localhost, port 1080, SOCKS v5, tick Proxy DNS. Every page now loads through your home connection: café Wi-Fi can't see what you're doing, and sites see your home IP. A poor man's VPN with nothing to install.
4. Remote forwarding (-R): expose something outward
ssh -R 9000:127.0.0.1:3000 john@192.168.1.50
Your laptop's port 3000 (a dev server, say) is now reachable on the server's port 9000. Combined with a public server, it's how you show a colleague something running on your machine. For the server to expose it beyond localhost, set GatewayPorts yes in its sshd_config.
5. Make it permanent in the config file
In ~/.ssh/config on your laptop:
Host home
HostName 192.168.1.50
User john
LocalForward 8384 127.0.0.1:8384
LocalForward 9443 127.0.0.1:9443
LocalForward 8443 192.168.1.1:443
Every ssh home now brings all three with it.
6. Keep a tunnel up
For a tunnel that should always exist (a Pi reporting home, say), use autossh, which reconnects when the link drops:
sudo apt install autossh -y
autossh -M 0 -fN -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" -L 8384:127.0.0.1:8384 john@192.168.1.50
Or wrap that in a systemd service (tutorial 13) with Restart=always.
Jump hosts
To reach a machine that's only accessible from the server:
ssh -J john@192.168.1.50 pi@192.168.1.60
-J hops through the first host. In the config file it's ProxyJump home.
Troubleshooting
"bind: Address already in use". Something on your laptop already uses that local port. Pick another number on the left side.
"channel: open failed: connect failed". The server couldn't reach the destination — the service isn't running, or the IP/port on the right side is wrong. Test on the server with curl http://127.0.0.1:8384.
Tunnel drops after a few minutes idle. Add ServerAliveInterval 30 to your SSH config.
Windows. Built-in ssh supports all of this. PuTTY has the same options under Connection → SSH → Tunnels.
Wrapping up
-L brings a port to you, -D turns SSH into a proxy, -R pushes a port out, and the config file makes any of them a habit. Keep admin pages on localhost and tunnel to them — it's the safest door you have, and it's already open.
