How to Set Up a SOCKS5 Proxy with SSH
SSH’s built-in dynamic port forwarding turns any SSH connection into a SOCKS5 proxy. Traffic you route through it exits at the remote server, not your local machine. Unlike a VPN, it works at the application level — you choose which apps use it — and requires no additional server software beyond sshd.
Table of Contents
- Basic SOCKS5 Proxy in One Command
- Configure Applications to Use the Proxy
- Persistent Connection with systemd
- Restricting the Server-Side Key
- Bind to a Specific Interface
- Multiple Hops (Jump Hosts)
- DNS Leak Check
- Comparing SSH SOCKS5 vs Other Options
- Advanced SSH Configuration for SOCKS Proxies
- Monitoring Proxy Health
- Debugging Connection Issues
- Application-Level Configuration Examples
- Performance Optimization
- Threat Model: SOCKS vs VPN vs Tor
- Security Hardening for Production
- Troubleshooting DNS Leaks with SOCKS5
- Related Reading
Basic SOCKS5 Proxy in One Command
# -D: dynamic port forwarding (SOCKS5 on local port 1080)
# -N: don't execute a remote command
# -f: fork to background after authentication
# -C: compress traffic
ssh -D 1080 -N -f -C user@your-server.example.com
# Verify the proxy is listening
ss -tlnp | grep 1080
# or
netstat -tlnp | grep 1080
The proxy now listens on 127.0.0.1:1080. Any app that supports SOCKS5 can route traffic through it.
Configure Applications to Use the Proxy
Firefox
- Settings → Network Settings → Manual proxy configuration
- SOCKS Host:
127.0.0.1, Port:1080 - Select SOCKS v5
- Check Proxy DNS when using SOCKS v5 — this prevents DNS leaks
curl
# Use the proxy for a single request
curl --socks5-hostname 127.0.0.1:1080 https://ifconfig.me
# Set as default in ~/.curlrc
echo 'socks5-hostname = 127.0.0.1:1080' >> ~/.curlrc
# Verify your exit IP matches the server
curl --socks5-hostname 127.0.0.1:1080 https://api.ipify.org
System-wide on Linux (environment variables)
# These are respected by most command-line tools
export ALL_PROXY=socks5h://127.0.0.1:1080
export HTTPS_PROXY=socks5h://127.0.0.1:1080
export HTTP_PROXY=socks5h://127.0.0.1:1080
# socks5h means the hostname resolution also happens through the proxy
Git
git config --global http.proxy socks5h://127.0.0.1:1080
git config --global https.proxy socks5h://127.0.0.1:1080
# Per-repo only
git config http.proxy socks5h://127.0.0.1:1080
Python requests
import requests
proxies = {
"http": "socks5h://127.0.0.1:1080",
"https": "socks5h://127.0.0.1:1080",
}
# Requires: pip install requests[socks]
response = requests.get("https://api.ipify.org", proxies=proxies)
print(response.text) # Should show your server's IP
Persistent Connection with systemd
For a proxy that survives reboots and reconnects automatically:
# /etc/systemd/system/ssh-socks-proxy.service
[Unit]
Description=SSH SOCKS5 Proxy
After=network.target
Wants=network.target
[Service]
Type=simple
User=yourlocaluser
ExecStart=/usr/bin/ssh \
-D 1080 \
-N \
-o ServerAliveInterval=60 \
-o ServerAliveCountMax=3 \
-o ExitOnForwardFailure=yes \
-o StrictHostKeyChecking=yes \
-i /home/yourlocaluser/.ssh/proxy_key \
user@your-server.example.com
Restart=on-failure
RestartSec=10
KillMode=process
[Install]
WantedBy=multi-user.target
# Enable and start
sudo systemctl daemon-reload
sudo systemctl enable --now ssh-socks-proxy
# Check status
sudo systemctl status ssh-socks-proxy
journalctl -u ssh-socks-proxy -f
Use a dedicated SSH key (proxy_key) for the proxy — separate from your login key. This key can be passphrase-free since it runs as a service, but restrict it server-side.
Restricting the Server-Side Key
On the remote server, limit what the proxy key can do:
# ~user/.ssh/authorized_keys on the server
# Prepend these options before the public key:
no-pty,no-X11-forwarding,no-agent-forwarding,no-user-rc,permitopen="*:*" ssh-ed25519 AAAA... proxy-key
The no-pty option prevents interactive shell access using this key. The key can only be used for forwarding.
Bind to a Specific Interface
By default, -D 1080 binds to 127.0.0.1 — local only. To share the proxy with other machines on your LAN:
# Bind to all interfaces (careful — firewalled network only)
ssh -D 0.0.0.0:1080 -N -f user@server
# Bind to LAN IP specifically
ssh -D 192.168.1.100:1080 -N -f user@server
If you share the proxy on your network, require authentication. The basic SOCKS5 proxy via SSH has no username/password layer — rely on network-level access controls.
Multiple Hops (Jump Hosts)
Route through an intermediate host before reaching the proxy server:
# Proxy via jump host
ssh -J jumpuser@jump.example.com -D 1080 -N -f proxyuser@destination.example.com
# Or in ~/.ssh/config
Host proxy-via-jump
HostName destination.example.com
User proxyuser
ProxyJump jumpuser@jump.example.com
DynamicForward 1080
IdentityFile ~/.ssh/proxy_key
# Then simply
ssh -Nf proxy-via-jump
DNS Leak Check
Confirm DNS queries go through the proxy, not your local resolver:
# With proxy active, check DNS from a remote perspective
curl --socks5-hostname 127.0.0.1:1080 https://1.1.1.1/cdn-cgi/trace | grep ip
# Check if DNS is leaking locally
# Run tcpdump locally while making a request through the proxy
sudo tcpdump -i any -n port 53 &
curl --socks5-hostname 127.0.0.1:1080 https://example.com
# If you see DNS queries in tcpdump, use socks5h:// (hostname resolution through proxy)
Comparing SSH SOCKS5 vs Other Options
| Method | Setup | Speed | Per-app | DNS leak protection |
|---|---|---|---|---|
| SSH SOCKS5 | Minutes | Fast | Yes | With socks5h |
| WireGuard VPN | More setup | Very fast | No (system-wide) | Built-in |
| Tor | Minutes | Slow | Via browser/torsocks | Built-in |
| HTTP proxy | Simple | Fast | Yes | No |
SSH SOCKS5 is the right choice when you already have a SSH server, need app-level proxying, and do not want to install VPN software.
Advanced SSH Configuration for SOCKS Proxies
For power users managing multiple proxies, ~/.ssh/config simplifies management:
# ~/.ssh/config for multiple SOCKS proxies
Host proxy-us
HostName us-server.example.com
User proxyuser
IdentityFile ~/.ssh/proxy_us_key
DynamicForward 127.0.0.1:1080
ServerAliveInterval 60
ServerAliveCountMax 3
ExitOnForwardFailure yes
StrictHostKeyChecking accept-new
Host proxy-eu
HostName eu-server.example.com
User proxyuser
IdentityFile ~/.ssh/proxy_eu_key
DynamicForward 127.0.0.1:1081
ServerAliveInterval 60
ServerAliveCountMax 3
Host proxy-asia
HostName asia-server.example.com
User proxyuser
IdentityFile ~/.ssh/proxy_asia_key
DynamicForward 127.0.0.1:1082
ProxyJump jumphost.example.com
Connect using:
# Opens SOCKS proxy on :1080
ssh -N proxy-us
# Open proxy-eu on different port
ssh -N proxy-eu
Monitoring Proxy Health
Ensure your proxy remains connected and responsive:
#!/bin/bash
# Monitor SOCKS proxy health
PROXY_PORT=1080
CHECK_INTERVAL=30
while true; do
# Test connectivity through proxy
response=$(curl -m 5 --socks5-hostname 127.0.0.1:$PROXY_PORT https://api.ipify.org 2>/dev/null)
if [ $? -eq 0 ]; then
echo "Proxy healthy: $response"
else
echo "Proxy failed at $(date)"
# Reconnect
ssh -N -D $PROXY_PORT user@server &
fi
sleep $CHECK_INTERVAL
done
Debugging Connection Issues
When the proxy fails to connect, these debugging steps help:
# 1. Verify SSH connection works
ssh -v user@server 'echo "SSH OK"'
# 2. Check if proxy port is listening
ss -tlnp | grep 1080
# 3. Test DNS through proxy
curl --socks5-hostname 127.0.0.1:1080 https://1.1.1.1/cdn-cgi/trace
# 4. Verify traffic goes through proxy with tcpdump
sudo tcpdump -i any 'dst port 1080' -A
# 5. Check for IPv6 leaks (if proxy is IPv6-capable)
curl -6 --socks5-hostname 127.0.0.1:1080 https://ifconfig.co/json
# 6. Test with specific ciphers if connection fails
ssh -c aes256-gcm@openssh.com -D 1080 user@server
Application-Level Configuration Examples
Rust/Cargo
# ~/.cargo/config.toml
[net]
git-fetch-with-cli = true
# Set environment variables
export ALL_PROXY=socks5h://127.0.0.1:1080
cargo fetch
Node.js/npm
# ~/.npmrc
https-proxy=socks5h://127.0.0.1:1080
proxy=socks5h://127.0.0.1:1080
# Or via command line
npm install --https-proxy socks5h://127.0.0.1:1080
Docker
# Configure Docker daemon to use SOCKS proxy
# /etc/docker/daemon.json
{
"proxies": {
"https-proxy": "socks5h://127.0.0.1:1080"
}
}
sudo systemctl restart docker
docker pull alpine # Will route through proxy
Java Applications
# java -D flags for SOCKS proxy
java -DsocksProxyHost=127.0.0.1 \
-DsocksProxyPort=1080 \
-DsocksProxyVersion=5 \
-jar application.jar
Performance Optimization
SOCKS proxies can experience latency. Optimize for your use case:
# Test proxy latency
for i in {1..5}; do
time curl -s --socks5-hostname 127.0.0.1:1080 https://example.com > /dev/null
done
# Monitor connection quality
watch -n 1 'curl -s --socks5-hostname 127.0.0.1:1080 https://api.ipify.org'
Typical latencies:
- Same region (US to US): 5-30ms
- Adjacent region (US to EU): 80-150ms
- Distant region (US to Asia): 150-300ms
- High-latency links: 400ms+
For real-time applications (gaming, video), 200ms+ becomes noticeable.
Threat Model: SOCKS vs VPN vs Tor
Choose the right solution for your threat model:
| Threat | SOCKS5 SSH | VPN | Tor |
|---|---|---|---|
| ISP sees destination | No | No | No |
| Server sees your IP | No | No | No |
| Exit node logging | Depends on host | Depends on provider | No (distributed) |
| Latency | Low | Low | High |
| Bandwidth restriction | None | Often limited | Low |
| Setup complexity | Medium | Low | Low |
| Cost | Server cost | $5-15/mo | Free |
| App-level control | Yes | No | Browser only |
Choose SSH SOCKS5 if: You control both endpoints or trust your proxy host, need low latency, and want app-level control.
Choose VPN if: You need “set and forget” system-wide encryption without managing infrastructure.
Choose Tor if: You need maximum anonymity and don’t mind latency overhead.
Security Hardening for Production
If you’re running a proxy server for others:
# Restrict SSH key to SOCKS forwarding only
# /root/.ssh/authorized_keys (server-side)
restrict,pty,command="/bin/false" ssh-rsa AAAA...
# Lock down firewall
ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp from 203.0.113.0/24 # Your IP only
ufw enable
# Monitor proxy usage
netstat -an | grep ESTABLISHED | grep 127.0.0.1:1080 | wc -l
# Log all connections
socat - TCP-LISTEN:1080 | tee -a /var/log/socks-proxy.log
Troubleshooting DNS Leaks with SOCKS5
DNS can leak your real location even through a proxy. Test thoroughly:
# Test for DNS leaks
curl --socks5-hostname 127.0.0.1:1080 https://dnsleaktest.com/api/v1/my/nameservers
# Compare with direct connection
curl https://dnsleaktest.com/api/v1/my/nameservers
# They should show different DNS providers
# If leaking, use socks5h (h = hostname resolution through proxy)
curl -v --socks5h 127.0.0.1:1080 https://example.com