Documentation Blog Free tools [email protected]Log in

How to use a proxy in Node.js: fetch, axios and the gotchas

Node.js fetch and axios both route through a proxy agent to a proxy endpoint and out to the targetfetch(url,{dispatcher})undici ProxyAgentaxios.get(url,{httpsAgent})https-proxy-agentProxy endpointrotating exit IPTargetsees the proxy IP

Node.js has no built-in proxy setting — you attach a proxy agent to your HTTP client. With native fetch (Node 18+) that means an undici ProxyAgent passed as the dispatcher; with axios it means either the proxy config object or an https-proxy-agent. Both take a couple of lines once you know which knob each library exposes.

Native fetch (Node 18+)

Node's built-in fetch ignores the HTTP_PROXY environment variable and has no proxy option. The supported way is an undici ProxyAgent set as the request's dispatcher:

import { ProxyAgent } from "undici";

const agent = new ProxyAgent("http://USER:[email protected]:7777");
const res = await fetch("https://ipinfo.io/ip", { dispatcher: agent });
console.log(await res.text());   // the proxy's exit IP

undici ships with Node, so on Node 18+ there is usually nothing extra to install. The credentials go inline in the proxy URL. This is the modern, first-class path — older tutorials that tell you to set HTTP_PROXY for fetch are wrong for native fetch; that only worked for libraries that read the env var.

axios

axios gives you two options. The simplest is the built-in proxy config:

const res = await axios.get("https://example.com", {
  proxy: {
    host: "pr.quanticdata.io", port: 7777,
    auth: { username: "USER", password: "PASS" },
  },
});

There is a well-known catch: axios's built-in proxy option has historically been unreliable for HTTPS targets through an HTTP proxy (the CONNECT tunnel). The robust alternative most people end up on is an explicit agent:

import { HttpsProxyAgent } from "https-proxy-agent";

const agent = new HttpsProxyAgent("http://USER:[email protected]:7777");
const res = await axios.get("https://example.com", {
  httpsAgent: agent, proxy: false,   // proxy:false is required!
});

The proxy: false line is essential and the number-one axios-proxy bug: without it, axios tries to apply its own proxy handling on top of your agent and the request breaks. Set the agent, disable the built-in proxy, done.

The gotchas across both

SymptomCauseFix
fetch ignores the proxySet HTTP_PROXY env var expecting fetch to read itUse an undici ProxyAgent as dispatcher
axios HTTPS request hangs/failsBuilt-in proxy option on an HTTPS targetUse httpsAgent + proxy: false
407 auth errorSpecial chars in password not encodedURL-encode the credentials
Works locally, fails in DockerEnv-based proxy libs, wrong env scopePrefer explicit agents over env vars

Testing the proxy actually routes

Before pointing either client at your real target, confirm the proxy is in the path — a misconfigured agent silently falls back to your server's IP, and you only find out when the target bans your box. Hit an IP-echo endpoint through the proxy and check the returned address is the proxy's exit, not your machine's. Run it twice against a rotating endpoint and you should see two different IPs, confirming rotation is live; against a sticky one, the same IP twice. If it echoes your own address, the agent isn't wired in — recheck the dispatcher for fetch, or the httpsAgent plus proxy:false pair for axios. This ten-second check is the cheapest insurance against the whole class of scraper-worked-then-everything-got-banned bugs.

Rotation for scraping

Attaching one static proxy is fine for a few requests; scraping at volume needs many IPs. Instead of maintaining and health-checking your own IP list, point the agent at a rotating endpoint — the provider assigns a fresh exit IP per request automatically, so the same one-line agent gives you rotation with no extra code. Use residential IPs for defended targets and datacenter for tolerant, high-volume ones. Reuse the agent across requests (it pools connections) rather than constructing a new one per call — creating a fresh agent per request leaks sockets and slows the whole run. One more practical note for concurrent Node scrapers: cap your concurrency. It is tempting to fire hundreds of requests at once because Node makes it easy, but a rotating pool spreads load across IPs, it does not make a target immune to being hammered in aggregate; a sane concurrency limit plus small jitter between requests keeps the whole operation looking like traffic rather than an attack.

When Node plus a proxy isn't enough

A proxy fixes the IP, but a raw fetch or axios call still only gets you the server-rendered HTML — no JavaScript execution, and you write your own parsing with cheerio or regex. When a target renders client-side or blocks harder than proxies alone can beat, point your Node client at a scraping API instead of the site: one request with the URL, and you get back clean Markdown or structured JSON with proxy, rendering and anti-block handled server-side. Your Node code stays a single fetch or axios.post — no agent, no cheerio, no anti-block maintenance. Keep raw fetch/axios+proxy for simple static targets; reach for the API when the page fights back or you need it at scale. For agent-driven Node apps, the same capabilities are available as MCP tools.

Sources & further reading

FAQ

Quick answers on how to use proxy in node fetch.

Something else? Ask us →

How do I use a proxy with native fetch in Node.js?

Node's built-in fetch has no proxy option and ignores HTTP_PROXY. Create an undici ProxyAgent with your proxy URL (credentials inline) and pass it as the dispatcher on the fetch call: fetch(url, { dispatcher: agent }). undici ships with Node 18+, so there's usually nothing extra to install.

Why doesn't my axios proxy work on HTTPS sites?

axios's built-in proxy config has long been unreliable for HTTPS targets through an HTTP proxy. Use an https-proxy-agent instead: pass it as httpsAgent and set proxy: false on the request. The proxy: false is essential — without it axios applies its own proxy handling on top of your agent and the request breaks.

What is the difference between the axios proxy option and https-proxy-agent?

The proxy option is axios's built-in config (host, port, auth) and is simplest but flaky for HTTPS tunneling. https-proxy-agent is an explicit agent that reliably handles the HTTPS CONNECT tunnel; pass it as httpsAgent with proxy: false. Most production axios-through-a-proxy setups use the agent for reliability.

How do I rotate proxies in Node.js?

Point your proxy agent at a rotating endpoint — the provider assigns a fresh exit IP per request automatically, so one agent gives you rotation with no rotation code. This beats maintaining your own IP list and health-checking dead proxies. Reuse the agent across requests so it pools connections rather than reconnecting each call.

Should I use a proxy with fetch or a scraping API?

Use fetch or axios with a proxy for simple static targets you just need to reach from a different IP. Use a scraping API when the target renders JavaScript or blocks harder than proxies alone beat — it handles the browser, residential proxies and anti-block server-side and returns clean data, so your Node code stays one request.

Keep the Node code to one request

Point fetch or axios at the scraping API — clean Markdown or JSON back, with residential proxies, rendering and anti-block handled server-side. Pay per success, $2 of free usage every month.

Related reading