Node proxy rotation

Node.js proxy rotation without fake rotation

Explicit Undici dispatchers, axios agents that do not fight each other, and a lease pattern that makes rotation provable instead of assumed.

Field notes Setup checks Updated 2026-06-11

Native fetch needs an Undici dispatcher

Node's native fetch is powered by Undici, so the old http.Agent proxy packages do not automatically affect it. Give fetch a Undici ProxyAgent dispatcher built for the proxy you want to use.

import { fetch, ProxyAgent } from 'undici';
                        
                        const proxy = new ProxyAgent('http://USER:PASS@proxynade.net:2555');
                        
                        const res = await fetch('https://example.com', {
                          dispatcher: proxy
                        });
                        
                        console.log(res.status);

For hard rotation, create the dispatcher for one task and close it when the task ends. Connection pooling is useful until it hides rotation.

On a Proxynade pool the routing options ride in the username, so the full line looks like http://rt97db6958d9-plan-volume-country-us-lifetime-30:PASS@proxynade.net:2555: a required plan token (volume, premium, or datacenter), an optional lowercase country code, and an optional rotation lifetime in minutes. Datacenter usernames take a lifetime token only on a sticky session, and the password is used exactly as issued.

Axios must not fight your agent

When you hand axios a custom HTTP or SOCKS agent, disable its built-in proxy parsing with proxy: false, otherwise two proxy systems may try to own the same request. SOCKS5 proxy authentication is defined in RFC 1928.

import axios from 'axios';
                        import { SocksProxyAgent } from 'socks-proxy-agent';
                        
                        const agent = new SocksProxyAgent(
                          'socks5h://USER:PASS@proxynade.net:2555'
                        );
                        
                        const res = await axios.get('https://example.com', {
                          httpAgent: agent,
                          httpsAgent: agent,
                          proxy: false,
                          timeout: 30000,
                        });

Rotation strategy by job type

JobRotation choiceReason
Search result collectionRotate per query batchYou want spread, not a new IP every asset.
Account login flowSticky sessionA sudden exit change looks like a new machine.
Price monitoringRotate per domain or regionKeeps retries readable and cheap.
Blocked page retryChange proxy and slow downRetrying fast on one tunnel burns bandwidth.

Whichever split you choose, log proxy label, target host, status, bytes, and retry count together, because without those fields a failure six hours later just looks like random provider quality.

Retry loops are where bandwidth goes

Most Node scrapers waste their bandwidth after the first failure rather than before it. A timeout triggers a retry, the retry reloads the same heavy page, the target returns the same block, and the app records zero useful rows while the proxy meter counts every byte of it.

Small retry budgets, blocked waste assets in browser jobs, and a hard stop on responses that are clearly target blocks keep that loop from eating the bill.

Use a rotation lease

A clean Node rotation setup treats each proxy assignment as a lease that owns the client agent, the retry budget, and the labels. When the lease ends, the agent closes and that exit is done.

async function withProxyLease(proxyUrl, task) {
                          const agent = new ProxyAgent(proxyUrl);
                          try {
                            return await task(agent);
                          } finally {
                            await agent.close();
                          }
                        }
                        
                        await withProxyLease('http://USER:PASS@proxynade.net:2555', agent => {
                          return fetch('https://example.com', { dispatcher: agent });
                        });

This shape prevents a common fake rotation: one global agent, many proxy strings, and no proof that the connection changed.

Retry policy that does not burn the account

ResponseRetry?Reason
407NoAuth failed. Repeating burns time.
403Maybe once with a new leaseTarget blocked the route.
TimeoutMaybe onceCould be route or target load.
429Slow downFast retries make the signal worse.

Keep this policy next to the agent code, because when each caller invents its own retries the bandwidth waste just comes back from another file.

Node proxy rotation FAQ

Does Node fetch use HTTP_PROXY automatically? Not by default. Pass an explicit Undici dispatcher rather than relying on environment variables.

Why does my proxy not rotate? The client may be reusing an existing connection, or the lifetime token in the username pins the exit for that many minutes. Build rotation around task boundaries, not string assignment.

Should I use axios or fetch? Whichever your code can instrument; the important part is clear agent ownership and retry logging.

What should I log? Proxy label, target host, status, duration, retry count, and billed GB window.