Proxy Chain Setup: proxychains, Local Hops and When to Stop
How a multi-hop chain works on the wire, a proxychains-ng config, local hops for tools that cannot authenticate, and why a second hop rarely helps.
"Proxy chain" names two different setups, and which one you need decides whether the next hour is productive. The first is a multi-hop chain: your traffic enters proxy A, which forwards it to proxy B, which reaches the target, so the target sees B and B sees A. The second is a local hop: a small proxy on your own machine or network that a tool talks to without authentication, which then forwards to a provider gateway with the credential attached. If your problem is a tool that cannot carry a proxy credential, or a corporate proxy you must pass through, you need a local hop or a forced first hop, not a longer chain. This guide covers both setups, with configs you can run, and it is explicit about the point where a chain stops earning its latency.
One fact up front, because it shapes every decision below: a rotating or residential gateway is already a chain. Your request reaches the gateway, the gateway forwards it to an exit node, the exit reaches the target. Putting another provider's proxy in front of ours makes a three-hop chain out of a two-hop one, and the third hop adds a failure domain, a round trip, and nothing the target can see.
How a chain works on the wire
An HTTP proxy carries HTTPS by way of CONNECT host:port: the client asks the proxy to open a TCP tunnel, the proxy answers 200 Connection established, and the TLS handshake then runs end to end inside it. Chaining is that operation nested. The client sends CONNECT proxyB:port to proxy A; once that tunnel is up, the client sends CONNECT target:443 through it to proxy B; once that is up, TLS runs through both. Each hop sees only the address of the next hop and the bytes it is asked to relay. SOCKS5 nests the same way, with its own handshake at each hop.
Two consequences fall out of this. Authentication happens at every hop, independently, in whatever form that hop demands: a Proxy-Authorization header for an HTTP hop, a username and password exchange for a SOCKS5 hop. And DNS is resolved wherever you tell it to be. A chain that resolves the target name locally leaks the name to your own resolver before the first hop; one that resolves at the last hop does not. proxychains calls that setting proxy_dns, curl calls it the difference between socks5:// and socks5h://, and it is the easiest thing to get wrong.
proxychains-ng: the multi-hop tool
proxychains-ng works by preloading a library that intercepts the program's connect() calls and rewrites them through the chain, which is why it can wrap a binary that has no proxy setting at all. It is also why it cannot wrap everything. A statically linked binary, or a Go program, which makes its own system calls rather than going through libc, will ignore it and connect directly, with no error. Test with a program you know it handles, curl on any Linux distribution, before assuming a silent failure is the chain's fault.
The config file has three parts: a chain mode, options, and the proxy list. This one puts a corporate egress proxy first and the Node4 rotating gateway last:
# chain.conf.template
strict_chain
proxy_dns
tcp_read_time_out 15000
tcp_connect_time_out 8000
[ProxyList]
http 10.0.0.5 3128
socks5 GATEWAY_IP 1083 USERNAME PASSWORDstrict_chain uses every entry in order and fails if any hop is down. dynamic_chain skips dead hops, which sounds friendlier and is how a chain silently degrades to one hop without telling you. random_chain with chain_len is for anonymity research, not for a job whose exits you pay for by the gigabyte. The proxy list accepted only numeric addresses before proxychains-ng 4.14 and accepts hostnames only conditionally since, and our gateway hostnames resolve to addresses that can change, so resolve at run time and generate the file rather than pasting an address into a config that will outlive it:
GW=$(dig +short gw-rotating_datacenter.node4.io | head -1)
sed "s/GATEWAY_IP/$GW/" chain.conf.template > chain.conf
proxychains4 -q -f chain.conf curl -s https://api.ipify.orgRun the last line a few times. Each run should print an address from the rotating pool, and it should not be the corporate proxy's address. Drop -q and proxychains prints one line per hop as it negotiates, ending in OK or denied, which is the fastest way to see which hop refused you.
Our gateway hostnames resolve to more than one address, so head -1 pins one of them and gives up the round-robin. That is fine for a test and wrong for a fleet: spread the addresses across workers, or use a local hop, below, which resolves the name on every connection.
The local hop: what most people actually need
The common case is not two providers. It is one tool that cannot carry a proxy credential and one gateway that requires one. Browsers launched with --proxy-server, some SDKs, a CI job whose config file is checked in and must not contain a password: each of these wants a proxy URL with nothing between the scheme and the host. The fix is a local hop that accepts unauthenticated connections from your machine only and forwards them upstream with the credential attached.
In Node, the proxy-chain package does this in three lines, and Puppeteer proxy setup shows it wired into a browser launch. The general-purpose version is tinyproxy, a few hundred kilobytes of C that most package managers carry:
# /etc/tinyproxy/tinyproxy.conf
Port 8888
Listen 127.0.0.1
Allow 127.0.0.1
Upstream http USERNAME:PASSWORD@gw-rotating_datacenter.node4.io:8083
ConnectPort 443
ConnectPort 80Start it, then point anything at the local port with no credentials:
curl -x http://127.0.0.1:8888 https://api.ipify.orgThe credential now lives in one file with one owner, Listen 127.0.0.1 keeps the hop off the network, and every tool on the machine shares one rotation without ever seeing the password. If you also want to watch the traffic, mitmproxy's upstream mode does the same forwarding with a request log attached:
mitmdump --mode upstream:http://gw-rotating_datacenter.node4.io:8083 \
--upstream-auth USERNAME:PASSWORD --listen-port 8888Expect mitmproxy to intercept TLS by default, which means installing its certificate in the client or telling it which hosts to pass through untouched. For a plain forwarding hop, tinyproxy is the smaller tool and the smaller surprise.
Targeting still works through a local hop, because it rides in the username you put in the upstream line. USERNAME-country-us-session-a1b2 upstream gives every client behind the hop a United States exit pinned to one session; the segment grammar is in residential targeting and applies to the rotating tiers with the segments they support. Remember that the sticky window on rotating is counted from first assignment and does not extend with use, and that a hop shared by many workers puts all of their traffic on that one pinned exit.
An SSH tunnel is a chain too
The other local-hop pattern needs no proxy software at all. If you have a server with a fixed address, forward a local port to the gateway through it:
ssh -N -L 8083:gw-rotating_datacenter.node4.io:8083 user@jumphost
curl -x "http://USERNAME:PASSWORD@127.0.0.1:8083" https://api.ipify.orgThe gateway now sees the jump host's address on every connection, whatever your laptop's address is today. That is the arrangement that makes IP whitelisting usable from a machine that moves: register the jump host under Auth & IP Access once, and the credential in that curl line can go. The whitelist works on every product, and what you give up differs by product: on static datacenter proxies, shared and dedicated, nothing, since no request carries targeting; on the rotating tiers you keep country and session targeting by sending the username with an empty password; on the residential gateway you send no username and no password at all and accept an untargeted exit that rotates freely, because its targeting has nowhere to live without a username, so anything that pins a country, city or session there still authenticates with credentials. IP whitelisting best practices covers what an entry actually authorizes.
That last point generalizes. Whitelisting authenticates the last hop before us. Put a corporate proxy in front and the address we see is the corporate proxy's, which every colleague shares; whitelist it only if you are content for all of them to spend your allowance.
The corporate egress case
This is the chain people are forced into rather than choose: a workstation that can only reach the internet through a mandated proxy, and a job that needs our gateway. The first hop is not optional, so the only question is whether it will relay you. Two things usually stop it. The egress proxy allows CONNECT to port 443 and nothing else, so the tunnel to a gateway port is refused before your credential is ever sent; you see a 403 or a refused connection attributed to the first hop, and curl -v shows which hop wrote it. Or the egress proxy inspects TLS, in which case the certificate it presents will not match the target and the request fails inside the tunnel rather than at its mouth.
Both are policy, not configuration. The people who can change them run the egress proxy, and the useful thing to bring them is the exact port you need opened and a one-line explanation of what will use it. A SOCKS5 hop is no help here; if the egress proxy will not tunnel to the port, the protocol inside the tunnel is irrelevant. SOCKS5 setup is for the day the port is open and you want DNS resolved at the exit.
Reading a failure in a chain
A chain fails in one place, and the error tells you where if you read it as a hop number rather than a verdict.
- A
407on the firstCONNECTmeans the first hop wanted a credential you did not send. A407after the tunnel is up means the second hop did. curl printsReceived HTTP code 407 from proxy after CONNECTfor the outer case; proxychains shows which hop returneddenied. Fixing 407 is the checklist once you know which hop it was. - A refused connection or a timeout on the first hop means nothing reached the chain at all. On the last hop, resolve the gateway name and check the port against the product your plan actually holds; every gateway refuses an account that does not hold its product, and that refusal looks like a bad password.
- A
403or429from the target came through every hop successfully, and no amount of chain surgery will change it. That is a blocking question, and block replay will tell you whether the target objected to the address or to the request. - The exit address equals the first hop's address:
dynamic_chaindropped a dead hop and carried on, or the local hop is not forwarding upstream. Switch tostrict_chainwhile diagnosing.
Test each hop alone before testing the chain. Run curl -x against the corporate proxy with a plain target, then curl -x against our gateway from a network that reaches it directly, then the composition. A chain whose components have not been proven individually is two unknowns multiplied. Proxy error codes covers the rest of the status table.
When to stop chaining
The good reasons for a chain are the ones above: a hop that policy forces on you, a hop that keeps a credential out of a tool that cannot hold one, or a fixed address to whitelist from a moving machine. Every other motive is a variant of "more hops, more anonymity", and on a provider gateway it is not true. The target sees the exit. It cannot see the hop before the exit, whether there is one hop before it or five. What each extra hop reliably adds is one more round trip on every connection, one more place a timeout can start, and one more log that holds your traffic.
Where we lose in a chain is worth naming too. We see the last hop's address, not yours, so anything we attribute to a source address, from connection counts to an abuse report, lands on that one, and a busy shared hop earns whatever its heaviest user does. A fault in a hop we do not operate looks, from our side, like a client that stopped talking, and there is nothing in our logs to show you. And a chain adds its own latency to ours on every request, so chaining two rotating providers together "to be safe" makes every connection slower and protects nothing.
If what you need is rotation, sticky sessions and country selection with nothing to maintain, that is what the rotating gateway does without a chain, and the connection builder writes the one-hop string for your client. Plans are on the pricing page.