Use residential proxies in Node.js with the HTTP client you already have. Give it the gateway, your username and your password, and every request leaves from a real home IP in the country you pick. No SDK, no API key: one proxy URL works in the built-in fetch, undici, axios and got.
One proxy URLfetch, undici, axios and got
Real home IPs219 countries, down to the city
Browser TLS at the gateway139 fingerprints, same code
Testing a scraper costs less than a coffee: 1 GB of Residential is $0.99, and the same username works on every plan you add. Tested with Node.js 24.18, undici 8.12, axios 1.21 and got 16.2 — November 2026
KEY FACTS
Ludax works with any Node.js HTTP client through one proxy URL, http://resi-USERNAME:[email protected]:7777: no SDK, no client library, no API key.
Node.js 24 needs nothing installed: set HTTPS_PROXY and run with NODE_USE_ENV_PROXY=1, and the built-in fetch uses the proxy. Node.js still marks this switch experimental; undici’s ProxyAgent is the stable way.
undici: new ProxyAgent(url) as the dispatcher. axios: the built-in proxy option. got: an HttpsProxyAgent from hpagent.
One connection is one IP. axios opens a new connection per request, so every request gets a new IP; undici reuses its connection, so use one ProxyAgent per IP you want.
For the same IP across many requests add -session-<token>-time-<minutes> to the password (up to 1000 minutes). Without -time- a session lasts 1 minute.
A refused login is a 407, a dropped exit a 502. In undici and fetch they show as Proxy response (407) !== 200 when HTTP Tunneling; in axios as Request failed with status code 407.
Node.js has its own TLS handshake, which anti-bot systems recognise. The Residential TLS port (gw.ludaxproxy.com:9999) connects with a real browser handshake instead; run Node with NODE_EXTRA_CA_CERTS=ludax-ca.crt.
Using an AI assistant? Give it this guide
One Markdown file with everything ChatGPT, Claude, Cursor or Copilot needs to write working Ludax code in Node.js: the connection format, the gateways in force, sessions, errors and tested examples. Copy it into the chat, or paste this prompt:
Read https://ludaxproxy.com/docs/nodejs.md and use Ludax proxies in my Node.js project.
Two minutes, start to finish. If you see a JSON answer with a residential IP, your Node.js proxy works. Pick your client once: every code block on this page follows it.
Copy your proxy login
The dashboard’s Generate page shows it. The username (lx_k3m9q2p7xa) is yours on every plan; in the URL it gets the prefix of the product: resi- for Residential, premi- for Residential Premium. The password is the plan’s own, 16 hex characters. Everything you can add to it: proxy authentication.
Install your HTTP client
install_fetch.sh
# nothing to install: Node.js 24 or later# macOS and Linux (Windows PowerShell: $env:HTTPS_PROXY = "...")
export HTTPS_PROXY="http://resi-lx_k3m9q2p7xa:[email protected]:7777"
install_undici.sh
npm install undici
install_axios.sh
npm install axios
install_got.sh
npm install got hpagent
Run this
first_request_fetch.mjs
// run with: NODE_USE_ENV_PROXY=1 node first_request_fetch.mjs// (set HTTPS_PROXY first, as in the install step)const r = awaitfetch("https://api.ludaxproxy.com/whoami");
console.log(await r.json());
That is a real home connection in the United States, picked by -country-us in the password. tls_profile is null because the site sees Node’s own handshake (why that matters). Change us to de, jp or any code from the locations directory.
fetch, undici, axios or got: how each one treats the proxy
They all reach the same gateway, but they differ in the two things that matter with residential proxies: whether a request gets a new connection (so a new IP), and how a refused login reaches your code. Measured by counting the connections the gateway received.
FOUR CLIENTS, ONE GATEWAY
Client
How the proxy is set
Three requests in a row
A refused login (407)
Built-in fetch
HTTPS_PROXY and NODE_USE_ENV_PROXY=1
Reuses open connections: 2 connections, so mostly one IP
TypeError: fetch failed, the 407 in error.cause
undici
dispatcher: new ProxyAgent(url)
One agent keeps one connection: 1 connection, one IP
Same as fetch: Proxy response (407) !== 200 when HTTP Tunneling
axios
The proxy option
A new connection each time: 3 connections, 3 IPs
error.response.status === 407
got + hpagent
agent: { https: new HttpsProxyAgent(...) }
A new connection each time: 3 connections, 3 IPs
Bad response: 407, not retried
So: for a new IP on every request, axios and got do it out of the box; with undici or fetch, make a new agent per request (recipe 01). For one IP across many requests, every client needs a session token (recipe 02). The four clients reach the TLS ports the same way: start Node with NODE_EXTRA_CA_CERTS=ludax-ca.crt.
[ 03 / CODE GENERATOR ]
Generate Node.js proxy code for your plan
Choose the product, the country and whether the IP should stay the same, and copy the code for your client. Type your own username and password to get code you can run as is: they stay in this browser tab and are never sent anywhere.
The TLS gateways need the Ludax CA once: download ludax-ca.crt, keep it next to your script and run NODE_EXTRA_CA_CERTS=ludax-ca.crt node ludax.mjs. Why?
[ 04 / RECIPES ]
Six Node.js proxy patterns you will need
Each one runs on its own: paste it, put in your username and password, run. Where two clients are shown they follow the one you picked above.
01
Rotating proxies in Node.js: a new IP on every request
Scraping many pages where no page should see the same visitor twice. One connection is one IP, so the trick is a new connection per request.
rotate_undici.mjs
import { fetch, ProxyAgent } from"undici";
const PROXY = "http://resi-lx_k3m9q2p7xa:[email protected]:7777";
// undici keeps a connection open and reuses it, and one connection is one IP:// a new ProxyAgent per request opens a new connection, so a new IPasyncfunctiongetWithNewIp(url) {
const proxy = newProxyAgent(PROXY);
try {
const r = awaitfetch(url, { dispatcher: proxy });
returnawait r.json();
} finally {
await proxy.close();
}
}
for (let i = 0; i < 3; i++) {
console.log((awaitgetWithNewIp("https://api.ludaxproxy.com/whoami")).ip);
}
rotate_axios.mjs
import axios from"axios";
const proxy = {
protocol: "http",
host: "gw.ludaxproxy.com",
port: 7777,
auth: { username: "resi-lx_k3m9q2p7xa", password: "YOUR_PASSWORD-country-us" },
};
// axios opens a new connection for every request, so every request gets a new IPfor (let i = 0; i < 3; i++) {
const r = await axios.get("https://api.ludaxproxy.com/whoami", { proxy });
console.log(r.data.ip);
}
output (your IPs will differ)
203.0.113.24
198.51.100.7
192.0.2.66
Why the difference? undici keeps a connection open and sends the next request down it, so a single ProxyAgent stays on one IP. axios opens a fresh connection each time. Both behaviours were checked by counting the connections the gateway receives.
02
Sticky sessions: the same IP for a whole flow
Login, search, checkout: anything a site expects from one visitor. A random token keeps the flow on one IP for as long as -time- says.
sticky_undici.mjs
import { fetch, ProxyAgent } from"undici";
import { randomBytes } from"node:crypto";
// one token = one IP, kept for 30 minutes; a new token = a new IPconst token = randomBytes(4).toString("hex");
const proxy = newProxyAgent(
`http://resi-lx_k3m9q2p7xa:YOUR_PASSWORD-session-${token}[email protected]:7777`,
);
for (const step of ["login", "search", "checkout"]) {
const r = awaitfetch("https://api.ludaxproxy.com/whoami", { dispatcher: proxy });
console.log(step, (await r.json()).ip);
}
await proxy.close();
sticky_axios.mjs
import axios from"axios";
import { randomBytes } from"node:crypto";
// one token = one IP, kept for 30 minutes; a new token = a new IPconst token = randomBytes(4).toString("hex");
const client = axios.create({
proxy: {
protocol: "http",
host: "gw.ludaxproxy.com",
port: 7777,
auth: {
username: "resi-lx_k3m9q2p7xa",
password: `YOUR_PASSWORD-session-${token}-time-30-country-us`,
},
},
});
for (const step of ["login", "search", "checkout"]) {
const r = await client.get("https://api.ludaxproxy.com/whoami");
console.log(step, r.data.ip);
}
Sessions hold up to 1000 minutes on Residential and Residential Premium. Always send -time-: without it a session lasts 1 minute. For several flows at once, give each flow its own token.
03
Geo-targeting: a country, a region, a city
Local prices, local search results, local ads. The location goes in the password, after the session.
geo.mjs
import { fetch, ProxyAgent } from"undici";
// country, then state, then city: an IP in Munich, Germanyconst proxy = newProxyAgent(
"http://resi-lx_k3m9q2p7xa:YOUR_PASSWORD-session-muc01-time-10-country-de-state-by-city-munich@gw.ludaxproxy.com:7777",
);
const r = awaitfetch("https://api.ludaxproxy.com/whoami", { dispatcher: proxy });
console.log(await r.json());
await proxy.close();
output
{ ip: '203.0.113.81', country: 'Germany', ... }
Order matters: session first, then -country-, -state-, -city-. Region and city codes for every country are in the locations directory, for example Germany.
04
Many requests in parallel, each from its own IP
Bulk jobs such as price monitoring. Send a few at a time so you never flood the target, and let every request leave from a different home IP.
parallel.mjs
import axios from"axios";
const proxy = {
protocol: "http",
host: "gw.ludaxproxy.com",
port: 7777,
auth: { username: "resi-lx_k3m9q2p7xa", password: "YOUR_PASSWORD-country-us" },
};
const urls = Array.from({ length: 20 }, (_, n) => `https://api.ludaxproxy.com/whoami?page=${n}`);
// 5 requests at a time; each one leaves from its own IPconst ips = [];
for (let i = 0; i < urls.length; i += 5) {
const batch = urls.slice(i, i + 5).map((url) => axios.get(url, { proxy, timeout: 30000 }));
for (const r ofawait Promise.all(batch)) ips.push(r.data.ip);
}
console.log(ips.length, "requests from", newSet(ips).size, "IPs");
output
20 requests from 20 IPs
05
SOCKS5 proxies in Node.js with socks5h://
When your tool speaks SOCKS5. The h makes the gateway resolve the hostname, so no DNS query leaves your machine. SOCKS5 is on Residential and Residential Premium, not on the TLS ports.
install_socks.sh
npm install axios socks-proxy-agent
socks5h.mjs
import axios from"axios";
import { SocksProxyAgent } from"socks-proxy-agent";
// socks5h: the gateway resolves the hostname, so no DNS query leaves your machine// Residential Premium over SOCKS5 (Residential: gw.ludaxproxy.com:7575)const agent = newSocksProxyAgent(
"socks5h://premi-lx_k3m9q2p7xa:[email protected]:1080",
);
const r = await axios.get("https://api.ludaxproxy.com/whoami", {
httpAgent: agent,
httpsAgent: agent,
proxy: false,
});
console.log(r.data);
Production scrapers. A dropped exit is worth one more try; a refused login is not.
retry_undici.mjs
import { fetch, ProxyAgent } from"undici";
const PROXY = "http://resi-lx_k3m9q2p7xa:[email protected]:7777";
// the real reason sits at the end of the error's cause chainfunctionproxyStatus(error) {
for (let e = error; e; e = e.cause) {
const m = /Proxy response \((\d{3})\)/.exec(e.message ?? "");
if (m) returnNumber(m[1]);
}
returnnull;
}
// retry a dropped exit (502), stop at once on a refused login (407)asyncfunctionget(url, tries = 3) {
for (let attempt = 1; ; attempt++) {
const proxy = newProxyAgent(PROXY);
try {
returnawait (awaitfetch(url, { dispatcher: proxy })).json();
} catch (error) {
if (proxyStatus(error) === 407 || attempt === tries) throw error;
awaitnewPromise((resolve) => setTimeout(resolve, 1000 * 2 ** (attempt - 1)));
} finally {
await proxy.close();
}
}
}
console.log(awaitget("https://api.ludaxproxy.com/whoami"));
retry_axios.mjs
import axios from"axios";
const proxy = {
protocol: "http",
host: "gw.ludaxproxy.com",
port: 7777,
auth: { username: "resi-lx_k3m9q2p7xa", password: "YOUR_PASSWORD-country-us" },
};
// retry a dropped exit (502) or a timeout, stop at once on a refused login (407)asyncfunctionget(url, tries = 3) {
for (let attempt = 1; ; attempt++) {
try {
returnawait axios.get(url, { proxy, timeout: 30000 });
} catch (error) {
const status = error.response?.status;
const retry = status === 502 || error.code === "ECONNABORTED" || error.code === "ECONNRESET";
if (!retry || attempt === tries) throw error;
awaitnewPromise((resolve) => setTimeout(resolve, 1000 * 2 ** (attempt - 1)));
}
}
}
console.log((awaitget("https://api.ludaxproxy.com/whoami")).data);
In undici and the built-in fetch the error you catch only says fetch failed; the gateway’s answer is further down error.cause, as Proxy response (502) !== 200 when HTTP Tunneling. axios puts it in error.response.status.
[ 05 / GETTING BLOCKED? ]
Node.js requests blocked? Fix the TLS fingerprint, keep your code
If a site blocks your Node.js scraper even from a clean home IP, it is usually not the IP. It is the TLS handshake.
Before a site reads your URL or a single header, your client says hello: the TLS handshake. Chrome says hello one way; Node.js (so fetch, undici, axios and got) says it in a way no browser does. Anti-bot systems read that hello as a fingerprint (JA3/JA4) and block on it, however good the IP is.
You could move your scraper into a headless browser and pay for it in memory and speed. With Residential TLS proxies you change the proxy instead: the gateway connects to the site with a real browser’s handshake, HTTP/2 settings and matching headers, and your Node.js code stays as it is.
The TLS gateway: gw.ludaxproxy.com:9999 (Residential Premium TLS: gw.ludaxproxy.com:8081).
-AE-chrome154 at the end of the username: the browser to look like. AE also sets a matching User-Agent for you.
ludax-ca.crt, downloaded once: the gateway re-signs HTTPS to send the browser handshake, so Node.js must trust its CA. NODE_EXTRA_CA_CERTS works for every client; it is read when Node.js starts.
import { fetch, ProxyAgent } from"undici";
// the Residential TLS port; -AE-chrome154 = look like Chrome 154const proxy = newProxyAgent(
"http://resi-lx_k3m9q2p7xa-AE-chrome154:[email protected]:9999",
);
// run with: NODE_EXTRA_CA_CERTS=ludax-ca.crt node tls_check_undici.mjsconst r = awaitfetch("https://api.ludaxproxy.com/whoami", { dispatcher: proxy });
console.log((await r.json()).tls_profile);
tls_check_axios.mjs
import axios from"axios";
// run with: NODE_EXTRA_CA_CERTS=ludax-ca.crt node tls_check_axios.mjsconst r = await axios.get("https://api.ludaxproxy.com/whoami", {
proxy: {
protocol: "http",
host: "gw.ludaxproxy.com",
port: 9999, // the Residential TLS portauth: {
username: "resi-lx_k3m9q2p7xa-AE-chrome154", // + the browser to look likepassword: "YOUR_PASSWORD-country-us",
},
},
});
console.log(r.data.tls_profile);
output
Chrome 154
The whoami endpoint reads your handshake the way an anti-bot system does. Plain Node.js prints null; through the TLS gateway it prints the browser you picked. Sticky IP on a TLS port: add -sameip after the session, as in -session-abc123-time-30-sameip.
TWO WAYS TO LOOK LIKE A BROWSER FROM NODE.JS
Headless browser (Puppeteer, Playwright)
Ludax Residential TLS
Your code
Rewritten around pages and selectors
Your fetch, axios or got code, unchanged
Memory and speed
A browser per job: hundreds of MB, seconds per page
Start on Residential. If a site blocks you from clean IPs, switch the same code to Residential TLS: 1 GB is enough to test a target properly, and your username stays the same.
Good to know: AE replaces your User-Agent; with NAE your headers pass through unchanged, so send a User-Agent from the same browser. Apps with certificate pinning refuse a re-signed certificate: use the standard gateways (gw.ludaxproxy.com:7777, gw.ludaxproxy.com:8080) for those. Request and response bodies are never stored.
[ 06 / ERROR MESSAGES ]
Node.js proxy errors, client by client
The exact message each client shows, what it means and the one thing to do. All gateway answers: status codes.
SYMPTOM, CAUSE, FIX
You see
Why
Fix
TypeError: fetch failed with Proxy response (407) !== 200 when HTTP Tunneling
undici or the built-in fetch: the login was refused. A typo, a prefix that does not match the port, or a plan with no data left.
Copy the credentials again from Generate and check the plan. Do not loop: 30 failed logins in 5 minutes block your IP for 10 minutes. Every 407 cause.
AxiosError: Request failed with status code 407 or got’s Bad response: 407
The same refused login, as axios and got report it.
As above. A retry cannot fix a 407.
Proxy response (502) !== 200 when HTTP Tunneling, status code 502
The exit IP dropped or could not reach the site.
Retry (recipe 06). With a sticky session, switch to a new token after two failures. Many at once: check the network status.
UNABLE_TO_VERIFY_LEAF_SIGNATURE, unable to verify the first certificate or SELF_SIGNED_CERT_IN_CHAIN on a TLS port
The TLS gateways re-sign HTTPS with the Ludax CA, which Node.js does not know.
Download ludax-ca.crt and start Node with NODE_EXTRA_CA_CERTS=ludax-ca.crt. Never set NODE_TLS_REJECT_UNAUTHORIZED=0.
The built-in fetch ignores HTTPS_PROXY
Node.js reads the proxy variables only when asked to.
Run with NODE_USE_ENV_PROXY=1 (Node.js 24), or use undici’s ProxyAgent.
The same IP on every request, with no session
undici reuses one open connection, and one connection is one IP.
A new ProxyAgent per request (recipe 01), or use axios.
The IP changes in the middle of a flow
No -time- (a session then lasts 1 minute), the time ran out, or no -sameip on a TLS port.
Add -time-30 (or what the flow needs) after the session token; on the TLS ports, -sameip after it.
ECONNREFUSED or ETIMEDOUT to the gateway
A wrong host or port, or a firewall that blocks outgoing connections.
Check the host is gw.ludaxproxy.com and the port matches the product: 7777, 9999, 8080 or 8081 (SOCKS5: 7575, 1080).
01How do I use a proxy with a username and password in Node.js?
Put them in the proxy URL: http://resi-lx_k3m9q2p7xa:[email protected]:7777. In undici pass new ProxyAgent(url) as the dispatcher; in axios use the proxy option with auth; in got use an HttpsProxyAgent from hpagent. In Node.js 24 the built-in fetch also reads HTTPS_PROXY when you run with NODE_USE_ENV_PROXY=1.
02Does the built-in fetch in Node.js support proxies?
Yes, from Node.js 24: set HTTPS_PROXY to the proxy URL and start Node with NODE_USE_ENV_PROXY=1 (still marked experimental by Node.js). For a proxy per request, import fetch and ProxyAgent from undici and pass the agent as dispatcher.
03How do I rotate proxies in Node.js?
Every new connection leaves from a new IP. axios opens a new connection for each request, so it rotates on its own. undici reuses its connection, so create a new ProxyAgent for each request and close it after. To keep one IP instead, add -session-<token>-time-<minutes> to the password.
04How do I set an axios proxy with authentication?
Use the proxy option: protocol http, host gw.ludaxproxy.com, port 7777 and auth with your username and password. axios (tested with 1.21) tunnels HTTPS through it; no extra package is needed.
05Does Node.js support SOCKS5 proxies?
Not on its own. Install socks-proxy-agent and pass new SocksProxyAgent("socks5h://[email protected]:7575") as httpAgent and httpsAgent in axios, with proxy: false. SOCKS5 is on Residential (7575) and Residential Premium (1080).
06Why is my Node.js scraper blocked even with a residential IP?
Usually the TLS handshake: Node.js does not say hello like a browser, and anti-bot systems block on that fingerprint. The Residential TLS port (gw.ludaxproxy.com:9999) connects with a real browser handshake while your code stays the same; start Node with NODE_EXTRA_CA_CERTS=ludax-ca.crt.
07How much does it cost to test Ludax from Node.js?
There is no free trial; the smallest plan is the test. 1 GB of Residential costs $0.99 and 1 GB of Residential TLS $1.49, prepaid, with no subscription.
[ 08 / NEXT ]
Other ways to use Ludax
The products behind the code, and guides for the jobs Node.js developers run on them.