Check a port first
Test whether a port on a public host is reachable from the internet:
Local forwarding: -L
Opens a port on your machine and forwards it, through the SSH server, to a destination.
ssh -L 8080:localhost:80 user@server.example.com
Now http://localhost:8080 on your computer reaches port 80 as seen from the server. The form is -L local_port:destination_host:destination_port.
A common use is reaching a database that only accepts connections from inside a network:
ssh -L 5433:db.internal.example:5432 user@bastion.example.com
psql -h localhost -p 5433 mydb
Remote forwarding: -R
Opens a port on the SSH server and forwards it back to a destination reachable from your machine.
ssh -R 9000:localhost:3000 user@server.example.com
Anyone on the server can now reach your local port 3000 at localhost:9000 there. This is how you share a development site from behind a router without opening a port. By default the server binds it to its loopback address only; exposing it publicly needs GatewayPorts enabled in the server's configuration, which should be a deliberate choice. Compare with port forwarding on a router.
Dynamic forwarding: -D
ssh -D 1080 user@server.example.com
This starts a SOCKS proxy on localhost:1080. Set a browser or program to use it and its traffic leaves from the server. It behaves like a small VPN for those programs.
Useful flags
| Flag | Effect |
|---|---|
-N | Don't run a remote command. Use it when you only want the tunnel. |
-f | Go to the background after connecting. |
-J host | Jump through another host first. |
-p port | Connect to an SSH server that isn't on port 22. |
ssh -fN -L 8080:localhost:80 user@server.example.com
Troubleshooting
- "bind: Address already in use". Something already listens on that local port. Pick another, or find the process with the steps in how to check open ports.
- "channel open failed: administratively prohibited". The server's
AllowTcpForwardingsetting forbids it. An administrator has to allow it. - "connect failed: Connection refused". The tunnel works but nothing listens at the destination you named.
- The tunnel drops when idle. Add
-o ServerAliveInterval=60to keep it alive.
Security notes
- Bind forwards to
localhost(the default) so nobody else on your network can use them. - A tunnel can reach services that a firewall was meant to protect. On servers where you don't need forwarding, turn it off with
AllowTcpForwarding no. - Use keys, not passwords, for connections you leave running.