Web Scraping & Automation

Using Proxies in Node.js: Axios, Fetch and Got

How to send requests through proxies in Node.js: axios with https-proxy-agent, native fetch with undici ProxyAgent, got with hpagent, SOCKS5 agents, rotation and common errors.

By Team of Monkeys Editorial Updated 5 min read Affiliate disclosure

This guide contains affiliate links to Geonode. If you buy through them we may earn a commission, at no extra cost to you.

Proxies in Node.js: use agents

Node.js HTTP clients route traffic through proxies using agents: objects that control how connections are made. The most reliable approach across libraries is to create a proxy agent and pass it to your client. This guide covers axios, native fetch (powered by undici) and got. For browser automation in Node, see Puppeteer proxies.

Axios

Axios has a built-in proxy option, but it has historically had problems with HTTPS targets through HTTP proxies. The dependable pattern is to disable the built-in option and use https-proxy-agent:

javascript
import axios from 'axios';
import { HttpsProxyAgent } from 'https-proxy-agent';

const agent = new HttpsProxyAgent('http://USER:PASS@gate.example.com:7777');

const { data } = await axios.get('https://httpbin.org/ip', {
  httpsAgent: agent,
  httpAgent: agent,
  proxy: false,
  timeout: 30000,
});
console.log(data);

For a reusable instance:

javascript
const client = axios.create({ httpsAgent: agent, httpAgent: agent, proxy: false, timeout: 30000 });

Native fetch (Node 22 and later)

Node's built-in fetch is implemented by undici. Use undici's ProxyAgent as a dispatcher:

javascript
import { ProxyAgent } from 'undici';

const dispatcher = new ProxyAgent('http://USER:PASS@gate.example.com:7777');
const res = await fetch('https://httpbin.org/ip', { dispatcher });
console.log(await res.json());

If your Node version's global fetch doesn't accept the dispatcher, import fetch from undici directly. You can also set a global dispatcher with setGlobalDispatcher(new ProxyAgent(...)). Node 20 reached end of life on 30 April 2026, so build new projects on Node 22 or 24, which are the supported LTS lines as of October 2026.

got

javascript
import got from 'got';
import { HttpsProxyAgent } from 'hpagent';

const res = await got('https://httpbin.org/ip', {
  agent: {
    https: new HttpsProxyAgent({ proxy: 'http://USER:PASS@gate.example.com:7777' }),
  },
  timeout: { request: 30000 },
}).json();
console.log(res);

SOCKS5

Use socks-proxy-agent with axios or got:

javascript
import { SocksProxyAgent } from 'socks-proxy-agent';
const agent = new SocksProxyAgent('socks5h://USER:PASS@socks.example.com:1080');
const { data } = await axios.get('https://httpbin.org/ip', { httpsAgent: agent, httpAgent: agent, proxy: false });

socks5h resolves DNS through the proxy. See SOCKS5 proxies.

Rotating proxies

Create a new agent per session:

javascript
const newAgent = () =>
  new HttpsProxyAgent(`http://customer-USER-country-us-session-${Math.random().toString(36).slice(2, 10)}:PASS@gate.example.com:7777`);

async function fetchWithFreshIp(url) {
  const agent = newAgent();
  return axios.get(url, { httpsAgent: agent, httpAgent: agent, proxy: false, timeout: 30000 });
}

Agents keep connections alive, so reusing one agent with a rotating gateway may keep you on the same exit. A new agent per session avoids that. See proxy rotation strategies.

Retries

javascript
async function getWithRetry(url, attempts = 4) {
  for (let n = 0; n < attempts; n++) {
    try {
      const res = await fetchWithFreshIp(url);
      if (res.status === 200) return res.data;
    } catch (e) {
      if (n === attempts - 1) throw e;
    }
    await new Promise((r) => setTimeout(r, 1000 * 2 ** n));
  }
}

Environment variables

Some tools respect HTTP_PROXY/HTTPS_PROXY, but Node's core HTTP modules historically ignored them. That has changed: Node 24.5 and later (and 22.21 onwards) can read HTTP_PROXY, HTTPS_PROXY and NO_PROXY for both fetch and the http/https modules when you opt in with NODE_USE_ENV_PROXY=1 or the --use-env-proxy flag. It's off by default, so don't assume a library picks the variables up unless its documentation says so. See the section on built-in proxy support below.

Common errors

ErrorCause
407 Proxy Authentication RequiredWrong credentials, or special characters not URL-encoded
ECONNREFUSEDWrong proxy host/port
ECONNRESET / socket hang upProxy dropped connection; rotate and retry
EPROTO / TLS errorsUsing axios's built-in proxy with HTTPS; switch to an agent
ETIMEDOUTSlow exit; increase timeout or rotate

See common proxy errors.

Proxy tip: Want to skip proxy management on some targets? Geonode offers both raw proxies and a Scraper API that returns HTML or Markdown with optional JavaScript rendering. We compare the two approaches in using Geonode with Node.js.

Choosing a Node.js HTTP client for proxy work

ClientProxy approachNotes
Native fetch (undici)ProxyAgent dispatcherNo extra HTTP dependency; modern and fast
axioshttps-proxy-agent + proxy: falseVery popular; interceptors are handy
gothpagent agentsBuilt-in retries and hooks
undici requestProxyAgentLowest-level, highest performance

