SSH Tunneling: A Practical Guide to Port Forwarding
SSH tunneling lets you reach services that aren’t open to the internet, share a local app with a remote server, or browse through a secure proxy, all over an encrypted SSH connection. Here’s how local, remote and dynamic port forwarding work, with copy-and-paste commands.
Local forwarding (-L)Remote forwarding (-R)Dynamic SOCKS (-D)PuTTY & OpenSSH
The basics
What is SSH tunneling?
An SSH tunnel carries other network traffic inside an encrypted SSH connection. You connect to a server you can already SSH into, and that server passes traffic on to another port or machine for you.
It’s the go-to way to reach a database that only listens on localhost, open an internal admin page, get past a restrictive firewall, or encrypt traffic for apps that don’t support encryption themselves. The only requirement is an SSH login on the server. New to SSH? Start with how to SSH into a server.
A bit of CodeBrowser history
In the mid-2000s, codebrowser.org was the home of zaTunnel, an SSH tunneling app for Windows Mobile phones and Pocket PCs, alongside the zaTelnet SSH client and zaSFTP. The phones are long gone, but SSH tunnels are as useful as ever.
Three types
Local, remote and dynamic port forwarding
Local (-L)
Bring a remote service to your computer. A port on your machine forwards through the SSH server to a service on the other side. Use it to reach a remote database or admin panel.
Remote (-R)
Send a local service out to the server. A port on the SSH server forwards back through the tunnel to your machine. Use it to share a local dev site or reach a computer behind NAT.
Dynamic (-D)
Turn the SSH server into a SOCKS proxy. Apps on your computer send any traffic through the tunnel. Use it to browse securely on public Wi-Fi or reach many internal sites.
Local port forwarding
Local port forwarding (ssh -L)
The pattern is -L local_port:destination_host:destination_port. The destination is resolved from the SSH server, so localhost means the server itself.
Your computerlocalhost:5433SSH serverencrypted tunnelDatabaselocalhost:5432
Reach a remote PostgreSQL database
ssh -N -L 5433:localhost:5432 user@server
Open an internal web app on another host
ssh -N -L 8080:intranet.local:80 user@server
With the first tunnel running, connect your database tool to localhost port 5433 and you’re talking to PostgreSQL on the server, even though port 5432 is never exposed to the internet. With the second, open http://localhost:8080 in your browser.
Remote port forwarding
Remote port forwarding (ssh -R)
The pattern is -R remote_port:destination_host:destination_port. This time the port opens on the SSH server, and the destination is resolved from your computer.
SSH serverlocalhost:9000Encrypted tunnelback to youYour computerlocalhost:3000
Share a local dev server with the remote machine
ssh -N -R 9000:localhost:3000 user@server
Reach a home computer behind NAT from your VPS
ssh -N -R 2222:localhost:22 user@vps
By default the remote port only listens on the server’s loopback address, so only users on that server can reach it. To expose it on the server’s public IP, the server needs GatewayPorts yes (or clientspecified) in /etc/ssh/sshd_config. Only do this if you understand who can reach that port.
Dynamic port forwarding
Dynamic port forwarding: a SOCKS proxy (ssh -D)
Instead of forwarding one port to one destination, -D starts a SOCKS5 proxy on your computer. Any app you point at it sends its traffic through the SSH server, which connects to whatever site or host the app asks for.
Browser or appSOCKS5 localhost:1080SSH serverencrypted tunnelAny websiteor internal host
Start a SOCKS5 proxy on port 1080
ssh -N -D 1080 user@server
Test it with curl
curl --socks5-hostname localhost:1080 https://ifconfig.me
In Firefox, open Settings → Network Settings, choose Manual proxy configuration, set SOCKS Host to localhost and port 1080, select SOCKS v5 and tick Proxy DNS when using SOCKS v5 so your DNS lookups go through the tunnel too.
Tips
Useful SSH tunnel options
Run in the background (-f) with no shell (-N)
ssh -f -N -L 5433:localhost:5432 user@server
Keep idle tunnels from dropping
ssh -N -o ServerAliveInterval=60 -o ServerAliveCountMax=3 -L 5433:localhost:5432 user@server
Reconnect automatically with autossh
autossh -M 0 -N -o ServerAliveInterval=30 -L 5433:localhost:5432 user@server
Tunnel through a jump host (-J)
ssh -N -J user@bastion -L 5433:localhost:5432 user@db-server
Use the same tunnel every day? Save it in ~/.ssh/config (our SSH Config Generator can build it), then start it with ssh -N db-tunnel:
Host db-tunnel
HostName server.example.com
User deploy
LocalForward 5433 localhost:5432
ServerAliveInterval 60
Troubleshooting
Common SSH tunnel errors and fixes
| Error | What it means | Fix |
|---|---|---|
bind: Address already in use | Something on your computer already uses that local port. | Pick a different local port, or stop the other program or old tunnel. |
channel 2: open failed: connect failed: Connection refused | The tunnel works, but nothing is listening at the destination. | Check the service is running and that the host and port are correct from the server’s point of view. |
Warning: remote port forwarding failed for listen port | The SSH server couldn’t open the remote port. | Choose a free port above 1024, or ask the admin whether AllowTcpForwarding is disabled. |
Privileged ports can only be forwarded by root | Ports below 1024 need admin rights. | Use a port above 1024, like 8080 instead of 80. |
| Tunnel drops after a few minutes | An idle connection was closed by a firewall or router. | Add ServerAliveInterval 60, or use autossh. |
FAQ
SSH tunneling questions, answered
Is SSH tunneling the same as a VPN?
Not quite. A VPN usually routes all of your device’s traffic through another network. An SSH tunnel forwards specific ports, or acts as a SOCKS proxy for apps you configure. It’s lighter and needs only an SSH login, but it isn’t a full VPN.
Is SSH tunneling secure?
Traffic inside the tunnel is encrypted between your computer and the SSH server. Traffic from the SSH server to the final destination is not encrypted by SSH, so for the best protection keep the destination on the server itself or on a trusted private network.
What does the -N option do?
It tells SSH not to run a remote command or open a shell. The connection only carries your tunnel, which is what you want for a dedicated port forward.
How do I stop an SSH tunnel?
Press Ctrl+C in the terminal running it. If you started it in the background with -f, find the process with ps aux | grep ssh and end it with kill followed by the process ID.
Can I forward more than one port at once?
Yes. Add as many -L, -R or -D options as you need to a single ssh command, or list several LocalForward lines for one host in your SSH config file.
Does SSH tunneling work on Windows?
Yes. The OpenSSH client built into Windows 10 and 11 supports the same -L, -R and -D options in PowerShell, and PuTTY and MobaXterm offer the same features through their settings.
Set up key-based login for your tunnels
Tunnels that run in the background or reconnect on their own work best with an SSH key instead of a password. Create one in seconds.