Map

SSH port forwarding

Wiki conceptsshnetworkingsecurity โ†ณ show in map Markdown
title
SSH port forwarding
type
concept
summary
The four shapes of SSH tunneling โ€” local (-L), remote (-R), dynamic SOCKS (-D), and jumphost (-J) โ€” and which network shape each one solves
tags
ssh, networking, security
created
2026-05-21
updated
2026-05-21

SSH carries arbitrary TCP traffic alongside the shell channel. Four flags pick which direction it goes and how it's exposed at each end. The flag names hint at where the local port lives in the address pair: -L puts it on the Left, -R puts it on the Right.

Local (-L)

ssh -N -f -L <local_port>:<remote_host>:<remote_port> user@ssh_server

The SSH client opens a listener on <local_port> on the operator's machine. Connections to it are tunneled through the SSH server and exit toward <remote_host>:<remote_port> as seen from the SSH server. The classic case: a web service bound to 127.0.0.1:80 on the SSH server, accessed via localhost:1337 on the laptop.

<remote_host> doesn't have to be the SSH server itself โ€” it can be anything the SSH server can reach. That makes -L a two-hop proxy: operator โ†’ SSH server โ†’ arbitrary inside-host.

Remote (-R)

ssh -N -f -R <remote_port>:<local_host>:<local_port> user@ssh_server

The SSH server opens the listener instead. Connections to <remote_port> on the SSH server are tunneled back through the existing connection and exit toward <local_host>:<local_port> as seen from the client. Used to expose a service that lives in the client's network to a network the client can't otherwise reach.

By default -R listeners on OpenSSH bind to 127.0.0.1 on the server; GatewayPorts yes in sshd_config widens that to 0.0.0.0.

Dynamic (-D, SOCKS proxy)

ssh -N -f -D <local_port> user@ssh_server

The client becomes a SOCKS5 proxy. Anything that speaks SOCKS โ€” proxychains, Firefox, curl with --proxy socks5://... โ€” routes its traffic through the SSH server. Unlike -L, the destination is chosen per connection by the client, so the same tunnel works for any host the SSH server can reach.

DNS over SOCKS depends on the client. Firefox has "Proxy DNS when using SOCKS V5"; proxychains has proxy_dns in its config. Without it the local resolver answers, which leaks the lookup and may resolve to addresses the SSH server can't reach.

Jumphost (-J)

ssh -J user1@hop1,user2@hop2 user3@target

Chains SSH connections through intermediate hosts without giving the operator a shell on the intermediates. Each hop authenticates separately. Identical in effect to nested ProxyCommand setups but readable in one line. Same as the ProxyJump config keyword.

Gateway mode (-g)

Modifier on -L (and via -L on -D). -g makes the local listener bind 0.0.0.0 instead of 127.0.0.1, exposing the forward to anyone on the operator's LAN. Useful when the operator wants their laptop to be a shared pivot for a team; risky if the local network is hostile.

When to use which

Network shape Use
Operator has SSH to target, target binds loopback-only -L
Operator can't reach target, but target can reach operator -R
Operator wants browser/CLI access to many things behind one foothold -D
Operator must traverse N gates to reach final shell -J
Operator wants others on LAN to share the forward add -g

Runtime forwards

The ~C escape sequence inside an active SSH session opens the client's command line. Typing -D 8080 there adds a dynamic forward to a session that started as a normal shell, without disconnecting. The reverse works too โ€” -KD 8080 cancels it. Useful when discovery happens mid-session and reopening the connection would be noisy.

See also

Sub-pages