SSH Tunneling & Port Forwarding: The Complete Guide
Master SSH tunneling with practical examples — local port forwarding, remote port forwarding, dynamic SOCKS proxy, and jump hosts. Access any service securely without a VPN.
Why SSH Tunneling?
You have a database, web panel, or internal service that only listens on localhost or a private network. You need to access it from your laptop — but opening ports to the internet is risky.
SSH tunneling creates an encrypted channel through an existing SSH connection, forwarding traffic between your machine and the remote service. No VPN needed, no firewall changes, no extra software.
Common use cases:
- Access a remote database (MySQL, PostgreSQL, Redis) without exposing it
- Reach an admin panel that only listens on
127.0.0.1 - Browse the internet through a SOCKS proxy on a remote server
- Expose a local development server to the internet
- Chain through jump hosts to reach isolated networks
The Three Types of SSH Tunneling
| Type | Flag | Direction | Use Case |
|------|------|-----------|----------|
| Local forwarding | -L | Remote service to your machine | Access remote DB locally |
| Remote forwarding | -R | Your machine to remote server | Expose local app to internet |
| Dynamic forwarding | -D | SOCKS proxy through remote | Browse via remote server |
Local Port Forwarding (-L)
"Bring a remote service to my machine."
Syntax
Example 1: Access Remote MySQL
MySQL on db-server only listens on 127.0.0.1:3306. You can't connect directly. But you have SSH access to db-server:
Now connect to MySQL as if it were local:
Example 2: Access a Web Panel
Grafana runs on monitoring-server:3000 but is firewalled:
Open http://localhost:8080 in your browser — you're viewing Grafana through the SSH tunnel.
Example 3: Access a Service on Another Host
Your SSH server can reach 192.168.1.50:5432 (PostgreSQL on a different machine in the same network):
The tunnel goes: Your laptop → SSH to ssh-server → forwards to 192.168.1.50:5432.
Run in Background
Add -f -N to run the tunnel in the background without opening a shell:
-f— run in background after authentication-N— don't execute any remote command
Remote Port Forwarding (-R)
"Expose my local machine to a remote server."
Syntax
Example 1: Share Local Dev Server
You're developing a web app on localhost:3000 and want a colleague to see it. You have a public server at public-server.com:
Now http://public-server.com:8080 shows your local app.
Example 2: Access Home Machine from Work
Your home PC runs an SSH server. From home, create a reverse tunnel to a cloud server:
From work, connect to your home PC via the cloud server:
Enable Remote Binding
By default, remote forwarded ports only bind to 127.0.0.1 on the server. To make them accessible from other machines, enable GatewayPorts on the SSH server:
Or bind to all interfaces in the command:
Dynamic Port Forwarding (-D)
"Use the remote server as a SOCKS proxy."
Syntax
Example: Browse Through a Remote Server
Configure your browser to use a SOCKS5 proxy at 127.0.0.1:1080. All your web traffic now routes through remote-server.com.
Use with curl
Firefox SOCKS Proxy Setup
- Open Firefox Settings
- Search for "proxy"
- Select Manual proxy configuration
- Set SOCKS Host:
127.0.0.1, Port:1080 - Select SOCKS v5
- Check Proxy DNS when using SOCKS v5 (important for privacy)
System-Wide Proxy (Linux)
Jump Hosts / ProxyJump (-J)
"SSH through an intermediate server to reach an isolated target."
Your target server (10.0.0.50) is on a private network. You can only reach it through a bastion host (bastion.example.com).
Old Way (ProxyCommand)
Modern Way (ProxyJump)
Multiple Jumps
Chain through multiple hosts:
Combine with Tunneling
Forward a port through a jump host:
This creates: Your laptop → bastion → 10.0.0.50 → forwards 5432.
SSH Config File
Typing long SSH commands is tedious. Save them in ~/.ssh/config:
# Jump host / bastion
Host bastion
HostName bastion.example.com
User admin
IdentityFile ~/.ssh/id_ed25519
# Database server (through bastion)
Host db-prod
HostName 10.0.0.50
User admin
ProxyJump bastion
LocalForward 5432 127.0.0.1:5432
# Monitoring server
Host monitoring
HostName monitoring.example.com
User admin
LocalForward 3000 127.0.0.1:3000
LocalForward 9090 127.0.0.1:9090
# SOCKS proxy
Host socks-proxy
HostName remote-server.com
User admin
DynamicForward 1080
# Common settings for all hosts
Host *
ServerAliveInterval 60
ServerAliveCountMax 3
AddKeysToAgent yes
Now access everything with simple commands:
SSH Tunneling on Windows
Using OpenSSH (Built into Windows 10/11)
Open PowerShell or Command Prompt — the syntax is identical to Linux:
Using PuTTY
- Open PuTTY, enter the SSH server hostname
- Go to Connection → SSH → Tunnels
- For local forwarding:
- Source port:
8080 - Destination:
127.0.0.1:3000 - Select Local, click Add
- Source port:
- For dynamic forwarding:
- Source port:
1080 - Select Dynamic, click Add
- Source port:
- Go back to Session, save, and connect
Using VS Code
VS Code has built-in port forwarding. When connected to a remote server via SSH:
- Open the Ports panel (bottom bar)
- Click Forward a Port
- Enter the remote port number
- VS Code automatically creates the tunnel
Persistent Tunnels with autossh
SSH tunnels break when the connection drops. Use autossh to auto-reconnect:
-M 0— disable autossh's built-in monitoring (use SSH keepalives instead)-o ServerAliveInterval=30— send keepalive every 30 seconds-o ServerAliveCountMax=3— disconnect after 3 missed keepalives
Run as a systemd Service
Create /etc/systemd/system/ssh-tunnel-db.service:
Enable it:
The tunnel now starts on boot and auto-reconnects on failure.
Security Best Practices
1. Use Key-Based Authentication
2. Restrict Tunnel-Only Users
Create a user that can only tunnel, not get a shell:
In /etc/ssh/sshd_config:
Match User tunnel-user
AllowTcpForwarding yes
X11Forwarding no
PermitTTY no
ForceCommand /bin/false
3. Limit Which Ports Can Be Forwarded
Match User tunnel-user
PermitOpen 127.0.0.1:3306 127.0.0.1:5432
AllowTcpForwarding local
This user can only forward to MySQL and PostgreSQL — nothing else.
4. Use Fail2ban
Default config bans IPs after 5 failed SSH attempts for 10 minutes.
5. Change the SSH Port
Not real security, but eliminates 99% of automated bot traffic.
Quick Reference
Local Forwarding
Remote Forwarding
Dynamic Forwarding
Jump Host
Manage Tunnels
SSH Tunneling vs VPN
| Feature | SSH Tunnel | VPN | |---------|-----------|-----| | Setup time | Seconds (one command) | Minutes to hours | | Scope | Specific ports/services | Entire network | | Encryption | Yes (SSH) | Yes (IPsec/WireGuard) | | Performance | Good for light traffic | Better for heavy traffic | | Requires client software | No (SSH is everywhere) | Yes (VPN client) | | Persistent connections | With autossh | Built-in | | Best for | Quick access to specific services | Full network connectivity |
Use SSH tunneling when you need quick, temporary access to specific services. Use a VPN when you need persistent, full network access for multiple users.