Linux

Setting Up SSH Tunnels: Secure Port Forwarding and Traffic Routing

In this guide, you'll learn how to create local, remote, and dynamic SSH tunnels. Get ready-to-use commands and understand how to securely route traffic over an encrypted connection.

Updated at April 5, 2026
15-20 min
Medium
FixPedia Team
Применимо к:Ubuntu 20.04 / 22.04 LTSDebian 11 / 12RHEL / AlmaLinux 9+OpenSSH 8.0+

Introduction / Why You Need This

SSH tunneling allows you to securely transmit unencrypted traffic over an existing SSH connection. It provides secure access to internal databases, admin panels, or network resources without exposing them directly to the internet. This is a standard tool for system administrators and developers to establish temporary secure access, bypass corporate network restrictions, and test services.

Requirements / Preparation

Before running the commands, ensure the following baseline conditions are met:

  • The openssh-client and openssh-server packages are installed on both your local machine and the remote host.
  • You have working SSH access to the server (key-based authentication is preferred over passwords).
  • You know the server's IP address or domain name, as well as the ports of the target internal services.
  • Your local firewall does not block the ports you choose, and port 22 is open on the server.

Step 1: Local Port Forwarding

A local tunnel accepts connections on your computer, encrypts them, and forwards them to the remote server, which then delivers them to the target host. Use this mode when the required service is only accessible within the server's local network or bound to 127.0.0.1.

ssh -L 9090:10.0.0.5:3306 user@remote-server.com -N -f

Parameter breakdown:

  • -L 9090:10.0.0.5:3306 — binds your local port 9090 to 10.0.0.5:3306 on the server's network.
  • -N — prevents opening a remote shell, keeping only the tunnel active.
  • -f — sends the process to the background after password entry or key verification.

You can now connect to the remote database using mysql -u root -p -h 127.0.0.1 -P 9090. All traffic will be encrypted.

Step 2: Remote Port Forwarding

The reverse scenario: you need to make a port on your local machine accessible to other users via an external server. This is useful for quickly demonstrating a local web application or API.

ssh -R 8888:localhost:3000 user@remote-server.com -N -f

By default, the server will bind port 8888 only to 127.0.0.1. To make the tunnel accessible via the server's external IP, add the line GatewayPorts clientspecified to /etc/ssh/sshd_config on the remote server, then restart the daemon: sudo systemctl restart sshd.

⚠️ Important: Before running the command, ensure that port 8888 is free on the server. Otherwise, you will get a bind: Address already in use error and the tunnel will fail to create.

Step 3: Dynamic Proxy (SOCKS5)

A dynamic tunnel turns your SSH client into a SOCKS5 proxy. You can route web traffic through it to bypass local network restrictions without manually specifying each destination address.

ssh -D 1080 user@remote-server.com -N -f

To use it, configure your client application:

  • In Chromium/Firefox browsers, go to network settings and specify 127.0.0.1 port 1080, type SOCKS v5. Be sure to enable the "Proxy DNS" or "Remote DNS" option, otherwise DNS leaks will compromise your security.
  • For terminal usage, wrap your commands with proxychains or tsocks, after configuring /etc/proxychains.conf accordingly.

Verifying the Setup

You can verify that the tunnels are working correctly with a single command:

ss -tulpn | grep -E '9090|8888|1080'

The output will show the processes listening on the specified ports and confirm they are bound to 127.0.0.1 or ::1. To test data transmission, run curl -v http://127.0.0.1:9090 or open the target resource in your browser.

To make the tunnel survive network drops, use autossh:

autossh -M 0 -N -f -L 9090:127.0.0.1:80 user@remote-server.com

The -M 0 flag disables autossh's built-in monitoring and delegates connection health checks to SSH keepalives.

Troubleshooting

  • Connection refused when connecting to the local port. Check if the background SSH process has terminated. Ensure you are connecting to 127.0.0.1 and not localhost (some systems resolve localhost to ::1, where the port isn't bound).
  • Tunnel drops during long periods of inactivity. Add ServerAliveInterval 60 and ServerAliveCountMax 3 to your ~/.ssh/config. The client will start sending keepalive pings every 60 seconds and will attempt to reconnect if it stops receiving responses.
  • IPv6 binding causes conflicts. If your network heavily relies on IPv6, explicitly specify the protocol version in your command: ssh -4 -L ... or add AddressFamily inet to your SSH configuration file.

F.A.Q.

Why does the SSH tunnel close immediately after creation?
Can I forward UDP traffic over standard SSH?
How do I enable tunnel creation on the server side?

Hints

Prepare the Environment
Create a Local Tunnel (-L)
Set Up a Dynamic Proxy (-D)
Verify and Maintain the Connection

Did this article help you solve the problem?

FixPedia

Free encyclopedia for fixing errors. Step-by-step guides for Windows, Linux, macOS and more.

© 2026 FixPedia. All materials are available for free.

Made with for the community