A tunnel comes up, the client can ping the gateway, and every web request through the proxy path dies. The redsocks process is running and its port is listening, so the service looks healthy from the gateway itself. What fails is the delivery step: a packet redirected from the tunnel does not land on the socket you started. The bind address and the redirect target have to agree, and only one of them is usually checked.

Where a redirected packet actually lands

Transparent proxying on a gateway uses a nat table REDIRECT in PREROUTING to rewrite the destination of an incoming connection to a local port. The rewritten destination is a local address as seen from the interface the packet arrived on, not the loopback address of a process that started somewhere else. A listener bound only to 127.0.0.1 accepts connections that were already local to the host, which is exactly the case that keeps working while tunnel traffic fails. The client's own loopback is a third address and is never the target of the gateway's rule.

So before touching upstream credentials, establish two facts: which local address the rule rewrites to, and which address the proxy socket is bound to.

Read the listener and the redirect rule together

Print the socket table and the nat rules in the same pass, because a rule that points at a port nobody owns looks identical to a rule that points at the wrong address. sudo ss -ltnp | grep redsocks shows the bind address and the owning process. sudo iptables -t nat -S PREROUTING shows the REDIRECT target, and sudo iptables -t nat -L PREROUTING -v -n shows whether anything matched it.

A jump to a user chain named REDSOCKS accomplishes nothing unless that chain exists and carries the intended exclusions and the REDIRECT action. Read the chain it jumps to with sudo iptables -t nat -S REDSOCKS rather than assuming the name does the work.

Then compare the two values. If the rule redirects to a gateway address on port 12345 and the listener reports 127.0.0.1:12345, the mismatch explains the failure without any further diagnosis.

Bind narrowly, then constrain what can reach it

The upstream sample configuration defaults the listener to loopback for a good reason: a transparent proxy accepts connections on behalf of whoever can reach it. Changing the bind to 0.0.0.0 fixes delivery and simultaneously opens the port on every interface, which is a security change, not a neutral repair. Prefer binding to the specific local address the redirect targets, and keep the port closed on untrusted interfaces. If a wider bind is unavoidable, add a host firewall rule that permits only the tunnel source range and the gateway's own local traffic.

Opening the port on the public interface is never required for redirected VPN traffic, because the connection terminates locally after the rewrite. Do not expose the proxy to the internet to make a local redirect work.

Prove it with a new connection from a client

Test from an actual VPN client with a fresh TCP connection, not from a shell on the gateway. curl -sS -o /dev/null -w '%{http_code}\n' http://example.com on the client exercises the redirected path end to end.

Then confirm that a direct connection to the listener from an untrusted network is still refused, and that the proxy's own upstream connection is not being captured again by the same rule. Recursive capture shows up as a proxy that connects to itself and never completes a request. Add the proxy's destination to the exclusion set, or mark its outgoing packets so the chain skips them.

Finally, remember what this path does not cover. Ordinary redsocks TCP redirection does not turn UDP, QUIC, or DNS into SOCKS traffic, and private destinations usually need explicit exclusions. A successful local test only proves the loopback path that was never the problem.

Winndannde jowitiiɗe heen