For new projects, native fetch with undici's ProxyAgent is a clean default.

Concurrency with p-limit

Control concurrency so proxies and targets aren't overwhelmed:

javascript
import pLimit from 'p-limit';
import { ProxyAgent } from 'undici';

const limit = pLimit(10);
const session = () => `customer-USER-country-us-session-${Math.random().toString(36).slice(2, 10)}`;

async function fetchPage(url) {
  const dispatcher = new ProxyAgent(`http://${session()}:PASS@gate.example.com:7777`);
  try {
    const res = await fetch(url, { dispatcher, headers: { 'Accept-Language': 'en-US,en;q=0.9' } });
    return { url, status: res.status, body: await res.text() };
  } finally {
    await dispatcher.close();
  }
}

const results = await Promise.all(urls.map((u) => limit(() => fetchPage(u))));

Closing each dispatcher releases its connections; a new dispatcher per request guarantees a new session.

TLS fingerprints in Node

Node's TLS fingerprint differs from browsers. For sites that check it, consider:

Debugging tips

  • Log res.status and the first part of the body to detect block pages.
  • Test the same request with curl to isolate proxy vs code issues. See curl proxy.
  • Watch for unhandled promise rejections hiding proxy errors.
  • Set timeouts explicitly; defaults can be long.

For general patterns, see web scraping proxies.

Handling proxy rotation with axios interceptors

Interceptors let you attach a fresh proxy agent to every request automatically:

javascript
import axios from 'axios';
import { HttpsProxyAgent } from 'https-proxy-agent';

const client = axios.create({ proxy: false, timeout: 30000 });

client.interceptors.request.use((config) => {
  const sid = Math.random().toString(36).slice(2, 10);
  const agent = new HttpsProxyAgent(`http://customer-USER-country-us-session-${sid}:${process.env.PROXY_PASS}@gate.example.com:7777`);
  config.httpsAgent = agent;
  config.httpAgent = agent;
  return config;
});

client.interceptors.response.use(undefined, async (error) => {
  const cfg = error.config;
  cfg.__retries = (cfg.__retries || 0) + 1;
  if (cfg.__retries <= 3) return client(cfg); // new agent assigned by the request interceptor
  throw error;
});

Each retry passes through the request interceptor again and gets a new session, so retries use fresh IPs.

Handling cookies through a proxy

Node HTTP clients don't keep cookies by default. For sessions that need them, use a cookie jar (for example tough-cookie with an axios cookie-jar integration) and keep the same proxy session for the whole flow. Rotating the IP while keeping cookies can look like session hijacking. See sticky vs rotating sessions.

Serverless environments

On serverless platforms, outbound IPs change frequently, so IP whitelisting won't work; use username/password authentication. Cold starts mean agents are created often, which is fine for rotation but adds a little latency. Keep proxy credentials in the platform's secret store. See proxy authentication methods.

Node's built-in proxy support

Recent Node.js releases can route requests through a proxy without any extra package. Since Node 24.5 (and 22.21 on the older line), built-in proxy support covers both fetch() and the http/https modules, configured from the standard environment variables:

bash
NODE_USE_ENV_PROXY=1 \
HTTPS_PROXY=http://USER:PASS@gate.example.com:7777 \
NO_PROXY=localhost,127.0.0.1 \
node scraper.js

Or use the flag instead of the variable: node --use-env-proxy scraper.js. Newer releases (24.14 and 25.4 onwards) also add http.setGlobalProxyFromEnv() to switch it on from code, and you can give individual agents their own settings with the proxyEnv option.

Things to know:

  • It's opt-in. Setting HTTPS_PROXY alone still does nothing in Node unless the flag or variable is set.
  • Credentials go in the URL, URL-encoded if they contain special characters.
  • It's one proxy per process (or per agent). For per-request rotation, keep using undici's ProxyAgent or https-proxy-agent as shown above.
  • SOCKS isn't covered; use socks-proxy-agent for that.

The details, including NO_PROXY syntax, are in Node's built-in proxy support documentation. Node 20 reached end of life in April 2026; Node's release schedule shows which versions are supported. For other languages, see Python requests proxies and curl proxy.

Key takeaways

  • Node.js clients use proxy agents: undici ProxyAgent for fetch, https-proxy-agent for axios, hpagent for got.
  • With axios, disable the built-in proxy option when using an agent.
  • Create a new agent per session to guarantee rotation with backconnect gateways.
  • Use socks-proxy-agent with socks5h for SOCKS proxies.
  • Control concurrency with p-limit and set explicit timeouts. See web scraping proxies.

FAQ

Why doesn't axios's proxy option work with HTTPS?

Historically it sent requests in a way some proxies and HTTPS targets didn't handle well. Using https-proxy-agent with proxy: false avoids the issue.

Which proxies should I use for Node.js scraping?

The same principles apply as in any language. See best proxies for web scraping.

Affiliate disclosure: Team of Monkeys is a Geonode affiliate. Links and banners marked as sponsored, and links to Geonode in the text, use our referral link, and we may earn a commission if you buy through them, at no extra cost to you. Our explanations and comparisons are written to be useful whichever provider you choose.

Editorial note: Proxy providers change their pools, features and prices often, so treat any figures here as typical ranges at the time of writing and confirm current terms with the provider before you buy.