SSH Tunnelling Explained: Port Forwarding to Reach Devices Behind NAT

Your project dashboard runs on a device with no public IP. SSH tunnelling forwards ports through an encrypted SSH connection so you can reach it from anywhere. Covers local (-L), remote (-R) and dynamic (-D) forwarding with exact commands, keepalive setup, and the security rules students usually skip.

Written by Projectech8 min readPublished
For B.E./B.Tech Computer and IT students who need to reach a campus-hostel or home device from outside the LAN for academic and mini projects Topics: SSH, Port Forwarding, Linux, Raspberry Pi
Technical illustration of an encrypted SSH tunnel carrying forwarded ports between a laptop and a remote device behind a firewall
Illustration generated for this guide.
In this guide

SSH Tunnelling Explained: Port Forwarding to Reach Devices Behind NAT

Summary: Your project dashboard runs on a Raspberry Pi in your hostel room, but the hostel Wi-Fi gives it no public IP. SSH tunnelling lets you reach it — securely, from anywhere — by forwarding ports through an encrypted SSH connection. This guide explains local, remote and dynamic forwarding, shows the exact commands, and warns you about the security pitfalls students hit most.

The problem: everything sits behind NAT

Campus hostels, homes and mobile networks hand your devices private addresses (192.168.x.x, 10.x.x.x). Your router's NAT table lets your Pi reach the internet, but nothing from the internet can start a connection back to it. There is no public IP pointing at your device, and you cannot ask your hostel warden to forward ports on the building router.

So your situations look like this:

  • A Flask dashboard runs on a Raspberry Pi in your room at localhost:5000. You want to show it to your project guide from another city.
  • An ESP32-CAM streams on the lab subnet. You want to view it from your laptop on a different network.
  • You need to pull code onto a server that only accepts connections from one jump host.

SSH tunnelling solves all three. You already have an SSH client; a tunnel just reuses that encrypted connection to carry other traffic. If you can SSH from machine A to machine B, you can forward any TCP port between them — without installing a VPN, without touching a router, and with everything encrypted end-to-end over the SSH channel.

How a tunnel actually works

Forget magic: an SSH tunnel is port forwarding through an encrypted pipe. When you create a tunnel, your SSH client listens on one machine and forwards everything it receives through the SSH connection to the other machine, which then makes a local TCP connection to the destination service.

There are three forwarding modes. The trick is remembering who listens where:

  • Local forwarding (-L): your local machine listens on a port and forwards into the remote network. You use this to reach something on the far side.
  • Remote forwarding (-R): the remote machine listens on a port and forwards back to your machine. You use this to expose something local to the far side.
  • Dynamic forwarding (-D): your local machine becomes a SOCKS proxy that forwards anywhere through the SSH connection. You use this for general browsing-like access.

Read the syntax as "-L [listen-side]:[listen-port]:[target-host]:[target-port]", where target-host is resolved from the perspective of the SSH server.

1. Local forwarding: reach a far-side service

Scenario: the Pi in your room runs a Node-RED dashboard at localhost:1880 on the Pi itself. You SSH into the Pi as pi-user. From your laptop:

ssh -L 8080:localhost:1880 pi-user@campus-host

Now open http://localhost:8080 on your laptop — you are talking to the Pi's dashboard. The breakdown:

  • Your laptop listens on port 8080.
  • Traffic goes through the encrypted SSH session to the Pi.
  • The Pi connects to localhost:1880 — which is the Pi's localhost, not yours — and shuttles bytes back.

The service itself never touches the public internet. This is the pattern to demo a project to an examiner: tunnel the dashboard to your laptop, share your screen, and nobody can hit the Pi directly.

A second classic use: reaching a service on a third machine only visible to the SSH server. If the Pi can ping a lab NAS at 192.168.5.20:9000:

ssh -L 9000:192.168.5.20:9000 pi-user@campus-host

192.168.5.20 is resolved from the Pi's network. Your laptop never needed a route to that subnet.

2. Remote forwarding: expose a local service to the far side

Scenario: reversed — you are home on a laptop running a demo web app on port 3000, and a teammate's server (team-server) needs to reach it. From your laptop:

ssh -R 9000:localhost:3000 teammate@team-server

Now the server can open http://localhost:9000 and reach your laptop's app. Traffic flows back through the same SSH connection you initiated — no inbound firewall rule on your side needed.

