SSH Tunneling — Part 3/3: Dynamic Port Forwarding and SOCKS Proxies.

Learn how to route application traffic through an encrypted SSH connection using dynamic port forwarding (-D). Explore SOCKS proxies, browser configuration, command-line tools, DNS handling, troubleshooting, and security best practices.

SSH Tunneling — Part 3/3: Dynamic Port Forwarding and SOCKS Proxies.
Photo by yC — yannickcoffi · Prints · yC · yc.wed


In Part 1: Local Port Forwarding, we used SSH’s -L option to send traffic from a local port through the SSH server to a remote service.
In Part 2: Remote Port Forwarding, we used SSH’s -R option to make a local service accessible through a port on the remote server.

In this final part, we'll examine Dynamic Port Forwarding (-D), which turns an SSH connection into a SOCKS proxy.

Unlike -L and -R, which forward traffic to a predefined destination, Dynamic Forwarding allows compatible applications to request connections to different destinations through the same SSH tunnel.

This is useful for remote browsing, testing applications from another network perspective, and accessing services reachable from an SSH server.

1. What Is Dynamic Port Forwarding?

Dynamic port forwarding creates a local listening port that acts as a SOCKS proxy. Applications configured to use that proxy can send connection requests through SSH to different destinations.

For example:

Your Machine
     │
     ▼
127.0.0.1:1080
     │
     │ SOCKS proxy
     ▼
Encrypted SSH Tunnel
     │
     ▼
Remote SSH Server
     │
     ├──► example.com:443
     ├──► internal-server:8080
     └──► another-host:22

The destination is selected by the application for each connection rather than being fixed in the SSH command.

The key difference: local and remote forwarding define a destination in advance; dynamic forwarding lets the application specify the destination through the SOCKS protocol.

2. Creating a SOCKS Proxy with SSH

The basic syntax is:

$ ssh -N -D [BIND_ADDRESS:]LOCAL_PORT user@REMOTE_SERVER

For example:

$ ssh -N -D 127.0.0.1:1080 user@server.example.com

This creates a SOCKS proxy listening on 127.0.0.1:1080 on your local machine.

Understanding the options

  • -D: Enables dynamic port forwarding.
  • 127.0.0.1:1080: Specifies the local listening address and port.
  • -N: Prevents execution of a remote command or opening of an interactive shell.
  • user@server.example.com: Specifies the remote SSH server.

Once connected, compatible applications can send their traffic through the proxy.

Important: Creating the proxy does not automatically route all your computer's traffic through SSH. You must configure each application to use it, or use another tool that explicitly routes traffic through the SOCKS proxy.

3. Testing the SOCKS Proxy

After establishing the tunnel, test it with curl.

For example:

$ curl --proxy socks5h://127.0.0.1:1080 https://example.com

The request follows this path:

curl
  │
  ▼
127.0.0.1:1080
  │
  ▼
SSH Tunnel
  │
  ▼
Remote SSH Server
  │
  ▼
example.com:443

The socks5h:// scheme tells curl to resolve the destination hostname through the proxy rather than resolving it locally first.

This is useful when you want DNS resolution to occur from the remote side, particularly when accessing internal hostnames.

To compare, you can also use:

$ curl --proxy socks5://127.0.0.1:1080 https://example.com

With socks5://, hostname resolution may happen locally, depending on how the application uses the SOCKS proxy. With socks5h://, hostname resolution is delegated through the proxy.

4. Configuring a Browser to Use SSH

One of the most common uses for dynamic port forwarding is routing browser traffic through a remote server.

First, establish the tunnel:

$ ssh -N -D 127.0.0.1:1080 user@server.example.com

Then configure your browser to use the SOCKS proxy.

For browsers that support manual proxy configuration, enter:

SettingValue
Proxy typeSOCKS v5
Proxy hostname127.0.0.1
Proxy port1080
Proxy DNS requestsEnabled, if available

In Firefox, these settings are available under Settings → General → Network Settings → Manual proxy configuration. Select SOCKS v5 and enable DNS proxying where the option is available.

Once configured, compatible browser connections will travel through the SSH tunnel.

Browser
   │
   ▼
SOCKS5 Proxy
127.0.0.1:1080
   │
   ▼
SSH Tunnel
   │
   ▼
Remote Server
   │
   ▼
Destination Website

Other applications continue to use their normal network connections unless they are separately configured to use the proxy.

5. Accessing Internal Network Services

Dynamic forwarding becomes particularly useful when the remote SSH server has access to services on a private network.

Suppose your SSH server can reach:

10.10.20.50:8080
10.10.20.60:3000
internal.example.local:443

You can create a SOCKS proxy:

$ ssh -N -D 127.0.0.1:1080 user@bastion.example.com

Configure a SOCKS-compatible application to use 127.0.0.1:1080, then request the required internal destination.

The connection path becomes:

Your Machine
     │
     ▼
SOCKS Proxy
     │
     ▼
SSH Tunnel
     │
     ▼
Bastion Host
     │
     ├──► 10.10.20.50:8080
     ├──► 10.10.20.60:3000
     └──► internal.example.local:443

The SSH server must be able to resolve and reach the requested destinations, and its forwarding policies must permit the connections.

This technique can provide convenient access to multiple internal services without creating a separate static port forward for each one.

