> ## Content Index
> Fetch the complete content index at: https://snubmonkey.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# SSH Port Forwarding ⤳ aka SSH Tunneling — Remote Port Forwarding (Part 2)
- URL: https://snubmonkey.com/ssh-port-forwarding-aka-ssh-tunneling-remote-port-forwarding-part-2/
- Published: 2026-09-09T00:04:39.000Z
- Updated: 2026-09-09T00:04:40.000Z
- Description: Learn how SSH remote port forwarding securely makes local services accessible from remote systems. Explore ssh -R, reverse SSH tunnels, NAT and firewall traversal, GatewayPorts, security considerations, and practical examples.
- Author: SNUBmonkeyteam
- Tags: how to 🪄, ethical hacking 🤖, SSH 🕳, security 🦾🦿

# SSH Tunneling — Part 2: Remote Port Forwarding

*How reverse SSH tunnels make local services accessible from remote systems*

In [Part 1](https://snubmonkey.com/ssh-port-forwarding-aka-ssh-tunneling-local-port-forwarding-part-1/), we used **local port forwarding (`-L`)** to access a remote service from our local machine.

This time, we **reverse** the direction.

**Remote port forwarding (`-R`)** allows a service running on your local machine—or on a host reachable from it—to be accessed through a listening port on the remote SSH server.

This is commonly called a **reverse SSH tunnel**.

## How SSH Port Forwarding Works

SSH supports three primary forwarding modes:

1. **Local port forwarding (`-L`)** — a local port forwards to a service reachable from the remote side.
2. **Remote port forwarding (`-R`)** — a remote port forwards to a service reachable from the local side.
3. **Dynamic port forwarding (`-D`)** — creates a SOCKS proxy that dynamically forwards connections through the SSH server.

The key difference between `-L` and `-R` is **where the listening port is created**.

### Local forwarding

```
Local Machine
localhost:8080
      │
      │ SSH tunnel
      ▼
Remote Server
localhost:3000
```

### Remote forwarding

```
Remote Server
localhost:8080
      │
      │ SSH tunnel
      ▼
Local Machine
localhost:3000
```

With `-R`, the SSH connection is still established from the local machine to the remote server. What changes is **where SSH creates the listening port**.

# Remote Port Forwarding (`-R`)

Remote port forwarding allows you to expose a service reachable from your local machine through a port on the remote SSH server.

The basic syntax is:

```
$ ssh -N -R [REMOTE_BIND_ADDRESS:]REMOTE_PORT:DESTINATION_HOST:DESTINATION_PORT user@REMOTE_SERVER
```

The easiest way to think about it is:

```
-R REMOTE_PORT:DESTINATION_HOST:DESTINATION_PORT
```

> Create a listening port on the remote side and send its traffic through SSH to the destination on the local side.  

## A Practical Example

Suppose you have a web application running on your local machine:

```
localhost:2399
```

You want a remote server to be able to access that application.

Create the reverse tunnel:

```
$ ssh -N -R 2393:localhost:2399 user@REMOTE_SERVER_IP
```

SSH establishes the connection from your machine to:

```
REMOTE_SERVER_IP
```

and creates a listening port:

```
REMOTE_SERVER_IP:2393
```

The traffic flow becomes:

```
Remote Server
localhost:2393
      │
      │ SSH tunnel
      ▼
Your Local Machine
localhost:2399
      │
      ▼
Web Application
```

Now, a process running on the remote server can connect to:

```
localhost:2393
```

and its traffic is carried through the existing SSH connection to:

```
localhost:2399
```

on your local machine.

### The important distinction

The SSH connection itself is:

```
Your Machine ───────────► Remote Server
```

But the forwarded traffic flows:

```
Remote Server ──────────► Your Machine
```

That is why this is commonly called a **reverse tunnel**.

The SSH session does not reverse direction. **The forwarded connection does.**

# Why Reverse SSH Tunneling Is Useful

Remote port forwarding is particularly useful when the machine running the service cannot accept inbound connections.

For example:

```
Your Laptop
     │
     │ outbound SSH connection
     ▼
Remote Server
```

Your laptop might be behind:

- NAT
- A home router
- A corporate firewall
- CGNAT
- A restrictive network

Normally, the remote server cannot establish a direct connection back to your laptop.

But if your laptop can establish an **outbound SSH connection**, it can create a reverse tunnel:

```
Your Laptop
     │
     │ SSH connection
     ▼
Remote Server
     │
     │ remote forwarded port
     ▼
     └──────────► SSH tunnel ──────────► Your Laptop
```

No inbound connection to your laptop is required.

This makes reverse tunneling useful for remote development, testing, temporary access, and reaching services behind restrictive network boundaries.

# The Destination Does Not Have to Be `localhost`

Just like local forwarding, the destination does not have to be `localhost`.

For example:

```
$ ssh -N -R 8080:10.10.20.50:80 user@REMOTE_SERVER
```

The remote server listens on port `8080`, while traffic is forwarded through the SSH connection to:

```
10.10.20.50:80
```

from the local side's network perspective.

This can be useful when your local machine can reach an internal service that the remote server cannot reach directly.

For example:

```
Remote Server
     │
     │ connects to :8080
     ▼
Remote SSH Forward
     │
     │ SSH tunnel
     ▼
Your Local Network
     │
     ▼
10.10.20.50:80
```

The SSH client acts as the bridge between the two network contexts.

# Controlling Who Can Access the Remote Port

There is an important security detail with `-R`.

By default, OpenSSH binds remote forwarded ports to the loopback interface. In practice, this usually means the forwarded port can only be accessed from the remote server itself.

For example:

```
$ ssh -N -R 8080:localhost:2399 user@REMOTE_SERVER
```

typically creates:

```
Remote Server
127.0.0.1:8080
```

rather than:

```
Remote Server
0.0.0.0:8080
```

This default is important because it prevents a reverse tunnel from automatically becoming a publicly accessible service.

# GatewayPorts

The SSH server's `GatewayPorts` setting controls whether remote forwarded ports can be accessed from other hosts.

It is configured in:

```
/etc/ssh/sshd_config
```

The main options are:

### `GatewayPorts no`

This is the default.

Remote forwarding is bound to the loopback interface, so the forwarded port is normally accessible only from the SSH server itself.

```
127.0.0.1:8080
```

### `GatewayPorts yes`

Allows remote forwarded ports to bind to wildcard addresses.

For example:

```
0.0.0.0:8080
```

If the server is publicly reachable, this can make the forwarded service accessible from the Internet.

This should therefore be enabled only when there is a clear requirement and appropriate network controls are in place.

### `GatewayPorts clientspecified`

Allows the SSH client to specify the address on which the remote forwarded port should listen.

For example:

```
$ ssh -N -R 15.152.66.244:8080:localhost:80 user@REMOTE_SERVER
```

This requests that the remote forwarding listen specifically on:

```
15.152.66.244:8080
```

rather than all interfaces.

Whether this works depends on the SSH server configuration and the address being available on the remote host.

# Remote Forwarding and Security

A reverse tunnel can be extremely useful—but it can also unintentionally turn a private service into a remotely accessible one.

For example:

```
Private Service
localhost:2399
       │
       │ SSH tunnel
       ▼
Public Server
:8080
       │
       ▼
Potential external access
```

If the remote forwarded port is bound to a public interface, anyone who can reach that port may potentially reach the service behind the tunnel.

For this reason:

- Keep remote forwards bound to loopback unless external access is required.
- Restrict which users are allowed to create forwarding tunnels.
- Use firewall rules to limit access where appropriate.
- Monitor persistent SSH connections.
- Avoid exposing administrative interfaces or sensitive services unnecessarily.
- Use SSH authentication controls and least privilege.

A reverse tunnel should be treated as a **network access mechanism**, not simply as a convenient port mapping.

# Running a Reverse Tunnel in the Background

For a tunnel that does not require an interactive shell:

```
$ ssh -f -N \
  -R 127.0.0.1:2393:localhost:2399 \
  user@REMOTE_SERVER
```

The remote server now has:

```
127.0.0.1:2393
```

forwarding to:

```
Your Machine → localhost:2399
```

You can verify the listening port on the remote server with:

```
$ ss -lntp
```

# Keeping a Reverse Tunnel Alive

For long-running reverse tunnels, SSH keepalives can help detect broken connections:

```
$ ssh -N \
  -o ServerAliveInterval=30 \
  -o ServerAliveCountMax=3 \
  -R 127.0.0.1:2393:localhost:2399 \
  user@REMOTE_SERVER
```

For persistent environments, `autossh` can automatically restart a failed SSH connection:

```
$ autossh -M 0 -N \
  -o ServerAliveInterval=30 \
  -o ServerAliveCountMax=3 \
  -R 127.0.0.1:2393:localhost:2399 \
  user@REMOTE_SERVER
```

Keepalives help detect a dead connection; `autossh` adds automatic recovery when the SSH session terminates.

# Troubleshooting

If the reverse tunnel does not work, start with verbose SSH output:

```
$ ssh -vvv -N \
  -R 127.0.0.1:2393:localhost:2399 \
  user@REMOTE_SERVER
```

Then verify the remote listening socket:

```
$ ss -lntp
```

On the remote server, test the forwarded port:

```
$ curl http://127.0.0.1:2393
```

If the remote port is listening but the connection fails, verify that the destination service is reachable from the machine running the SSH client:

```
$ curl http://127.0.0.1:2399
```

The diagnostic path is therefore:

```
Remote listener works?
        │
        ▼
SSH connection works?
        │
        ▼
Local destination reachable?
        │
        ▼
Application responding?
```

This helps isolate whether the problem is with SSH, forwarding, networking, or the application itself.

# Quick Reference

### Basic reverse tunnel

```
$ ssh -N -R 8080:localhost:3000 user@server
```

```
Remote :8080 → Local :3000
```

### Explicit loopback binding

```
$ ssh -N -R 127.0.0.1:8080:localhost:3000 user@server
```

### Forward to another local-network host

```
$ ssh -N -R 8080:10.10.20.50:80 user@server
```

### Background tunnel

```
$ ssh -f -N -R 127.0.0.1:8080:localhost:3000 user@server
```

### Debugging

```
$ ssh -vvv -N -R 127.0.0.1:8080:localhost:3000 user@server
```

# The Mental Model

If `-L` means:

```
-L

Local listener
      │
      ▼
Remote destination
```

then `-R` means:

```
-R

Remote listener
      │
      ▼
Local destination
```

A simple way to remember it:

```
-L  → Listen here, reach there.
-R  → Listen there, reach here.
```

The SSH connection itself still originates from the local machine:

```
Local ───────────► SSH Server
```

But with remote forwarding, connections arriving at the remote listener are carried back through that SSH session:

```
Remote listener
      │
      ▼
SSH tunnel
      │
      ▼
Local destination
```

Once this distinction is clear, `-R` becomes much easier to reason about.

## What’s Next?

Remote forwarding moves traffic **from a remote system toward a service on your local side**.

In this tutorial, we used **remote port forwarding (`-R`)** to make a local service accessible through a remote SSH server—without requiring a direct inbound connection to the local machine.

The key idea is simple:

```
-R REMOTE_PORT:DESTINATION_HOST:DESTINATION_PORT
```

**Create the listener on the remote side, and carry the traffic back through the existing SSH connection to the destination on the local side.**

In the next part, we move beyond fixed port mappings with **dynamic port forwarding (`-D`) and SOCKS proxies**, allowing applications to dynamically route TCP connections through an SSH server.

As we wrap up this tutorial, I’d like to sincerely thank everyone who took part and contributed along the way. Your questions, ideas, and curiosity are what make learning truly valuable.

Learning is a collaborative journey, so keep exploring, keep asking questions, and don’t be afraid to dig deeper.

Thank you again for being part of it, and best of luck on the journey ahead!