SSH Port Forwarding and the Command Cargo Cult
Someone is Wrong on the Internet
If you look up how to only forward ports with
ssh, you may come across
solutions like this:
ssh -nNT -L 8000:example.com:80 email@example.com
Or perhaps this, if you also wanted to send
ssh to the background:
ssh -NT -L 3306:db.example.com:3306 example.com &
Both of these use at least one option that is entirely redundant,
and the second can cause
ssh to fail to connect if you happen to be
using password authentication.
However, they seem to still persist in various articles about
ssh port forwarding.
I myself was using the first variation until just recently, and I figured
I would write this up to inform others who might be still using these
The correct option for this situation is not
-nNT but simply
-N, as in:
ssh -N -L 8000:example.com:80 firstname.lastname@example.org
If you want to also send
ssh to the background,
then you’ll want to add
-f instead of using your shell’s built-in
feature, because you can then input passwords into
ssh if necessary1
Honestly, that’s the point of this article, so you can stop here if you want. If you’re looking for a detailed explaination of what each of these options actually does, or if you have no idea what I’m talking about, read on!
What is SSH Port Forwarding?
ssh is a powerful tool for remote access to servers,
allowing you to execute commands on a remote machine.
It can also forward ports
through a secure tunnel with the
Basically, you can forward a connection to a local port to a remote server
ssh -L 8080:other.example.com:80 ssh.example.com
In this example, you connect to
ssh.example.com and then
any traffic on your local machine port 80802
other.example.com port 80 via
This is a really powerful feature, allowing you to jump3 inside your firewall
with just an
ssh server exposed to the world.
It can work in reverse as well with the
-R option, allowing connections
on a remote host in to a server running on your local machine.
For example, say you were running a website on your local machine on port 8080
but wanted it accessible on
example.com port 804.
You could use something like:
ssh -R 8080:example.com:80 example.com
The trouble with
ssh port forwarding is that, absent any additional options,
you also open a shell on the remote machine.
If you’re planning to both work on a remote machine and use it to forward
some connection, this is fine, but if you just need to forward a port quickly
and don’t care about a shell at that moment, it can be annoying, especially since
if the shell closes
ssh will close the forwarding port as well.
This is where the
-N option comes in.
SSH just forwarding ports
ssh manual page5,
-N is explained like so:
Do not execute a remote command. This is useful for just forwarding ports.
This is all we need. It instructs
ssh to run no commands on the remote server,
just forward the ports specified in the
But people seem to think that there are a bunch of other necessary options,
so what do those do?
SSH and stdin
-n controls how
ssh interacts with standard input,
specifically telling it not to:
Redirects stdin from /dev/null (actually, prevents reading from stdin). This must be used when ssh is run in the background. A common trick is to use this to run X11 programs on a remote machine. For example, ssh -n shadows.cs.hut.fi emacs & will start an emacs on shadows.cs.hut.fi, and the X11 connection will be automatically for‐ warded over an encrypted channel. The ssh program will be put in the background. (This does not work if ssh needs to ask for a password or passphrase; see also the -f option.)
SSH passwords and backgrounding
ssh to background, freeing up the terminal in which you ran
to do other things.
Requests ssh to go to background just before command execution. This is useful if ssh is going to ask for passwords or passphrases, but the user wants it in the background. This implies -n. The recommended way to start X11 programs at a remote site is with something like ssh -f host xterm.
As indicated in the description of
-n, this does the same thing as using
-n, but allows you to put in any necessary passwords first.
SSH and pseudo-terminals
-T is a little more complicated than the others and has a very short
Disable pseudo-terminal allocation.
It has a counterpart in
-t, which is explained a little better:
Force pseudo-terminal allocation. This can be used to execute arbitrary screen-based programs on a remote machine, which can be very useful, e.g. when implementing menu services. Multiple -t options force tty allocation, even if ssh has no local tty.
As the description of
ssh is allocating a pseudo-terminal on
the remote machine, not the local one.
However, I have confirmed6 that
-N doesn’t allocate a pseudo-terminal either,
since it doesn’t run any commands.
Thus this option is entirely unnecessary.
What’s a pseudo-terminal?
This is a bit complicated, but basically it’s an interface used in UNIX-like
systems, like Linux or BSD, that pretends to be a terminal
Programs like your shell, or any text-based menu system made in libraries
like ncurses, expect to be connected to one (when used interactively at least).
Basically it fakes as if the input it is given
(over the network, in the case of
ssh) was typed on a physical terminal
device and do things like raise an interrupt (SIGINT) if Ctrl+C is pressed.
I don’t know why these incorrect uses of
ssh got passed around as correct,
but I suspect it’s a form of cargo cult,
where we use example commands others provide and don’t question what they do.
One stack overflow answer I read that provided these options seemed to think
-T was disabling the local pseudo-terminal, which might go some way towards
explaining why they thought it was necessary.
I guess the moral of this story is to question everything and actually read the manual, instead of just googling it.
Not that you SHOULD be using
sshwith password authentication anyway, but people do. ↩
Only on your loopback address by default, so that you’re not allowing random people on your network to use your tunnel. ↩
I can’t say I recommend a setup like this for anything serious, as you’d need to
sshas root to forward ports less than 1024. SSH forwarding is not for permanent solutions, just short-lived connections to machines that would be otherwise inaccessible. ↩
Specifically, my source is the ssh(1) manual page in OpenSSH 8.4, shipped as 1:8.4p1-5 in Debian bullseye. ↩
I just forwarded ports with
-Nand then logged in to that same machine and looked at psuedo-terminal allocations via
ps ux. No terminal is associated with
sshconnections using just the