6. Local Binding and Security

By default, OpenSSH dynamic forwarding generally listens on the local loopback interface.

For example:

$ ssh -N -D 127.0.0.1:1080 user@server.example.com

The proxy is accessible from your own machine but is not normally exposed to other devices on your network.

You can verify the listening socket on Linux:

$ ss -lntp

On macOS:

$ lsof -nP -iTCP:1080 -sTCP:LISTEN

Avoid binding the proxy to 0.0.0.0 unless other devices genuinely need access.

For example:

$ ssh -N -D 0.0.0.0:1080 user@server.example.com

This requests a listener on all local IPv4 interfaces. Other machines may be able to use the proxy if network and firewall rules permit it.

An exposed SOCKS proxy can allow unauthorized users to route traffic through your SSH connection. Keep the listener restricted to loopback unless broader access is intentional and appropriately controlled.


7. Running the Proxy in the Background

If you want the proxy to remain active without occupying an interactive terminal, use -f:

$ ssh -f -N -D 127.0.0.1:1080 user@server.example.com

SSH moves into the background after authentication.

To terminate the tunnel, identify the relevant SSH process and stop it, or close the original terminal if you did not start it in the background.

For persistent use, you can manage the connection through a service manager or use a tool such as autossh to help restart failed connections.

For example:

$ autossh -M 0 -N -D 127.0.0.1:1080 user@server.example.com

You can add keepalive options to detect unresponsive connections:

$ autossh -M 0 -N -o ServerAliveInterval=30 -o ServerAliveCountMax=3 -D 127.0.0.1:1080 user@server.example.com

Keepalives help detect failures, while autossh provides connection-monitoring and restart behavior.

8. Troubleshooting Dynamic Port Forwarding

If the proxy does not work, verify the following.

1. Confirm that the SSH connection is established

Run SSH with verbose output:

$ ssh -vvv -N -D 127.0.0.1:1080 user@server.example.com

Look for authentication failures, forwarding restrictions, and connection errors.

2. Check that the local port is listening

On Linux:

$ ss -lntp

On macOS:

$ lsof -nP -iTCP:1080 -sTCP:LISTEN

3. Test the proxy with curl

$ curl --proxy socks5h://127.0.0.1:1080 https://example.com

If this fails, the error may indicate a local connection problem, a SOCKS negotiation failure, a DNS issue, or an unreachable destination.

4. Verify destination reachability.

The remote SSH server must be able to connect to the requested destination. Network restrictions, DNS configuration, firewalls, and SSH server forwarding policies can prevent access.

5. Check for port conflicts.

If another process already occupies port 1080, choose another local port:

$ ssh -N -D 127.0.0.1:1081 user@server.example.com

Then update your application's proxy settings to use port 1081.

9. Security Considerations

A SOCKS proxy over SSH is useful, but it is important to understand what it protects.

  • SSH encryption: Protects traffic between your local machine and the SSH server.
  • HTTPS: Continues to protect application traffic between your browser and the destination website.
  • DNS resolution: Can be delegated through the proxy when the application supports remote resolution.
  • Remote visibility: The SSH server can generally observe destination addresses and connection metadata.
  • Unencrypted traffic: Traffic that is not independently encrypted may be readable beyond the SSH endpoint.
  • Application scope: Only traffic actually routed through the proxy benefits from the SSH tunnel.

Dynamic forwarding is not a full VPN. It does not automatically route every application, UDP traffic, or all system networking through SSH.

Use trusted SSH servers, protect your credentials, restrict proxy access, and verify that the applications you care about are actually using the tunnel.



Quick Reference

Create a local SOCKS proxy

$ ssh -N -D 127.0.0.1:1080 user@server

Test the proxy with remote DNS resolution

$ curl --proxy socks5h://127.0.0.1:1080 https://example.com

Run the proxy in the background

$ ssh -f -N -D 127.0.0.1:1080 user@server

Enable SSH debugging

$ ssh -vvv -N -D 127.0.0.1:1080 user@server

Use a different local port

$ ssh -N -D 127.0.0.1:1081 user@server

The Mental Model

The three SSH forwarding modes serve different purposes:

-L  Local listener  → Remote destination
-R  Remote listener → Destination reachable from the SSH client
-D  Local SOCKS proxy → Dynamically selected destinations

With dynamic forwarding, the SSH command establishes the proxy, and compatible applications decide which destinations to reach through it.

The essential command is:

$ ssh -N -D 127.0.0.1:1080 user@server


Think of it as:
Create a local proxy, send its traffic through SSH, and let the application choose the destination.



SSH tunneling offers three flexible ways to connect services across network boundaries:

  • Local forwarding (-L) provides local access to remote services.
  • Remote forwarding (-R) exposes local services through a remote listener.
  • Dynamic forwarding (-D) routes compatible application traffic through a SOCKS proxy.

Together, these techniques provide practical solutions for remote administration, development, testing, and controlled access to private networks.

That wraps up our three-part SSH tunneling series, covering local forwarding, remote forwarding, and dynamic forwarding with SOCKS proxies.

We hope this series has helped you understand how to use SSH tunnels securely and effectively.

Keep learning. Keep exploring. Stay secure.

Keep Us Caffeinated  ⦿ ⦿
Spotify Logo
↑
SNUBmonkey Join our readers