An SSH tunnel lets you reach Remote Desktop without exposing port 3389 to the whole internet. Your RDP app connects to a port on your own computer. SSH carries that traffic to the server and hands it to Remote Desktop on 127.0.0.1:3389. Bots see only the SSH port, which you can protect with keys.
This guide sets up the tunnel with OpenSSH on your Windows VPS, tests it, and only then restricts public access to 3389.
Before you start
- A Windows VPS with Administrator access over Remote Desktop.
- OpenSSH Server installed and running on the VPS. If it is not, follow Install OpenSSH Server on Windows first.
- An inbound firewall rule for SSH (TCP 22). The OpenSSH installer normally creates one called
OpenSSH-Server-In-TCP. - An SSH client on your computer. Windows 10/11, macOS and Linux all include
ssh. - Access to your VPS provider's browser console as a fallback.
Note: Key-based SSH login is strongly recommended before you rely on this tunnel. See Set up SSH key login on Windows.
How the tunnel works
A local forward has three parts:
- Local port on your computer, for example
13389. - SSH server, your VPS at
203.0.113.10, port 22. - Destination as seen from the VPS:
127.0.0.1:3389, where Remote Desktop listens.
Your RDP app connects to 127.0.0.1:13389. SSH encrypts the traffic, sends it to the VPS, and the VPS connects to itself on 3389. Because that last hop comes from the server itself, it does not need the public firewall rule.
We use 13389 locally so it never clashes with anything already running on your PC.
Check SSH works first
From your computer, sign in over SSH with your Windows username:
ssh admin@203.0.113.10Accept the host key the first time, enter your password (or use your key), and you should get a cmd or PowerShell prompt on the server. Type exit to leave. If this fails, fix SSH before going further.
Open the tunnel
On your computer, run:
ssh -N -L 13389:127.0.0.1:3389 admin@203.0.113.10-L 13389:127.0.0.1:3389forwards local port 13389 to 127.0.0.1:3389 on the server.-Nopens the tunnel without starting a remote shell.
After you sign in, the window looks idle. That is correct. The tunnel stays open as long as this window does. Press Ctrl + C to close it.
On Windows, run the same command in PowerShell or Windows Terminal. On macOS or Linux, run it in Terminal.
Save it in your SSH config
To avoid typing the full command, add an entry to ~/.ssh/config (on Windows, C:\Users\<you>\.ssh\config):
Host rdp-tunnel
HostName 203.0.113.10
User admin
LocalForward 13389 127.0.0.1:3389
ServerAliveInterval 30Then start it with ssh -N rdp-tunnel. ServerAliveInterval keeps idle tunnels from being dropped by routers.
Connect Remote Desktop through the tunnel
With the tunnel open:
- Press Win + R, type
mstscand press Enter. - In Computer, enter
127.0.0.1:13389. - Click Connect and sign in with your Windows username and password.
Or from PowerShell:
mstsc /v:127.0.0.1:13389On a Mac, add a PC in Windows App with the PC name 127.0.0.1:13389.
The certificate warning will mention a name mismatch, because you connected to 127.0.0.1. That is expected for a tunnel.
Check it works
While connected through the tunnel, open PowerShell on the server and run:
Get-NetTCPConnection -LocalPort 3389 -State Established |
Format-Table LocalAddress, RemoteAddressA line with RemoteAddress of 127.0.0.1 (or ::1) proves your session arrived through SSH rather than directly over the internet.
Tighten public access to 3389
Only do this after the tunnel has worked at least once, and preferably from more than one place.
Warning: Once 3389 is closed to the internet, the tunnel becomes your only Remote Desktop route. If SSH breaks, you will need the browser console in your VPS provider's control panel to get back in.
The safer option is to allow 3389 only from one fixed IP you trust, as described in Restrict RDP port 3389 to your own IP addresses:
Set-NetFirewallRule -DisplayName "Remote Desktop (TCP 3389)" -RemoteAddress 198.51.100.20If you want the tunnel to be the only route, disable the public rule instead. Disabling keeps it, so you can turn it back on quickly:
Disable-NetFirewallRule -DisplayName "Remote Desktop (TCP 3389)"To undo, run Enable-NetFirewallRule with the same name, from the browser console if needed. Use the exact display name of your 3389 rule.
Troubleshooting
- "bind: Address already in use": something on your computer already uses 13389. Pick another local port, such as 23389, in both the
sshandmstsccommands. - mstsc says it can't connect to 127.0.0.1:13389: the tunnel window is closed or the SSH login failed. Start the tunnel again and keep that window open.
- "channel 2: open failed: connect failed": the tunnel is up, but nothing answers on 3389 on the server. Check that Remote Desktop listens on 3389 from the server itself.
- Tunnel drops after idle time: add
ServerAliveInterval 30to your SSH config, as shown above.
Next steps
- Learn more about forwarding: SSH local port forwarding.
- Compare options: Proxy vs VPN vs SSH tunnel.
