SSH Reverse Tunnel
Overview
An SSH reverse tunnel exposes a local service to the internet through a VPS. It works by establishing an outbound SSH connection from your local machine to the VPS, which then forwards incoming traffic back through that connection to your local service.
This is useful when you are behind NAT, a firewall, or lack a public IP address.
Architecture
Internet Request
│
▼
┌─────────────────┐
│ VPS (Public) │
│ nginx :443 │
│ │ │
│ ▼ │
│ localhost:5201 │◄── SSH tunnel listens here
└────────┬────────┘
│
SSH Connection
(outbound from local)
│
▼
┌─────────────────┐
│ Local Machine │
│ localhost:8080 │◄── Your service
└─────────────────┘
Traffic flow:
- Client requests
https://<domain> - nginx terminates TLS and proxies to
127.0.0.1:5201 - Port 5201 is the remote end of the SSH tunnel
- Traffic flows through the tunnel to your local machine on port 8080
Prerequisites
- VPS setup completed (see VPS Setup)
- nginx installed and configured (see nginx Setup)
- A local service running (this guide uses
localhost:8080)
Setup
Configure a subdomain with nginx reverse proxy to 127.0.0.1:5201 and enable SSL (see nginx Setup).
SSH Reverse Tunnel Command
From your local machine:
ssh -N -R 5201:localhost:8080 <username>@<vps-ip>
| Flag | Purpose |
|---|---|
-N | Do not execute a remote command. Port forwarding only. |
-R 5201:localhost:8080 | Bind remote port 5201 to local port 8080. |
Format: -R [remote_port]:[local_host]:[local_port]
The tunnel remains open while the SSH connection is active.
Observability
Stream VPS nginx logs to your local machine:
ssh <username>@<vps-ip> "tail -f /var/log/nginx/access.log" | grep --line-buffered <domain>
Example
# Start local service
npm run dev # localhost:3000
# Establish tunnel
ssh -N -R 5201:localhost:3000 <username>@<vps-ip>
Access from anywhere: https://<domain>