Demystifying SSH Port Forwarding: Local, Remote, and Dynamic Tunnels Explained

Learn local, remote, and dynamic SSH port forwarding with practical commands for databases, web apps, and SOCKS proxies.

Demystifying SSH Port Forwarding: Local, Remote, and Dynamic Tunnels Explained

SSH port forwarding lets you reach a network service through an encrypted SSH connection instead of exposing that service directly. You will usually use it when a database, dashboard, or development server is reachable from an SSH host but not from your laptop.

The three common forms are local forwarding, remote forwarding, and dynamic forwarding. They use similar syntax, but the listening port sits in a different place.

What SSH Port Forwarding Does

SSH port forwarding, also known as SSH tunneling, redirects traffic from one host and port to another host and port through your SSH session. The destination is resolved from the side that makes the final connection. For local forwarding, the SSH server connects to the target. For remote forwarding, your SSH client connects to the target.

This offers several advantages:

  • Security: Encrypts traffic that would otherwise be unencrypted between your client and the target service.
  • Access Control: Allows access to services running on private networks or behind firewalls that are not directly accessible from your current location.
  • Bridging Networks: Connects different network segments securely.

Local Port Forwarding (-L)

Local port forwarding is the most common type. It allows you to forward connections from a port on your local machine to a port on a remote machine, via an SSH server. Essentially, you make a service running on the remote network appear as if it's running on your local machine.

How it works:

  1. You start an SSH client on your local machine.
  2. You specify a local port (local_port) that SSH will listen on.
  3. Any connection made to local_port on your local machine is forwarded through the SSH connection to the remote_host and remote_port.

Syntax:

ssh -L [local_bind_address:]local_port:remote_host:remote_port [user@]ssh_server_host
  • local_bind_address: Optional address on your local machine to bind. If omitted, OpenSSH normally binds to loopback addresses unless your client configuration says otherwise.
  • local_port: The port on your local machine that SSH will listen on.
  • remote_host: The hostname or IP address of the target machine that the SSH server will connect to.
  • remote_port: The port on the remote_host that the traffic will be directed to.
  • user@ssh_server_host: Your username and the hostname/IP address of the SSH server you are connecting to.

Practical Use Case: Accessing a database server running on a remote server's private IP address.

Imagine a database server (e.g., PostgreSQL on 192.168.1.100:5432) that's only accessible from your company's internal network. You can use local port forwarding to access it from your laptop at home:

ssh -L 5433:192.168.1.100:5432 your_user@your_ssh_server.com
  • This command connects you to your_ssh_server.com.
  • It opens port 5433 on your local machine (localhost by default).
  • Any connection to localhost:5433 will be forwarded through your_ssh_server.com to 192.168.1.100:5432.

Now, you can configure your local database client to connect to localhost:5433, and the traffic will be securely tunneled to the remote database server.

Tip: Use ssh -N to create a tunnel without executing a remote command. This is useful for background tunnels.

ssh -N -L 5433:192.168.1.100:5432 your_user@your_ssh_server.com

Remote Port Forwarding (-R)

Remote port forwarding allows you to forward connections from a port on the remote SSH server to a port on your local machine or another machine accessible from your local machine. This is useful for making a service running on your local machine accessible to the remote server or its network.

How it works:

  1. You start an SSH client on your local machine.
  2. You specify a remote port (remote_port) on the SSH server that SSH will listen on.
  3. Any connection made to remote_port on the SSH server is forwarded through the SSH connection back to your local machine and then to a specified destination_host and destination_port.

Syntax:

ssh -R [remote_bind_address:]remote_port:destination_host:destination_port [user@]ssh_server_host
  • remote_bind_address: (Optional) The address on the SSH server to bind the listening port to. Defaults to localhost (meaning only the SSH server itself can connect to this port). Use 0.0.0.0 or * to allow other machines on the remote network to connect.
  • remote_port: The port on the SSH server that SSH will listen on.
  • destination_host: The hostname or IP address of the target machine that your SSH client will connect to (often localhost if the service is on your local machine).
  • destination_port: The port on the destination_host that the traffic will be directed to.
  • user@ssh_server_host: Your username and the hostname/IP address of the SSH server you are connecting to.

Practical Use Case: Exposing a local web server to a remote network.

