This page looks best with JavaScript enabled

SSH Port Forwarding: Local and Remote Tunnels Explained

 ·  โ˜• 4 min read · ๐Ÿ‘€... views

SSH port forwarding (also called SSH tunneling) lets you securely route network traffic through an encrypted SSH connection. There are two directions: local forwarding (-L) to access remote services, and remote forwarding (-R) to expose local services to a remote machine.

Figure 1: SSH port forwarding connects two machines through an encrypted tunnel

Figure 1: SSH port forwarding connects two machines through an encrypted tunnel

Remote port forwarding (-R)

Remote port forwarding forwards a port on the remote machine to a specified address and port on the local machine.

Syntax:

1
ssh -R [remote_bind_address:]remote_port:local_host:local_port user@remote_host

Example: expose a local web service (localhost:3000) on the remote server’s port 8080.

1
ssh -R 8080:localhost:3000 user@remote_server

After this, anyone who connects to remote_server:8080 will actually reach your local localhost:3000.

Figure 2: Remote port forwarding: traffic to the remote server’s port 8080 is tunneled back to your local machine on port 3000

Figure 2: Remote port forwarding: traffic to the remote server’s port 8080 is tunneled back to your local machine on port 3000

Common use cases

  • expose a local development server to a remote server or colleague
  • NAT traversal: let a public server access services behind your NAT/firewall
  • remote debugging: let a remote server connect to a local database

Key parameters

  • -R specifies remote port forwarding
  • GatewayPorts yes (in the remote server’s sshd_config): allows other hosts to access the forwarded port; by default it binds only to 127.0.0.1
  • -N do not execute a remote command, only forward ports
  • -f run in the background

Example: allow all remote hosts to access the forwarded port:

1
ssh -R 0.0.0.0:8080:localhost:3000 user@remote_server

Local port forwarding (-L)

Local port forwarding forwards a port on the local machine to an address reachable from the SSH server.

Syntax:

1
ssh -L [local_bind_address:]local_port:remote_host:remote_port user@ssh_server

Example: access a remote database through a jump host.

1
ssh -L 5432:db-server.internal:5432 user@jump_host

After connecting, localhost:5432 on your local machine is equivalent to db-server.internal:5432 from the jump host’s perspective.

Figure 3: Local port forwarding: you connect to a local port, and the traffic is forwarded through the SSH tunnel to the remote service

Figure 3: Local port forwarding: you connect to a local port, and the traffic is forwarded through the SSH tunnel to the remote service

Common use cases

  • securely access internal services (databases, redis, admin panels)
  • reach services behind a firewall through a bastion host
  • encrypt traffic between your local machine and a remote service

Key parameters

  • -L specifies local port forwarding
  • -N do not execute a remote command, only forward ports
  • -f run in the background

Example: bind to localhost only (the default behavior):

1
ssh -L 8888:api.internal:443 user@gateway

Comparison

FeatureLocal (-L)Remote (-R)
DirectionLocal port โ†’ Remote targetRemote port โ†’ Local target
Typical useaccess internal services via jump hostexpose a local service to the remote side
Bind address127.0.0.1 by default127.0.0.1 by default (remote side)
Triggerwhen you connect to the local portwhen someone connects to the remote port

Persistent configuration via ~/.ssh/config

You can persist port forwarding rules in ~/.ssh/config so you don’t need to specify flags every time.

Command-line to config mapping

CLI flagConfig keywordValue format
-LLocalForwardlocal_port remote_host:remote_port
-RRemoteForwardremote_port local_host:local_port

LocalForward example

1
2
3
4
5
6
# ~/.ssh/config
Host db-tunnel
    HostName jump-server.example.com
    User admin
    LocalForward 5432 db-server.internal:5432
    LocalForward 6379 redis.internal:6379

Usage: ssh db-tunnel (equivalent to ssh -L 5432:db-server.internal:5432 -L 6379:redis.internal:6379 admin@jump-server.example.com).

After connecting, access localhost:5432 and localhost:6379 locally to reach the internal services through the jump host.

RemoteForward example

1
2
3
4
5
6
# ~/.ssh/config
Host expose-dev
    HostName public-server.example.com
    User deploy
    RemoteForward 8080 localhost:3000
    RemoteForward 9090 localhost:8080

Usage: ssh expose-dev (forwards remote ports 8080/9090 to local ports 3000/8080 respectively).

Forward-only mode (no shell)

1
2
ssh -N db-tunnel      # establish tunnel only, no remote shell
ssh -N -f db-tunnel   # same, but run in background

combine -N with your config aliases for quick, background-only tunnels.

Share on