Every Node.js HTTP client handles proxies differently, and half of them don't handle proxy authentication at all without a helper package. axios silently ignores its own proxy option for HTTPS targets in some versions, native fetch has no proxy option whatsoever, and the error messages — ECONNRESET, 407, or just a hang — tell you nothing.
This is the complete, working reference for routing axios, got, node-fetch, native fetch (undici), and superagent through authenticated residential proxies. It's the Node companion to our Python guides (requests/httpx/aiohttp, Scrapy). All examples use the standard placeholder format — swap in your real credentials:
socks5h://USERNAME:[email protected]:913
Don't use the built-in proxy options. Use a proxy agent. Node's HTTP clients delegate connection handling to an "agent" object, and the agent packages (https-proxy-agent, socks-proxy-agent) implement CONNECT tunneling and authentication correctly. The built-in options often don't — axios's proxy config is the most notorious example.
npm install socks-proxy-agent https-proxy-agent
const axios = require("axios");
const { SocksProxyAgent } = require("socks-proxy-agent");
const agent = new SocksProxyAgent(
"socks5h://USERNAME:[email protected]:913"
);
const res = await axios.get("https://api.ipify.org?format=json", {
httpAgent: agent, // for http:// targets
httpsAgent: agent, // for https:// targets
proxy: false, // IMPORTANT: disable axios's own proxy handling
});
console.log(res.data); // -> the proxy's exit IP
Two things people miss: you must set both httpAgent and httpsAgent (axios picks by target scheme), and you must set proxy: false so axios doesn't also try to apply HTTP_PROXY environment variables on top of your agent.
const got = require("got");
const { SocksProxyAgent } = require("socks-proxy-agent");
const agent = new SocksProxyAgent(
"socks5h://USERNAME:[email protected]:913"
);
const body = await got("https://api.ipify.org?format=json", {
agent: { http: agent, https: agent },
}).json();
If you're scraping protected targets with got, look at got-scraping instead — same API, plus browser-like header generation and HTTP/2 fingerprint mimicry (why that matters: JA3/JA4 explained).
Node's built-in fetch comes from undici, ignores proxy environment variables, and has no agent option. The undici way is a dispatcher:
const { ProxyAgent } = require("undici");
// undici's ProxyAgent speaks HTTP CONNECT (use your HTTP proxy port)
const dispatcher = new ProxyAgent({
uri: "http://us.jibaoproxy.com:1000",
token: "Basic " + Buffer.from("USERNAME:PASSWORD").toString("base64"),
});
const res = await fetch("https://api.ipify.org?format=json", { dispatcher });
console.log(await res.json());
Note: undici's ProxyAgent is HTTP-proxy only. For SOCKS5 with native fetch, either front it with a local forwarder or use a client that accepts socks agents (axios/got above).
const fetch = require("node-fetch");
const { SocksProxyAgent } = require("socks-proxy-agent");
const agent = new SocksProxyAgent(
"socks5h://USERNAME:[email protected]:913"
);
const res = await fetch("https://api.ipify.org?format=json", { agent });
With a rotating residential gateway you don't manage IP lists — the gateway hands you a fresh exit per connection, or holds one exit per session id. In Node that maps cleanly to one agent per identity:
// Sticky: same session id -> same exit IP across requests
function identityAgent(sessionId) {
return new SocksProxyAgent(
`socks5h://USERNAME-session-${sessionId}:[email protected]:913`
);
}
// Account A keeps IP A, account B keeps IP B - cookies and IP move together
const agentA = identityAgent("acct_a");
const agentB = identityAgent("acct_b");
Reuse the agent for connection pooling within an identity; never share one agent across identities. When to rotate vs stick is its own topic — see sticky vs rotating sessions.
| Symptom | Cause | Fix |
|---|---|---|
407 Proxy Authentication Required | Credentials not reaching the proxy | Put user:pass in the agent URL, not the client config |
| axios works on http://, fails on https:// | Only httpAgent set | Set httpsAgent too, and proxy: false |
ECONNRESET immediately | Wrong port / wrong protocol (HTTP port with SOCKS agent) | Match agent type to port |
| DNS leaks / internal hostnames fail | socks5:// resolves DNS locally | Use socks5h:// — the h sends hostnames through the proxy |
| Native fetch ignores HTTP_PROXY | undici doesn't read env vars | Pass a dispatcher explicitly |
| Works locally, 403 on target site | Not a proxy bug — TLS fingerprint | got-scraping or a real browser; see TLS guide |
socks-proxy-agent / https-proxy-agent); never trust built-in proxy options.proxy: false. got: agent: {http, https}. Native fetch: undici ProxyAgent dispatcher.socks5h:// so DNS resolves through the proxy — no leaks.SOCKS5 + HTTP endpoints, sticky or rotating, per-GB pricing — 500MB free traffic, no card required.
Start Free TrialNew users get 500MB free traffic instantly, plus an extra first-deposit reward — limited-time offer.