Practical Guide to SSH Tunneling and Port Forwarding
Secure Shell (SSH) is widely recognized for enabling secure remote server access, but its advanced feature — SSH tunneling (also called port forwarding) — is indispensable for developers and system administrators needing to securely route network traffic through encrypted tunnels. This guide is created for developers with 1-5 years of experience and delivers practical, runnable examples, comprehensive explanations, and side-by-side comparisons of SSH tunneling techniques.
Table of Contents
- Introduction to SSH Tunneling
- Local Port Forwarding
- Remote Port Forwarding
- Dynamic Port Forwarding (SOCKS Proxy)
- Comparison of Port Forwarding Types
- Best Practices and Pitfalls
Introduction to SSH Tunneling
SSH tunneling, also referred to as port forwarding, establishes encrypted pathways between your local computer and a remote host, allowing TCP traffic to be routed securely. This mechanism enables you to:
- Connect safely to remote services over untrusted or public networks
- Work around firewall rules and other network restrictions
- Ensure that sensitive data sent between client and server cannot be intercepted in plaintext
- Provide secure, temporary access to local resources from remote systems
Port forwarding comes in three main varieties:
- Local Port Forwarding: Redirects a port from your local computer to a remote service accessible via the SSH server.
- Remote Port Forwarding: Makes a local service on your computer available on a port of the remote SSH server.
- Dynamic Port Forwarding: Sets up a SOCKS proxy on your machine, dynamically routing outgoing connections through the SSH server.
Each method serves different use cases and is suitable for various network architectures. Let's explore them in detail.
Local Port Forwarding
# Example: Forward local port 5433 to remote database port 5432
ssh -L 5433:localhost:5432 [email protected]
# After this, you can connect locally to the remote DB:
psql -h localhost -p 5433 -U dbuser dbname
# Expected output when connecting:
# psql (13.3)
# Type "help" for help.
# dbname=#
What it does: This command maps your local port 5433 to port 5432 on remote-server.com via an encrypted SSH tunnel. After running it, you can use tools like psql or any database client to connect to the remote PostgreSQL instance as if it were running locally on 5433.
Why it matters: Local port forwarding allows you to interact with internal or protected services on a remote host without exposing them to the public internet. For example, connecting securely to a remote database for development, troubleshooting, or data migration without changing firewall rules.
Suppose your team has a staging PostgreSQL database that's only accessible from within your company's VPN or private network. By establishing a tunnel as shown above, you can work with the database from anywhere, provided you have SSH access.
Common edge cases & pitfalls
- Port conflicts: Your chosen local port (5433) must not be in use by another application. Use
sudo lsof -i :5433ornetstat -tuln | grep 5433to verify availability. - Firewall on SSH server: The SSH host must be able to reach the service you're forwarding to (such as
localhost:5432). If the database is listening on another interface or port, adjust your command accordingly. - Server config: The SSH server's configuration file (typically
/etc/ssh/sshd_config) needs to haveAllowTcpForwarding yesenabled. Otherwise, the tunnel setup will fail.
For example, if you run the tunnel command and get a "Connection refused" error when attempting to connect to the forwarded port, check the above issues first.
Now that we've covered how to securely access remote services locally, let's look at how to make your own local services accessible from a remote system.
Remote Port Forwarding
# Example: Forward remote port 9000 to local port 8080
ssh -R 9000:localhost:8080 [email protected]
# Now someone on the remote server can access your local web server on port 9000:
curl http://localhost:9000
# Expected output:
# (HTML response from your local web server)
What it does: This command causes connections to port 9000 on the remote SSH host to be forwarded back through the secure tunnel to port 8080 on your local machine. This is useful for sharing local development servers, APIs, or other services with remote colleagues or servers without opening ports in your firewall.
Why it matters: Remote port forwarding is ideal when you need to give temporary, secure access to a service running on your workstation or local environment. For instance, a developer can let a QA engineer or a remote server access a new web application running at localhost:8080 without deploying it or exposing it on the internet.
Imagine you're developing a web app on your laptop and want to show it to a teammate on a remote server. Using remote forwarding, they can access your app by visiting localhost:9000 on their machine after you establish the tunnel.
Edge cases & production pitfalls
- GatewayPorts setting: By default, the forwarded port may only be accessible from the remote server itself. To make it available to other hosts, set
GatewayPorts yesin/etc/ssh/sshd_configand restart the SSH service. - Security risks: Making your local services accessible remotely increases their exposure. Limit remote forwarding to trusted users, and monitor usage to detect suspicious activity.
- Firewall on remote server: The firewall on the SSH host must permit incoming connections to the forwarded port (e.g., 9000). Otherwise, remote clients cannot connect.
If, after tunneling, your colleague cannot connect to localhost:9000 from their machine, check both SSH server settings and firewall rules.
While local and remote port forwarding are suitable for forwarding fixed ports, sometimes you may want to route arbitrary client connections through your SSH server. This is where dynamic port forwarding comes in.
Dynamic Port Forwarding (SOCKS Proxy)
# Example: Start a SOCKS5 proxy on local port 1080
ssh -D 1080 [email protected]
# Configure your browser or system proxy to use SOCKS5 at localhost:1080
# Example using curl with SOCKS5 proxy:
curl --socks5 localhost:1080 https://example.com
# Expected output:
# (HTML content of example.com)
What it does: This command sets up a SOCKS5 proxy server on your local machine (port 1080). Any application configured to use this proxy can send its network traffic through the SSH tunnel, which is then relayed by the SSH server to the final destination. SOCKS5 is a protocol that routes traffic at the TCP level, allowing for flexible, on-demand forwarding.
Why it matters: Dynamic forwarding is especially valuable when you need to securely browse the web, access multiple services, or bypass network restrictions from a single SSH connection. For example, if you're on public Wi-Fi and wish to encrypt your browser traffic or appear to browse from your remote server's location, using a SOCKS proxy achieves this.
Suppose you're traveling and connected to a hotel Wi-Fi network. By starting an SSH dynamic tunnel and setting your browser to use localhost:1080 as a SOCKS5 proxy, all your browser requests are securely routed through your home or office server, protecting your privacy and potentially bypassing local restrictions.
Practical considerations
- Proxy configuration: You need to update your browser or application's network settings to use the SOCKS proxy (localhost:1080). For example, in Firefox, you would set the manual proxy configuration to SOCKS5 at 127.0.0.1:1080.
- Performance: Network speed and latency may be affected by the SSH server's location and the quality of the connection. For large downloads or latency-sensitive applications, you may notice slower performance.
- Not a VPN: While a SOCKS proxy forwards TCP traffic, it does not tunnel UDP packets or system-wide network traffic like a VPN would. Only applications explicitly configured to use the proxy are affected.
If you notice that only some applications are being routed through the tunnel, double-check that each is configured to use the SOCKS5 proxy.
Having examined the three main types of port forwarding, it's helpful to compare their characteristics side by side.
Comparison of SSH Port Forwarding Types
| Type | Direction | Use Case | Command Flag | Common Pitfalls |
|---|---|---|---|---|
| Local Port Forwarding | Local → Remote | Access remote service as if local (e.g., remote DB) | -L | Local port conflicts, SSH server access to target service |
| Remote Port Forwarding | Remote → Local | Expose local service to remote server | -R | GatewayPorts config, remote firewall, security risks |
| Dynamic Port Forwarding | Local SOCKS proxy → Various | Secure browsing, proxy multiple destinations | -D | Requires app proxy config, limited to TCP |
This table summarizes the main attributes of each forwarding type for quick reference. When choosing a method, consider whether you need to access a single service, expose a local resource, or proxy arbitrary destinations.
Best Practices and Common Pitfalls
- Use SSH keys over passwords: For robust authentication and to help prevent brute force attacks, always use SSH key pairs instead of passwords for login.
- Limit forwarded ports: Only set up tunnels for the specific ports you need, reducing your exposure to potential attacks.
- Monitor active tunnels: Utilize tools such as
netstatorssto review which ports are currently forwarded and detect unauthorized activity. - Configure SSH server securely: Carefully control
AllowTcpForwardingand usePermitOpento restrict which hosts and ports can be forwarded. - Avoid exposing sensitive services publicly: Never make SSH (port 22) or database ports accessible on the public internet without strict network controls and access lists.
- Check port availability: Confirm that your chosen ports are free locally and remotely before initiating tunnels to avoid conflicts and failures.
- Use persistent tunnels for production: For critical or long-running tunnels, tools such as
autosshcan automatically restart the connection if it drops unexpectedly.
By mastering these tunneling techniques with SSH, you can securely connect to remote resources, safely share local services, and ensure that your data remains protected in transit — all with minimal configuration and effort.
For more detailed explanations and tutorials, see the DigitalOcean SSH Port Forwarding Tutorial.

