How to Connect SSH Through a SOCKS5 Proxy
October 20, 2026
SSH does not have a native socks5 option. The usual solution is to make OpenSSH start a SOCKS5 connector as its ProxyCommand. The connector authenticates to the proxy, asks it to open a TCP connection to the SSH server, and copies bytes between OpenSSH and that connection. The SSH handshake and application data remain encrypted, although the proxy operator can normally see destination metadata such as the hostname and IP address.
This pattern is useful when direct SSH egress is blocked, a provider requires traffic to originate from an allowlisted address, or you need to test whether a server accepts SSH from a controlled proxy egress. It differs from an SSH jump host: a jump host is another SSH server, while SOCKS5 is simply the transport used to reach the destination.
Before connecting
Check the following before changing the SSH configuration:
- The endpoint must support SOCKS5
CONNECTrequests for TCP traffic. A UDP-only SOCKS service cannot carry SSH. - The proxy account must allow the destination port, normally
22, or a provider-defined port range. - Your proxy client must support SOCKS5. OpenBSD
nc, Ncat, and dedicated SOCKS connectors generally do. - Decide whether the proxy or the SSH client should resolve the destination hostname.
- Keep SSH public-key authentication and host-key verification enabled. A proxy changes the network path, not the trust model.
Use the proxy only for systems and accounts you are authorized to access.
Verify the proxy in two stages
First, test basic TCP reachability to the proxy listener:
nc -vz proxy.example.net 1080
A successful result proves that the host and port are reachable, but it does not prove that the service speaks SOCKS5. Test the SOCKS handshake with cURL:
curl --socks5-hostname proxy.example.net:1080 --head --connect-timeout 10 --max-time 20 https://example.com
--socks5-hostname asks the proxy to resolve the destination name. This differs from curl --socks5, which resolves it locally. For an authenticated endpoint, add the provider's supported proxy-user option and use an approved secret-handling method:
curl --socks5-hostname proxy.example.net:1080 --proxy-user "$SOCKS_USERNAME" --head --max-time 20 https://example.com
Avoid putting username:password directly in a URL because it can leak through shell history, logs, or the process list.
Configure OpenSSH with ProxyCommand
Create a dedicated entry in ~/.ssh/config:
Host app-bastion
HostName bastion.example.net
User deploy
ProxyCommand /usr/bin/nc -X 5 -x proxy.example.net:1080 %h %p
ConnectTimeout 10
ServerAliveInterval 30
ServerAliveCountMax 3
ExitOnForwardFailure yes
Then connect normally:
ssh app-bastion
The %h and %p placeholders are replaced with the destination hostname and port. They prevent the command from accidentally targeting the proxy itself. ServerAliveInterval is especially useful when the proxy closes idle tunnels: encrypted SSH keepalives keep the connection active.
For a one-off connection, place the same command in an -o option:
ssh -o 'ProxyCommand=/usr/bin/nc -X 5 -x proxy.example.net:1080 %h %p' deploy@bastion.example.net
Not every nc build supports SOCKS. If -X is unavailable, install Ncat and test its equivalent command:
ProxyCommand /usr/bin/ncat --proxy proxy.example.net:1080 --proxy-type socks5 %h %p
Check nc -h or ncat --help on the actual machine rather than assuming the installed variant supports the same options.
Control DNS behavior
SOCKS5 can carry a domain name, so the proxy normally performs the destination lookup. The nc -X 5 path generally sends the hostname to the SOCKS server, while cURL's --socks5-hostname explicitly guarantees proxy-side resolution. OpenSSH may still perform local canonicalization or host-key lookups, depending on the client configuration.
This distinction matters for internal targets. A public SOCKS server usually cannot resolve db.internal.example.com through your corporate DNS. In that case, connect to a bastion that has internal DNS access and forward the database port from that bastion.
If the proxy returns a resolved IPv4 address but the destination is IPv6-only, temporarily test with AddressFamily inet in the SSH configuration. Remove that setting when the target genuinely requires IPv6; forcing IPv4 can hide the real routing problem.
Handle proxy authentication securely
SOCKS authentication happens before the SSH handshake. OpenSSH has no standard ProxyUsername setting because credentials are handled by the command in ProxyCommand.
Ncat builds that support it can use an option such as:
ProxyCommand /usr/bin/ncat --proxy proxy.example.net:1080 --proxy-type socks5 --proxy-user proxy-user %h %p
For unattended systems, use a connector or wrapper that reads credentials from a protected file, system keyring, or secret broker. Do not commit credentials to ssh_config, place them in a URL, or print them in diagnostics. IP allowlisting can be simpler when the same controlled egress address is used consistently.
An HTTP 407 Proxy Authentication Required response usually means the endpoint is an HTTP proxy rather than a SOCKS5 proxy. SOCKS authentication failures vary by client and may appear as a generic authentication error or an HTTP-style 403 response.
Forward a database through the SSH connection
A common use case is exposing an internal PostgreSQL or MySQL service through a bastion reached via SOCKS5. With the app-bastion entry above, create a local forward:
ssh -N -L 127.0.0.1:15432:db.internal.example.com:5432 app-bastion
The bastion resolves and connects to the database, while the client connects to the local loopback port:
psql "host=127.0.0.1 port=15432 dbname=analytics user=readonly"
Binding to 127.0.0.1 prevents the database port from being exposed on every network interface. ExitOnForwardFailure yes causes SSH to stop if the local listener cannot be created. It does not guarantee that the remote database is healthy; test the application connection separately.
Diagnose connection failures
Use verbose SSH output when the cause is unclear:
ssh -vvv app-bastion 2>ssh-debug.log
Review the log locally and remove it afterward because connection logs can expose usernames, internal hostnames, and authentication metadata. On the remote server, printenv SSH_CLIENT shows the source address that reached SSH, which should normally be the proxy's egress IP.
Common symptoms
Connection refused: the proxy listener is down, the port is wrong, or a firewall rejected the connection.invalid option -- X: the installednclacks SOCKS support. Use Ncat or another compatible connector.403 Forbiddenor an authentication error: check the proxy account, password, plan restrictions, and IP allowlist.Could not resolve hostname: determine whether the local client or proxy performed the lookup. Private names usually require a bastion with internal DNS.- A successful SOCKS handshake followed by a timeout: the proxy cannot reach the SSH port, or the target firewall blocks the proxy's egress address.
kex_exchange_identification: traffic reached the wrong service, the target port is incorrect, or the server closed the connection before the SSH exchange.Host key verification failed: confirm that the proxy is routing to the expected server. Investigate the address and certificate fingerprint rather than disabling the check.- Intermittent disconnects: compare the proxy idle timeout with
ServerAliveInterval, and check whether the endpoint changes addresses under load. channel open failedduring forwarding: verify that the bastion can resolve and reach the requested internal host and port.
Production checklist
Before standardizing the setup, use dedicated proxy credentials where possible, restrict destination access if the provider supports it, and keep SSH keys and host-key verification enabled. Do not enable agent forwarding unless the workflow genuinely requires it. OpenSSH does not automatically fail over between two ProxyCommand endpoints, so define separate host aliases or manage that behavior outside the client.
Finally, monitor proxy latency, authentication failures, and target reachability without logging secrets. This approach is most reliable when the SOCKS5 endpoint, DNS behavior, and SSH trust settings are tested together rather than treated as separate configuration problems.