The catch students forget: by default the remote port binds to 127.0.0.1 on the server, so only the server itself can use it. If you want the whole internet to reach it (rarely a good idea — see the security section), the server's sshd_config needs GatewayPorts yes, which campus/sysadmin machines almost never allow.

3. Dynamic forwarding: a SOCKS proxy through your SSH connection

ssh -D 1080 pi-user@campus-host

Point your browser's proxy settings at socks5://localhost:1080 and every site you visit now exits from the Pi's network, over the encrypted tunnel. Handy when a journal site or a campus portal only loads from the university network, or when you want to test how your deployed project behaves from a different network without leaving your desk.

Keeping tunnels alive and usable

SSH connections idle out. Three habits save your sanity:

  1. Run in the background with auto-reconnect:
ssh -f -N -L 8080:localhost:1880 pi-user@campus-host

-f backgrounds the client, -N skips the shell (no remote command needed). Pair with autossh on Linux for automatic restarts: autossh -M 0 -N -L 8080:localhost:1880 pi-user@campus-host.

  1. Tune keepalives so NAT boxes don't silently drop the idle TCP session. In ~/.ssh/config:
Host campus-host
    ServerAliveInterval 30
    ServerAliveCountMax 3
  1. Name your tunnels in the config file so you stop retyping flags:
Host pi-dashboard
    HostName campus-host
    User pi-user
    LocalForward 8080 localhost:1880
    ServerAliveInterval 30

Then ssh -f -N pi-dashboard is the whole workflow.

Security: the part students skip

A tunnel is a hole through a firewall, and holes get found. Respect these:

  • Bind locally by default. ssh -L binds to 127.0.0.1 unless you write 0.0.0.0: before the listen port. Never bind 0.0.0.0 on a shared machine unless you intend everyone on that network to ride your tunnel.
  • A tunnel is not authentication. Whatever service you expose through a tunnel gets exactly the authentication it already had. Tunnelling your dashboard does not add a login page — put one there.
  • Don't tunnel around policy. If your institute's network blocks something, there is usually a reason and often a rule. Tunnels for your own project demo are normal engineering; tunnels to evade network policy can violate your institute's IT rules.
  • Key-based auth only. Tunnels usually run unattended, so they must not depend on typed passwords — and unattended passwords sitting in scripts are a leak. Use Ed25519 keys with a passphrase and ssh-agent.
  • Watch remote forwarding. -R gives the remote side a path into your machine. Only accept remote forwards from hosts you administer, and prefer them scoped to 127.0.0.1 on that side.

Quick diagnosis table

Symptom Likely cause Fix
channel_setup_fwd_listener: cannot listen to port Port already in use locally Pick another local port, or find the process with ss -tlnp
Tunnel connects but the page never loads Target host/port wrong from the server's perspective SSH in and curl the target from the server itself first
Tunnel dies after minutes of idle NAT/firewall drops idle TCP ServerAliveInterval 30 in config
-R port unreachable from other machines Bound to 127.0.0.1 by default Expected behaviour; needs GatewayPorts on the server to change
Permission denied (publickey) Key not on the server ssh-copy-id the key, check ~/.ssh permissions (700/600)

FAQ

Can I tunnel UDP, like a game server or DNS?
OpenSSH port forwarding is TCP-only. UDP needs other tools (WireGuard, socat relays). Plan your project accordingly — most web dashboards and APIs are TCP anyway.

Is an SSH tunnel a VPN?
No. A VPN routes whole network layers; a tunnel forwards individual TCP ports through one connection. For a student project demo, tunnels are simpler and sufficient.

How is this different from port forwarding on my router?
Router port forwarding exposes a service to the entire internet permanently. An SSH tunnel exposes it only through your encrypted session, only while the session runs, and (with default binds) only to the machine you choose.

Can I do this on Windows?
Yes — OpenSSH ships with Windows 10/11, so ssh -L/-R/-D work in PowerShell. PuTTY also supports tunnels under Connection → SSH → Tunnels if you prefer a GUI.

Limitations

  • TCP only, as noted — no UDP forwarding with stock OpenSSH.
  • Throughput is single-connection and CPU-bound by encryption; fine for dashboards and APIs, not for bulk file transfer (use scp/rsync for that).
  • A tunnel is only as reliable as the underlying SSH session; for always-on exposure, a proper reverse-proxy host or a tunnel service with a static endpoint is the grown-up answer.
  • Some corporate and campus firewalls block outbound SSH (port 22) entirely — in that case, no client-side trick helps; you need an allowed egress path.

More project guides

More in Computer / IT