SSH SOCKS proxy tunnel: how to browse through a remote server from the terminal
Learn how to create an SSH SOCKS proxy tunnel to browse through a remote server: command, browser config, autostart, and DNS leak troubleshooting.
An SSH SOCKS proxy tunnel turns any SSH-capable server into an exit point for all your browser’s HTTP/HTTPS traffic. You open an SSH connection with the -D flag pointing to a local port, and the OpenSSH client starts listening on that port as if it were a native SOCKS5 proxy — any program configured to use 127.0.0.1:1080 sends packets through the encrypted tunnel and exits to the internet with the remote server’s IP.
The most common use case is debugging region-locked applications: you need to validate how your site renders for a user in another country, check whether the CDN is serving the correct regional cache, or simulate the experience of a customer behind a different route. Other typical scenarios: accessing admin panels restricted by IP allowlist (you only whitelist the VPS IP), connecting to private network resources without exposing additional ports, or simply browsing through a connection you consider more trustworthy than a public Wi-Fi.
This tutorial covers the complete setup in ~15 minutes: creating the tunnel, configuring the browser (Firefox and Chrome), validating with an IP check, preventing DNS leaks, and making the tunnel persistent with autossh and systemd.
Prerequisites
You need functional SSH access to a Linux server — a standard VPS, with no special configuration. The OpenSSH server ships pre-installed on any standard distribution (Ubuntu, Debian, Rocky, AlmaLinux) and accepts port forwarding by default. Root privileges on the remote server aren’t required; a regular user with shell login is enough.
OpenSSH client on your desktop (Linux/macOS native; Windows 10/11 via PowerShell or WSL2), remote server with SSH active and access via key or password, and port 22 reachable. The server doesn’t need any extra configuration — port forwarding is enabled by default in sshd_config.
Typical data to build the tunnel:
203.0.113.42 deploy 22 1080 Creating the SOCKS5 tunnel
The ssh -D flag is shorthand for “dynamic port forwarding” and enables SOCKS mode on the client. Unlike -L (forward of a specific port) or -R (reverse forward), -D creates a complete proxy that accepts connections to any host:port destination.
Open a local terminal and run the tunnel command:
ssh -D 1080 -N -C [email protected]-D 1080— opens the SOCKS5 port on127.0.0.1:1080-N— doesn’t execute a remote command (just keeps the tunnel up)-C— enables compression (useful on slow links, irrelevant on fiber)
The terminal will look frozen after authentication — that’s normal. The process is active, holding the tunnel. Don’t close this window while you’re using the proxy.
In another terminal, confirm that the port is listening locally:
ss -tlnp | grep 1080The expected output is a line like LISTEN 0 128 127.0.0.1:1080 ... indicating that the SSH client is bound to the port. If nothing shows up, the tunnel didn’t come up — go back to the previous terminal and check for an error message.
By default the tunnel only listens on 127.0.0.1, which is correct for personal use. If you want to share the proxy with another machine on your LAN, use -D 0.0.0.0:1080 — but enable a local firewall so you don’t expose the proxy to the entire internet.
Configuring the browser
The tunnel is running; now you need to point the browser to it. Firefox and Chrome take different paths — Firefox has its own proxy independent of the system, Chrome uses the operating system’s proxy unless you force it via a flag.
Firefox
Open about:preferences in Firefox, scroll down to “Network Settings” and click “Settings…”. Select “Manual proxy configuration” and fill in:
- SOCKS Host:
127.0.0.1Port:1080 - Check “SOCKS v5”
- Check “Proxy DNS when using SOCKS v5” — essential to prevent leaks
Save and close. Firefox immediately starts routing all traffic through the tunnel.
Confirm in about:config that network.proxy.socks_remote_dns is set to true. This preference ensures DNS queries also go through the tunnel — without it, Firefox resolves hostnames using the local resolver before sending the request through the proxy, leaking your activity to your ISP.
Chrome / Chromium
Chrome ignores tab-level proxy configuration — it uses the system configuration. To force SOCKS5 without changing the entire system, open the browser with a flag:
Close all Chrome windows and run from the command line:
google-chrome \
--proxy-server="socks5://127.0.0.1:1080" \
--host-resolver-rules="MAP * ~NOTFOUND , EXCLUDE 127.0.0.1"The second flag forces DNS resolution via SOCKS (equivalent to Firefox’s socks_remote_dns). Without it, Chrome does a local lookup before tunneling.
Verification
The simplest way to confirm that traffic is exiting through the VPS is to check the public IP the browser sees.
In the configured browser, open https://ifconfig.me or https://ipinfo.io. The returned IP should be your VPS’s public IP, not your home connection’s IP.
To compare side by side: open https://ipinfo.io in a browser WITHOUT a proxy and copy the result. Then open the same URL in the browser with the tunnel active. The two IPs must be different — and the second one should match the IP from the curl ifconfig.me command run directly on the VPS.
Test for DNS leaks by visiting https://dnsleaktest.com and running the extended test. The returned resolvers should be those of the VPS provider (typically Cloudflare 1.1.1.1, Google 8.8.8.8, or the datacenter’s DNS), not your local ISP’s. If servers from your home provider show up, the remote DNS step didn’t take effect — review the browser configuration.
Even with the SOCKS proxy active, the browser’s WebRTC can reveal your local IP via STUN requests. In Firefox, disable it in about:config by setting media.peerconnection.enabled=false. In Chrome, install the “WebRTC Network Limiter” extension and configure it to route through the proxy. Sites like browserleaks.com/webrtc show exactly what leaks.
Persistent tunnel in the background
Keeping a terminal open for the tunnel is fragile — any accidental close drops the connection. For recurring use, run it in the background and configure automatic reconnection.
The simplest way to send the tunnel to the background is with the -f flag:
ssh -fND 1080 [email protected]The process detaches from the terminal and keeps running. To kill it later, find the PID:
pgrep -f "ssh -fND 1080"
kill <PID>Limitation: if the network connection drops (Wi-Fi disconnects, modem reboots), the tunnel dies and doesn’t come back on its own.
For automatic reconnection, install autossh:
sudo apt install autossh # Debian/Ubuntu
sudo dnf install autossh # Rocky/Alma
brew install autossh # macOSAnd run:
autossh -M 0 -fND 1080 \
-o "ServerAliveInterval 30" \
-o "ServerAliveCountMax 3" \
[email protected]-M 0 disables autossh’s monitor port and delegates failure detection to SSH’s own keepalives. The ServerAliveInterval=30 and ServerAliveCountMax=3 parameters drop the connection if 90 seconds pass without a response, triggering a reconnect.
To keep the tunnel running as a boot service on Linux, create a systemd unit at ~/.config/systemd/user/ssh-socks.service:
[Unit]
Description=SSH SOCKS5 tunnel via [email protected]
After=network-online.target
[Service]
ExecStart=/usr/bin/autossh -M 0 -NTD 1080 -o ServerAliveInterval=30 -o ServerAliveCountMax=3 [email protected]
Restart=always
RestartSec=10
[Install]
WantedBy=default.targetEnable it with:
systemctl --user daemon-reload
systemctl --user enable --now ssh-socksStatus is visible via systemctl --user status ssh-socks. For this to work without a password prompt, you need SSH key authentication, and the key must be loaded in ssh-agent or have an empty passphrase (not recommended on a shared desktop).
Troubleshooting
”channel 1: open failed: connect failed: Connection refused”
This message appears in the SSH terminal when the remote server tries to open a connection to the destination requested by the browser and fails. Common cause: the destination blocks traffic from the datacenter’s IP range (Cloudflare WAF, fail2ban on the destination), or the remote server has no IPv6 route and the browser is trying IPv6. Palliative fix: force IPv4 in the browser or in SSH with -4.
Slow connection after a while
SSH tunnels are single-stream TCP and suffer from the TCP-over-TCP penalty when there’s packet loss. If the connection is fine for a few minutes and then degrades, switch to a less-congested port (ssh -p 443 if the server is configured for it) or consider using mosh for the shell and a dedicated SOCKS tunnel in a separate session.
Tunnel drops every 5 minutes
The remote sshd is likely applying ClientAliveInterval and dropping idle sessions. Add -o "ServerAliveInterval=60" on the client — keepalive messages every 60 seconds keep the server convinced the connection is alive.
Next steps
With SOCKS5 working, you can evolve the setup in a few directions: configure a PAC file to enable the proxy only for specific domains (rather than full routing), use sshuttle as an alternative that captures traffic at the network level without configuring each application, or set up tinyproxy on the remote server to have a lighter HTTP proxy when you don’t need full SOCKS. For production debugging, it’s worth pairing the tunnel with mitmproxy running locally — you can inspect HTTPS leaving through the VPS without violating the destination’s TLS.
If you need a VPS dedicated exclusively to this kind of use (stable proxy, fixed IP, predictable latency), a Hostini VPS in the region closest to the destination reduces the overhead to a few milliseconds and eliminates the typical blocks tied to shared IPs.
Frequently asked questions
What's the difference between an SSH SOCKS proxy tunnel and a VPN?
A SOCKS proxy created via SSH operates at the application layer — only traffic from programs you explicitly configure (browser, curl, Slack) goes through the tunnel. A VPN operates at the network layer and captures every bit of system traffic, including DNS, games, and native clients. SSH SOCKS is lighter, requires no extra client, and uses only port 22 that's already open.
Does SOCKS5 over SSH protect DNS from leaking?
Only if you force DNS resolution through the proxy. By default, the browser may resolve hostnames locally before sending traffic through the tunnel — leaking your DNS to your ISP. In Firefox, enable `network.proxy.socks_remote_dns=true` in about:config. In Chrome, use the `--proxy-server="socks5://..."` flag, which already forces remote resolution.
Can I use SSH SOCKS5 to access databases on a private network?
Yes. Tools like DBeaver, TablePlus, and MySQL Workbench accept a SOCKS5 proxy in their connection settings. You expose local port 1080, configure the DB client to use 127.0.0.1:1080 as SOCKS5, and connect to the private host as if you were inside the server. A more direct alternative for a specific DB: a local tunnel `ssh -L 3306:private-db:3306`.
How much does latency increase with an SSH SOCKS proxy?
It adds the client-server round-trip on every request. If your VPS is in São Paulo (10ms latency) and you access a site in Frankfurt (180ms direct), the tunneled traffic becomes roughly 10 + 180 + 10 = 200ms. For normal browsing the impact is imperceptible; for streaming/gaming, choose a server close to the destination.
How do I keep the SSH SOCKS tunnel running after closing the terminal?
Use the `-f` flag (background) combined with `-N` (no remote command): `ssh -fND 1080 user@ip`. To run it as a persistent service that reconnects on its own after a network drop, use `autossh -M 0 -fND 1080 user@ip` or create a systemd unit with `Restart=always`.
Is it legal to use SSH SOCKS proxy to bypass geo-blocks?
It depends on the terms of service of the accessed platform. Technically it's legitimate encrypted SSH traffic, and no jurisdiction bans the protocol. But platforms like Netflix, banks, and online games frequently block access from datacenter IPs — you may face account blocks or persistent captchas. For professional debugging (validating CDN, testing geo-targeting) it's standard industry practice.