# How to Use a Proxy in Node.js

> Use a proxy in Node.js: native fetch needs an undici ProxyAgent, axios takes a proxy config or an https-proxy-agent. Auth, HTTPS, rotation and the common mistakes.

[Home](https://quanticdata.io/)/[Blog](https://quanticdata.io/blog/)/How to Use a Proxy in Node.js

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

GuidesJul 30, 2026·5 min read·QuanticData Team

On this page [Native fetch (Node 18+)](/blog/how-to-use-a-proxy-in-nodejs/#native-fetch-node-18) [axios](/blog/how-to-use-a-proxy-in-nodejs/#axios) [The gotchas across both](/blog/how-to-use-a-proxy-in-nodejs/#the-gotchas-across-both) [Testing the proxy actually routes](/blog/how-to-use-a-proxy-in-nodejs/#testing-the-proxy-actually-routes) [Rotation for scraping](/blog/how-to-use-a-proxy-in-nodejs/#rotation-for-scraping) [When Node plus a proxy isn't enough](/blog/how-to-use-a-proxy-in-nodejs/#when-node-plus-a-proxy-isn-t-enough)

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:PASS@pr.quanticdata.io: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:PASS@pr.quanticdata.io: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

| Symptom | Cause | Fix |
| --- | --- | --- |
| fetch ignores the proxy | Set `HTTP_PROXY` env var expecting fetch to read it | Use an undici `ProxyAgent` as `dispatcher` |
| axios HTTPS request hangs/fails | Built-in `proxy` option on an HTTPS target | Use `httpsAgent` + `proxy: false` |
| 407 auth error | Special chars in password not encoded | URL-encode the credentials |
| Works locally, fails in Docker | Env-based proxy libs, wrong env scope | Prefer 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](https://quanticdata.io/rotating-proxies/) — 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](https://quanticdata.io/residential-proxies/) 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](https://quanticdata.io/web-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](https://quanticdata.io/mcp-server/).

### Sources & further reading

- [Stack Overflow — Make a request in native fetch with proxy in Node.js 18](https://stackoverflow.com/questions/72306101/make-a-request-in-native-fetch-with-proxy-in-nodejs-18)

- [npm — https-proxy-agent](https://www.npmjs.com/package/https-proxy-agent)

## FAQ

Quick answers on how to use proxy in node fetch.

[Something else? Ask us →](mailto:hello@quanticdata.io)

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

[Start free — $2/month included](https://app.quanticdata.io/register)[Explore Web Scraping API](https://quanticdata.io/web-scraping-api/)

## Related reading

[Guides How to Use MCP in Cursor What MCP gives Cursor's agent, how to add a server in mcp.json, project vs global scope, approving tool calls, and connecting a web-data server for live scraping. Read →](https://quanticdata.io/blog/how-to-use-mcp-in-cursor/) [Guides How to Use a Proxy in Puppeteer Set the proxy in launch args, authenticate with page.authenticate, rotate per page via browser contexts, and dodge the mistakes that leak your real IP. Read →](https://quanticdata.io/blog/how-to-use-a-proxy-in-puppeteer/) [Guides How to Use undetected-chromedriver Install and launch the patched driver that evades Selenium detection, add a proxy, and understand the ceiling — IP reputation and behavior it can't fix. Read →](https://quanticdata.io/blog/how-to-use-undetected-chromedriver/)

---

Source: https://quanticdata.io/blog/how-to-use-a-proxy-in-nodejs/ · Site index for AI: https://quanticdata.io/llms.txt
