Guide

What is an SSH tunnel? SSH port forwarding explained

An SSH tunnel sends another program's network connection through an encrypted SSH connection. It's also called SSH port forwarding. A local forward (ssh -L) lets you reach a service the server can see, a remote forward (ssh -R) lets the server reach a service on your computer, and a dynamic forward (ssh -D) turns the connection into a SOCKS proxy.

Tunnels are the simple way to reach a database, an admin page or a desktop that isn't open to the internet, with nothing but the SSH port open. Here's how each kind works, with commands you can copy.

  • Local -L
  • Remote -R
  • Dynamic -D (SOCKS)

Updated · OpenSSH on Linux, macOS and Windows 10 and 11

How an SSH tunnel works

You start with an SSH connection to a server, usually on port 22 (if the server doesn't run SSH yet, enable SSH on Ubuntu or Debian first). A tunnel adds a second channel inside that connection. With a local forward, for example:

your app --> localhost:5433 on your computer
         --> encrypted SSH connection (port 22) -->
         server --> localhost:5432 on the server (the database)

Your app thinks the database is on your own computer. ssh carries the bytes to the server, and the server makes the last hop. Only the SSH port has to be reachable, and the part between you and the server is encrypted. The last hop, from the server to the target, is an ordinary connection: often that's the server talking to itself, which is ideal.

The three kinds at a glance

Local, remote and dynamic SSH port forwarding compared
KindExamplePort opens onUse it to
Local (-L)ssh -L 5433:localhost:5432 user@serverYour computerReach a service only the server can see.
Remote (-R)ssh -R 9000:localhost:3000 user@serverThe serverLet the server reach a service on your computer.
Dynamic (-D)ssh -D 1080 user@serverYour computer (SOCKS)Send a browser or app's traffic out from the server.

Local port forwarding (ssh -L)

The pattern is ssh -L local_port:target_host:target_port user@ssh_server. The target is seen from the server's side, so localhost means the server itself.

# a database that only listens on the server itself
ssh -L 5433:localhost:5432 admin@db.example.com

# an admin page on the server's private network
ssh -L 8443:192.168.10.5:443 admin@gateway.example.com

# a desktop shared over VNC on the server
ssh -L 5901:localhost:5900 admin@desktop.example.com

While ssh runs, point your program at localhost and the local port: the database tool at localhost:5433, the browser at https://localhost:8443 (it may warn that the certificate's name doesn't match), the VNC viewer at localhost:5901. Add -N to forward without opening a shell, and -f to send ssh to the background. One command can carry several -L options.

Remote port forwarding (ssh -R)

Remote forwarding is the mirror image: the port opens on the server and leads back to your side.

ssh -R 9000:localhost:3000 admin@server.example.com

On the server, localhost:9000 now reaches port 3000 on your computer, for example a web app you're developing that the server needs to call. By default the port only listens on the server's own loopback, so other machines can't use it. The server setting GatewayPorts changes that; leave it at its default (no) unless you mean to share the port.

Dynamic port forwarding (ssh -D): a SOCKS proxy

ssh -D 1080 -N admin@server.example.com

# then, for example:
curl --socks5-hostname localhost:1080 http://intranet.example.internal/

-D starts a SOCKS proxy on your computer. Any program you set to use the SOCKS5 proxy at localhost, port 1080, makes its connections from the server, so it can reach what the server reaches, names included. It's also how you can send other SSH connections through one server: see connecting to SSH servers through a SOCKS or HTTP proxy.

Hopping through a server: ProxyJump instead of a tunnel

To SSH to a machine that's only reachable through another one (a jump host or bastion), you don't need to build a tunnel by hand. ssh does it for you:

ssh -J admin@bastion.example.com admin@10.0.0.5

Save it as ProxyJump in your SSH config file and the hop happens every time. Your keys stay on your computer; the bastion only passes the encrypted connection along.

SSH agent forwarding

Agent forwarding (ssh -A, or ForwardAgent yes for one host in your config) lets programs on the server use the keys loaded in your computer's ssh-agent, for example to fetch code from a repository with your key, without copying the key to the server.

The catch: while you're connected, anyone with root on that server can use your agent to sign in as you elsewhere. Only forward your agent to servers you trust, never set it for Host *, and use ProxyJump rather than agent forwarding to hop between servers. Setting up the agent itself is in the guide to passwordless SSH with keys.

Keep SSH tunnels safe

  • Keep forwarded ports on localhost. That's the default. Writing -L 0.0.0.0:5433:... or adding -g opens the port to your whole network.
  • Close tunnels when you're done. Use -N for tunnel-only sessions, and -o ExitOnForwardFailure=yes so ssh stops if it can't open the port instead of carrying on without it.
  • Remember the last hop. Only the part between you and the SSH server is encrypted by the tunnel. Beyond the server, the connection is whatever the protocol itself is.
  • Ask before you tunnel out of a network you don't run. A tunnel goes around the firewall's rules, so check it's allowed.
  • Server admins decide. In sshd_config, AllowTcpForwarding no turns tunnels off for everyone or (inside a Match block) for some users, and PermitOpen limits where local forwards may go.
  • Keep idle tunnels alive. -o ServerAliveInterval=60 stops a router dropping a quiet connection.

SSH tunnels in RemoteAccessApp, without the command line

In the RemoteAccessApp SSH client, a saved SSH host can carry its own port forwards, which start when you open its terminal. It's free, in the Console for Windows and Linux:

  1. Step 1: Edit the SSH host

    On Connections, right-click the host and choose Edit. Open Advanced, then Port forwarding.

  2. Step 2: Add a forward

    Pick the kind: This PC's port to the server (like ssh -L), Server port to this PC (like ssh -R) or SOCKS proxy on this PC (like ssh -D). Enter the port that should listen and, for the first two, where the connection goes.

  3. Step 3: Save and connect

    Open the host's terminal. A strip above it says how many forwards are running and, if one couldn't start, why (a port already in use, or a server that doesn't allow forwarding). Closing the terminal stops them.

Good to know

Forwards only listen on your own PC (a remote forward only on the server itself), so other devices on your network can't use them; a host can have up to 10. Forwards that came from your other computers, an import or an AI assistant ask before they start the first time. LocalForward, RemoteForward and DynamicForward lines come in when you import your SSH config file. VNC hosts have their own Connect through SSH tunnel option, and reaching a server through a jump host is part of Pro. Details in the help article on jump hosts, port forwards and notes.

Want to try it? Download RemoteAccessApp; it's free for up to 5 saved hosts. If your server listens on an SSH port other than 22, put that in the host's Port box; the forwards use the same connection.

Questions, answered

Related