Skip to main content
[ DOCS / NODE.JS ]

Node.js proxy setup with fetch, axios and got

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.

[ 01 / FIRST REQUEST ]

Your first Node.js request through a home IP

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.

  1. 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.

  2. 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"
  3. 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 = await fetch("https://api.ludaxproxy.com/whoami");
    console.log(await r.json());

    You should see something like this, with your own exit IP:

    output
    { ip: '203.0.113.24', country: 'United States', type: 'residential', tls_profile: null, latency_ms: 412 }

    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.

    Official docs: Node.js: NODE_USE_ENV_PROXY · undici: ProxyAgent · axios: request config

[ 02 / PICK A CLIENT ]

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
ClientHow the proxy is setThree requests in a rowA refused login (407)
Built-in fetchHTTPS_PROXY and NODE_USE_ENV_PROXY=1Reuses open connections: 2 connections, so mostly one IPTypeError: fetch failed, the 407 in error.cause
undicidispatcher: new ProxyAgent(url)One agent keeps one connection: 1 connection, one IPSame as fetch: Proxy response (407) !== 200 when HTTP Tunneling
axiosThe proxy optionA new connection each time: 3 connections, 3 IPserror.response.status === 407
got + hpagentagent: { https: new HttpsProxyAgent(...) }A new connection each time: 3 connections, 3 IPsBad 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.

Product

Real home IPs for everyday scraping. Gateway: gw.ludaxproxy.com:7777

IP address

YOUR CONNECTION STRING

http://resi-lx_k3m9q2p7xa:YOUR_PASSWORD-country-us@gw.ludaxproxy.com:7777
  • username (+ browser on TLS)
  • plan password
  • session
  • location
  • gateway
ludax.mjs
[ 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 IP
async function getWithNewIp(url) {
  const proxy = new ProxyAgent(PROXY);
  try {
    const r = await fetch(url, { dispatcher: proxy });
    return await r.json();
  } finally {
    await proxy.close();
  }
}

for (let i = 0; i < 3; i++) {
  console.log((await getWithNewIp("https://api.ludaxproxy.com/whoami")).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 IP
const token = randomBytes(4).toString("hex");
const proxy = new ProxyAgent(
  `http://resi-lx_k3m9q2p7xa:YOUR_PASSWORD-session-${token}[email protected]:7777`,
);

for (const step of ["login", "search", "checkout"]) {
  const r = await fetch("https://api.ludaxproxy.com/whoami", { dispatcher: proxy });
  console.log(step, (await r.json()).ip);
}
await proxy.close();
output
login 203.0.113.24
search 203.0.113.24
checkout 203.0.113.24

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, Germany
const proxy = new ProxyAgent(
  "http://resi-lx_k3m9q2p7xa:YOUR_PASSWORD-session-muc01-time-10-country-de-state-by-city-munich@gw.ludaxproxy.com:7777",
);

const r = await fetch("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 IP
const 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 of await Promise.all(batch)) ips.push(r.data.ip);
}
console.log(ips.length, "requests from", new Set(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 = new SocksProxyAgent(
  "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);

Official docs: socks-proxy-agent

06

Handle proxy errors: retry a 502, stop on a 407

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 chain
function proxyStatus(error) {
  for (let e = error; e; e = e.cause) {
    const m = /Proxy response \((\d{3})\)/.exec(e.message ?? "");
    if (m) return Number(m[1]);
  }
  return null;
}

// retry a dropped exit (502), stop at once on a refused login (407)
async function get(url, tries = 3) {
  for (let attempt = 1; ; attempt++) {
    const proxy = new ProxyAgent(PROXY);
    try {
      return await (await fetch(url, { dispatcher: proxy })).json();
    } catch (error) {
      if (proxyStatus(error) === 407 || attempt === tries) throw error;
      await new Promise((resolve) => setTimeout(resolve, 1000 * 2 ** (attempt - 1)));
    } finally {
      await proxy.close();
    }
  }
}

console.log(await get("https://api.ludaxproxy.com/whoami"));

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.

WHAT CHANGES IN YOUR CODE

- "http://resi-lx_k3m9q2p7xa:[email protected]:7777"+ "http://resi-lx_k3m9q2p7xa-AE-chrome154:[email protected]:9999"- node scrape.mjs+ NODE_EXTRA_CA_CERTS=ludax-ca.crt node scrape.mjs
  1. The TLS gateway: gw.ludaxproxy.com:9999 (Residential Premium TLS: gw.ludaxproxy.com:8081).
  2. -AE-chrome154 at the end of the username: the browser to look like. AE also sets a matching User-Agent for you.
  3. 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.
terminal.sh
curl -O https://ludaxproxy.com/downloads/ludax-ca.crt
tls_check_undici.mjs
import { fetch, ProxyAgent } from "undici";

// the Residential TLS port; -AE-chrome154 = look like Chrome 154
const proxy = new ProxyAgent(
  "http://resi-lx_k3m9q2p7xa-AE-chrome154:[email protected]:9999",
);

// run with: NODE_EXTRA_CA_CERTS=ludax-ca.crt node tls_check_undici.mjs
const r = await fetch("https://api.ludaxproxy.com/whoami", { dispatcher: proxy });
console.log((await r.json()).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 codeRewritten around pages and selectorsYour fetch, axios or got code, unchanged
Memory and speedA browser per job: hundreds of MB, seconds per pageA plain HTTP request
User-Agent and client hintsFrom the browserAE sets them to match the profile
Browser profilesThe browser you run139: Chrome, Edge, Firefox, Safari, Opera, OkHttp
Pages that need JavaScriptYesNo: use a browser for those (Puppeteer guide)

Which plan for a Node.js scraper?

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.

Get 1 GB of TLS — $1.49

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 seeWhyFix
TypeError: fetch failed with Proxy response (407) !== 200 when HTTP Tunnelingundici 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: 407The 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 502The 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 portThe 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_PROXYNode.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 sessionundici 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 flowNo -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 gatewayA 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).
[ 07 / FAQ ]

Questions Node.js developers ask about proxies

Short answers. The full option list is in proxy authentication.

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.

[ START NOW ]

Point your Node.js client at a home IP

  1. Buy 1 GB of Residential, or of Residential TLS if sites already block you.
  2. Copy your login from Generate in the dashboard.
  3. Pick your client in the code generator and run it.

Stuck? Send us your code and the error on Telegram and we debug it with you. Every plan and price: pricing.