SSH Tunneling Masterclass: Local, Remote, and Dynamic Port Forwarding
Master all three types of SSH tunneling to securely access databases, bypass firewalls, and proxy traffic through encrypted connections.
SSH isn't just for remote shells. Its port forwarding capabilities create secure tunnels for any TCP traffic. Database connections, web services, API calls — all can flow through encrypted SSH tunnels, bypassing firewalls and protecting data in transit.
This guide covers all three types of SSH tunneling with practical examples you can use today.
Type 1: Local Port Forwarding (L)
Use case: Access a remote service as if it were running on your local machine.
How It Works
Local Machine SSH Server
localhost:8080 ──→ SSH Tunnel ──→ localhost:3306 (MySQL)
You connect to localhost:8080 on your machine, and the traffic is forwarded through the SSH tunnel to port 3306 on the remote server.
Basic Example: Remote MySQL
ssh -L 8080:localhost:3306 user@server.example.com
Now connect your MySQL client to localhost:8080:
mysql -h 127.0.0.1 -P 8080 -u root -p
The connection flows: Your MySQL Client → Local Port 8080 → SSH Tunnel → Server → MySQL on Port 3306
Advanced: Forward to Third-Party Host
ssh -L 8080:db.internal.company.com:3306 user@server.example.com
Traffic flows: Your Machine → SSH Tunnel → Server → db.internal.company.com:3306
The SSH server acts as a bridge to other hosts on its network.
Real-World Scenarios
Access internal dashboards:
# Forward Grafana dashboard
ssh -L 3000:monitoring.internal:3000 user@bastion.company.com
# Open http://localhost:3000 in browser
Connect to Redis:
ssh -L 6379:localhost:6379 user@redis-server.example.com
redis-cli -h 127.0.0.1 -p 6379
Test staging APIs:
ssh -L 8000:staging-api.internal:8000 user@staging-bastion.example.com
curl http://localhost:8000/api/health
FoxTerm Integration
FoxTerm makes local port forwarding visual:
- Connect to server via SSH
- Click "Port Forwarding" in toolbar
- Click "Add Local Forward"
- Enter local port (e.g., 8080)
- Enter remote host and port (e.g., localhost:3306)
- Click "Start"
FoxTerm manages the tunnel in the background. No terminal commands needed.
Type 2: Remote Port Forwarding (R)
Use case: Expose a local service to a remote server or the internet.
How It Works
Remote Server Your Machine
port 9090 ──→ SSH Tunnel ──→ localhost:3000 (dev server)
Anyone connecting to port 9090 on the remote server reaches your local port 3000.
Basic Example: Expose Dev Server
ssh -R 9090:localhost:3000 user@server.example.com
Now anyone can access your local dev server:
curl http://server.example.com:9090
Advanced: Gateway Ports
By default, remote forwards only listen on 127.0.0.1. To allow external access:
ssh -R 0.0.0.0:9090:localhost:3000 user@server.example.com
Server config must allow this:
# /etc/ssh/sshd_config
GatewayPorts yes
Real-World Scenarios
Demo local work to teammates:
ssh -R 8080:localhost:3000 user@demo-server.example.com
# Share: http://demo-server.example.com:8080
Bypass corporate firewall:
# At office, expose internal tool to home server
ssh -R 443:internal-tool.corp:443 user@home-server.example.com
# At home: https://home-server.example.com
Temporary webhook endpoint:
# Expose local webhook handler to Stripe/PayPal
ssh -R 8443:localhost:8080 user@public-server.example.com
# Configure webhook URL: https://public-server.example.com:8443/webhook
Security Warning
Remote port forwarding exposes your local services. Always:
- Use strong SSH authentication (keys, not passwords)
- Limit which interfaces the remote port binds to
- Close tunnels when done
- Consider using
ngrokorcloudflaredfor production exposure
Type 3: Dynamic Port Forwarding (D)
Use case: Create a SOCKS proxy to route arbitrary traffic through the SSH server.
How It Works
Your Apps → SOCKS Proxy (localhost:1080) → SSH Tunnel → SSH Server → Any Destination
Unlike local/remote forwarding (which forward specific ports), dynamic forwarding creates a general-purpose proxy.
Basic Example: SOCKS Proxy
ssh -D 1080 user@server.example.com
Configure your browser or system to use SOCKS proxy at localhost:1080. All traffic routes through the SSH server.
Browser Configuration
Firefox:
- Settings → Network Settings → Manual proxy configuration
- SOCKS Host:
127.0.0.1, Port:1080 - Select "SOCKS v5"
- Check "Proxy DNS when using SOCKS v5"
Chrome (command line):
chrome --proxy-server="socks5://127.0.0.1:1080"
System-wide (macOS):
networksetup -setsocksfirewallproxystate Wi-Fi on
networksetup -setsocksfirewallproxy Wi-Fi 127.0.0.1 1080
Real-World Scenarios
Access internal websites:
Browse internal company sites that are only accessible from the office network.
Bypass geo-restrictions:
ssh -D 1080 user@server-in-japan.example.com
# Now browse as if you're in Japan
Secure public WiFi:
ssh -D 1080 user@trusted-server.example.com
# All traffic encrypted through tunnel, safe from WiFi snooping
Debug production APIs:
ssh -D 1080 user@bastion.production.com
# Configure API client to use SOCKS proxy
# Access internal APIs as if you're on the production network
FoxTerm Integration
FoxTerm includes a built-in SOCKS proxy:
- Connect to server via SSH
- Click "Port Forwarding" → "Enable SOCKS Proxy"
- FoxTerm starts a SOCKS5 proxy on localhost:1080
- Configure apps to use the proxy
- Toggle on/off from the menu bar
No command-line setup required.
Combining Tunnel Types
Real-world scenarios often combine multiple tunnel types.
Example: Full Development Environment
# Local forward: Access remote database
ssh -L 5432:db.internal:5432 \
# Remote forward: Expose local dev server
-R 8080:localhost:3000 \
# Dynamic forward: SOCKS proxy for internal APIs
-D 1080 \
user@bastion.example.com
Now you have:
- PostgreSQL available at
localhost:5432 - Dev server accessible at
bastion.example.com:8080 - SOCKS proxy at
localhost:1080for internal API access
Jump Hosts (Bastion Servers)
Many organizations use bastion servers as gateways to internal networks.
Single Jump Host
ssh -J user@bastion.example.com user@internal-server
With port forwarding:
ssh -L 8080:db.internal:5432 -J user@bastion.example.com user@internal-server
Multiple Jump Hosts
ssh -J user@bastion1.example.com,user@bastion2.example.com user@internal-server
FoxTerm Configuration
FoxTerm supports jump hosts natively:
- Settings → SSH → Proxy/Jump Host
- Enter bastion server details
- FoxTerm automatically chains connections
SSH Config File
Simplify complex tunnel setups with ~/.ssh/config:
# ~/.ssh/config
# Bastion server
Host bastion
HostName bastion.example.com
User deploy
IdentityFile ~/.ssh/bastion_key
# Internal server via bastion
Host internal
HostName 10.0.1.100
User admin
ProxyJump bastion
# Local forward: database
LocalForward 5432 db.internal:5432
# Dynamic forward: SOCKS proxy
DynamicForward 1080
# Production web server
Host prod-web
HostName 10.0.2.50
User www
ProxyJump bastion
# Remote forward: expose local debug server
RemoteForward 9090 localhost:3000
Now connect with:
ssh internal
# Automatically sets up both port forwards
Troubleshooting
"Address already in use"
Another process is using the local port. Find and kill it:
lsof -i :8080
kill <PID>
Or use a different local port.
"Cannot bind to port"
Port < 1024 requires root. Use higher ports or run with sudo.
Tunnel Disconnects
SSH connections drop. Keep them alive:
ssh -o ServerAliveInterval=60 -o ServerAliveCountMax=3 user@server
Or in ~/.ssh/config:
Host *
ServerAliveInterval 60
ServerAliveCountMax 3
Debugging
Add -v flag to see tunnel establishment:
ssh -v -L 8080:localhost:3306 user@server
Look for: Local forwarding listening on 127.0.0.1 port 8080
Security Best Practices
1. Use Key-Based Authentication
Never rely on passwords for tunnel connections:
ssh-keygen -t ed25519 -f ~/.ssh/tunnel_key
ssh-copy-id -i ~/.ssh/tunnel_key user@server
2. Restrict Tunnel Permissions
Limit what users can do with tunnels:
# /etc/ssh/sshd_config
AllowTcpForwarding yes
GatewayPorts no
PermitOpen localhost:3306 localhost:5432
PermitOpen restricts which destinations are allowed.
3. Monitor Active Tunnels
Check what tunnels are active:
ss -tlnp | grep ssh
netstat -tlnp | grep sshd
4. Log Tunnel Usage
Enable logging:
# /etc/ssh/sshd_config
LogLevel VERBOSE
Logs show tunnel establishment in /var/log/auth.log.
5. Time-Limited Tunnels
For temporary access, use timeout:
timeout 3600 ssh -L 8080:localhost:3306 user@server
# Tunnel closes after 1 hour
Conclusion
SSH tunneling is a powerful tool for secure networking. Local forwarding brings remote services to your machine. Remote forwarding exposes local services to remote networks. Dynamic forwarding creates flexible SOCKS proxies. Combined with jump hosts and SSH config, you can navigate complex network topologies securely.
FoxTerm makes all of this visual and manageable, so you spend less time on commands and more time on your actual work.
FoxTerm Pro includes visual port forwarding management. Download at foxterm.app.