# https://tunnels.io/learn/what-is-ssh-tunneling

[Home](https://tunnels.io/) / [Learn](https://tunnels.io/learn) / What is SSH tunneling?

Concepts

# What is SSH tunneling?

SSH tunneling, also called SSH port forwarding, carries an ordinary TCP connection inside an encrypted SSH session. The client and server open a channel for each forwarded connection, so traffic reaches a host or port the network would otherwise block. There are three modes: local forwarding, remote forwarding and dynamic forwarding.

## How does an SSH tunnel actually work?

SSH multiplexes independent channels over one authenticated, encrypted TCP connection, normally on port 22. Port forwarding is built on that multiplexing: one side listens on a socket, and every connection accepted there becomes a new channel that the other side unwraps and connects onward to a real address. The payload is plain TCP, so SSH neither knows nor cares whether it is HTTP, the Postgres wire protocol or Redis. What SSH forwards is a connection, not a network interface, which is the difference between a tunnel and a VPN.

## What is local port forwarding, the `-L`` flag?

Local forwarding opens a listening socket on your machine and hands each connection to the SSH server, which dials the destination on your behalf. Run `ssh -L 5432:10.0.0.9:5432 user@bastion`` and port 5432 on your laptop starts behaving like port 5432 on 10.0.0.9 as seen from the bastion. The destination host and port are resolved on the server's network, which is the entire point: you reach things only the server can reach. By default the local socket binds to the loopback address, so nothing else on your LAN can use it unless you add `-g``.

## What is remote port forwarding, the `-R`` flag?

Remote forwarding is the mirror image. The SSH server opens the listening socket, and connections arriving there are pushed back down the session you already established, to a client that then connects to a local address. `ssh -R 8080:localhost:3000 user@server`` publishes port 8080 on the server and points it at port 3000 on your laptop. This is the reverse tunnel pattern that makes a machine behind NAT reachable without opening any inbound port, because the connection your client dialed out is the one carrying traffic back in. The usual surprise: sshd binds remote-forwarded sockets to loopback only, so the port is not reachable from the internet until an administrator sets `GatewayPorts`` in sshd_config.

## What is dynamic port forwarding, the `-D`` flag?

Dynamic forwarding turns the SSH client itself into a local SOCKS proxy. With `ssh -D 1080 user@server``, there is no fixed destination: each application connection tells the proxy where it wants to go, and the SSH server makes that outbound connection from its own network position. It only affects applications you actually point at the proxy, so it is not a system-wide VPN, and you should enable remote DNS resolution over SOCKS5 or your hostname lookups will still happen locally and leak where you are browsing.

## Which forwarding flag do I need?

The question that decides it is which end opens the listening socket. If you are reaching in, use `-L``. If you are publishing something out, use `-R``.

| Flag | Who listens | Example | Reach for it when |
| --- | --- | --- | --- |
| `-L` | Your machine | `-L 5432:db.internal:5432` | A private database or admin panel is only reachable from the server. |
| `-R` | The SSH server | `-R 8080:localhost:3000` | Something on your laptop, behind NAT, has to be reachable from outside. |
| `-D` | Your machine, as SOCKS | `-D 1080` | Many destinations, decided per connection by the application. |

## When does SSH tunneling stop being enough?

The first requirement is a server with a public address and a shell account on it, which is already more infrastructure than most people wanted. Even with one, `-R`` publishes a bare TCP port, so what you can share is `http://server:8080`` with no certificate and no hostname of its own; browsers warn, and webhook providers that require HTTPS simply refuse. Plain `ssh`` also does not come back on its own when your laptop sleeps or switches networks, which is why people wrap it in autossh. If several projects need URLs at once, you are hand-managing ports, DNS records and a TLS-terminating reverse proxy, and at that point you have built a small tunneling service.

## Is SSH tunneling secure?

The tunnel is, as far as it goes. Traffic between client and server is encrypted, and both ends are authenticated by host key and user credentials. What is not protected is everything past the SSH server: a `-L`` forward hands plaintext to the database over the server's own network, and a `-R`` forward with GatewayPorts enabled publishes an unencrypted port that anyone who finds it can connect to, with only whatever authentication your app already had. Server-side, leaving `AllowTcpForwarding`` on means any account with SSH access can pivot to other hosts on that network, which is why hardened bastions turn it off for most users.

## How does tunnels.io compare to an SSH tunnel?

tunnels.io does the same job as `ssh -R``, without the server. There is nothing to rent, no GatewayPorts to argue about with an administrator and no TLS to terminate yourself: run `tunnels localhost:3000`` and you get a public HTTPS URL with a valid certificate, from a client that reconnects when your machine wakes up. The free Basic plan is HTTP only, with 4 GB a month, one tunnel and a random URL; Pro at $4 a month adds up to three reserved subdomains, five tunnels, 128 GB and TCP tunnels for things like Postgres. Like SSH forwarding, the engine carries TCP and HTTP/HTTPS, not UDP. If you would rather stay in SSH entirely, Serveo is a hosted service driven by `ssh -R`` with no client to install; [our Serveo comparison](https://tunnels.io/compare/serveo) lays out where each one fits.

[Get started free →](https://tunnels.io/get-started) [tunnels.io vs Serveo](https://tunnels.io/compare/serveo)

## Related reading

[SSH into a machine behind NAT Using a hosted TCP tunnel as the transport instead of running a reachable server of your own.](https://tunnels.io/use-cases/ssh) [What is tunneling? The general idea SSH forwarding is one example of: carrying one protocol inside another.](https://tunnels.io/learn/what-is-tunneling) [What is a reverse tunnel? What ssh -R is doing, and why it works from behind NAT.](https://tunnels.io/learn/what-is-a-reverse-tunnel) [How to expose localhost to the internet SSH forwarding compared against router port forwarding, a tunnel service and just deploying.](https://tunnels.io/learn/how-to-expose-localhost) [All Learn articles Every concept and how-to in the series, in one place.](https://tunnels.io/learn)

## Frequently asked questions

### Is SSH tunneling the same as a VPN?

No. A VPN attaches your machine to a remote network at the IP layer, with routes and addresses, so every application is affected without being told. SSH forwarding moves individual TCP connections between specific sockets, and only the applications you point at those sockets are involved. Dynamic forwarding with SOCKS comes closest, but it still only serves applications configured to use the proxy.

### Can SSH tunnel UDP traffic?

Not with the port forwarding flags. Local, remote and dynamic forwarding all carry TCP, and OpenSSH's SOCKS proxy does not implement the UDP associate command, so DNS and QUIC will not travel through a `-D`` proxy. OpenSSH does have a separate tun device mode, `-w``, but it needs PermitTunnel enabled and elevated privileges on both ends, and it is a VPN-style layer 3 link rather than port forwarding.

### Why is my ssh -R port not reachable from outside the server?

Because sshd binds remote-forwarded ports to the loopback address by default. Until GatewayPorts is enabled in sshd_config, the forwarded port answers only to processes on the server itself, which is why curl works over SSH and your phone gets nothing. If you cannot change sshd_config, the fallback is to run your own reverse proxy on the server that listens publicly and forwards to the loopback port. A host firewall or cloud security group can block it as well.

### Does an SSH tunnel give me an HTTPS URL?

No. SSH encrypts the hop between client and server, but what it publishes on the far end is a plain TCP port on that server's address, with no certificate and no hostname of its own. To turn that into a real HTTPS URL you still need a DNS record, a certificate and a reverse proxy in front of the forwarded port. Anything that insists on HTTPS, including most webhook providers, will refuse the raw port.

### How do I stop an SSH tunnel from dropping?

Set ServerAliveInterval so idle sessions are not culled by a NAT device or firewall, and add ExitOnForwardFailure so the session fails loudly instead of connecting without the forward you asked for. Neither of those reconnects after a network change, so people run autossh or a systemd unit with a restart policy. A dropped tunnel is silent by design, which is the failure mode to plan around.

## Skip the bastion, keep the tunnel.

One command puts your local server on a public HTTPS URL. No server to rent, no certificate to renew, no credit card for the free plan.

[Get started →](https://tunnels.io/get-started) [Back to Learn](https://tunnels.io/learn)