Suppose you are developing a web application on your laptop and want to demonstrate it to a colleague who only has access to your company's internal network, and you have an SSH server (your_ssh_server.com) accessible from that network.

On your laptop, you would run:

ssh -R 8080:localhost:3000 your_user@your_ssh_server.com
  • This command connects you to your_ssh_server.com.
  • It tells your_ssh_server.com to listen on port 8080.
  • Any connection to your_ssh_server.com:8080 will be forwarded back through the SSH tunnel to your laptop (localhost) on port 3000 (where your web server is running).

Now, your colleague can access your web application by navigating to http://your_ssh_server.com:8080 in their browser. The traffic goes from their browser to the SSH server, through the tunnel to your laptop, and then to your web server.

Warning: In typical OpenSSH configurations, the remote_port is reachable only from the SSH server itself. To allow other machines on the remote network to access the forwarded port, explicitly set remote_bind_address to 0.0.0.0 or * and make sure the SSH server permits it with a GatewayPorts setting such as clientspecified or yes.

ssh -R 0.0.0.0:8080:localhost:3000 your_user@your_ssh_server.com

Dynamic Port Forwarding (-D)

Dynamic port forwarding creates a SOCKS proxy on your local machine. This is arguably the most flexible type, as it allows you to tunnel any application that supports SOCKS proxies through your SSH connection. Instead of forwarding a specific port, SSH listens on a local port and acts as a SOCKS proxy server.

How it works:

  1. You start an SSH client on your local machine.
  2. You specify a local port (local_port) that SSH will listen on as a SOCKS proxy.
  3. You configure your applications (web browser, etc.) to use localhost:local_port as their SOCKS proxy.
  4. When an application makes a request through this proxy, SSH forwards the traffic to the SSH server, which then makes the connection to the ultimate destination on behalf of your application.

Syntax:

ssh -D [local_bind_address:]local_port [user@]ssh_server_host
  • local_bind_address: (Optional) The address on your local machine to bind the listening SOCKS proxy port to. Defaults to localhost.
  • local_port: The port on your local machine that SSH will listen on as a SOCKS proxy.
  • user@ssh_server_host: Your username and the hostname/IP address of the SSH server you are connecting to.

Practical Use Case: Securely browsing the web from a public Wi-Fi.

When connected to an untrusted public Wi-Fi network, your traffic is vulnerable. You can use dynamic port forwarding to tunnel all your web browsing traffic through an encrypted SSH connection to a trusted server.

On your laptop, run:

ssh -D 1080 your_user@your_trusted_server.com
  • This command connects you to your_trusted_server.com.
  • It opens port 1080 on your local machine, acting as a SOCKS proxy.

Next, configure your web browser or other application to use a SOCKS proxy at localhost on port 1080. If the application has a SOCKS5 remote DNS option, use it when you do not want DNS lookups to happen on your local network.

Your browser traffic is sent to your SSH server, which then connects to the destination site. HTTPS still matters; the SSH tunnel protects the path to your SSH server, not every hop after it.

Tip: You can combine -D with -C for compression, which can be beneficial on slower network links.

ssh -C -D 1080 your_user@your_trusted_server.com

Advanced Considerations and Best Practices

  • SSH Server Configuration (sshd_config): Some forwarding features require server-side permission. Check AllowTcpForwarding, PermitOpen, PermitListen, and GatewayPorts before assuming a tunnel can listen or connect anywhere.
  • Firewalls: Remember that firewalls on either the client, server, or intermediate networks can block SSH connections or the ports used for forwarding. Ensure the necessary ports (usually 22 for SSH itself) are open.
  • Security: While port forwarding encrypts traffic, the security of the tunnel depends on the security of your SSH server. Use strong SSH keys, disable password authentication, and keep your SSH server updated.
  • Reliability: For scripts, add -o ExitOnForwardFailure=yes so SSH exits if it cannot create the requested forward. For long-running tunnels, consider autossh or a supervised service.

Takeaway

Use -L when your laptop needs to reach a private remote service. Use -R when a remote host needs to reach something near your laptop. Use -D when you need a SOCKS proxy through a trusted SSH server. Keep binds tight, check server policy, and treat every tunnel as a temporary network opening.