All guides
DEVELOPER GUIDES · 4 MIN READ

How to use a proxy with Node.js fetch

Route Node.js fetch through Undici ProxyAgent. Add proxy authentication, bound requests, and verify the response and exit IP.

How to use a proxy with Node.js fetch

Pass Undici's ProxyAgent as the dispatcher for each Node.js fetch() call. The example makes one target request and one exit-IP request through that same dispatcher, with a 15-second timeout per request and a 1 MiB body limit; a rotating proxy can still choose a different exit for each request.

Warning: These requests consume proxy traffic and may incur charges; a body-size check cannot refund bytes already transferred. This example uses an unencrypted HTTP client-to-proxy connection, so credentials can be exposed on that link.

Start with a sandbox or test account, set the provider budget, and use a trusted network. Remove any credential binding that reaches logs.

Audience: Node.js developers who know npm, modules, and async functions.

Match the runtime and Undici package

Tested with Node.js 22.23.1 and Undici 8.11.2. Install Node.js from the official downloads page. Node's built-in fetch uses a bundled Undici version.

This example imports fetch and ProxyAgent from the same installed package to keep their APIs aligned. Check process.versions.undici when diagnosing Node's built-in fetch.

Install the pinned package in the application:

npm install --save-exact undici@8.11.2

Keep PROXY_URL in a runtime secret binding. It may include proxy credentials, but do not print the value or an error object that could contain connection details. For an unauthenticated local test, set export PROXY_URL=http://proxy.example.net:8080 before running the script.

Send two bounded requests

Save as proxy-fetch.mjs:

import { fetch, ProxyAgent } from "undici";
import { isIP } from "node:net";

const TARGET = "https://example.com/";
const EXPECTED_MARKER = "Example Domain";
const EXIT_CHECK = "https://api.ipify.org?format=json";
const MAX_BYTES = 1_048_576;
const proxyUrl = process.env.PROXY_URL;
if (!proxyUrl) throw new Error("PROXY_URL is required");

async function readBounded(response) {
  if (!response.body) return "";
  const reader = response.body.getReader();
  const chunks = [];
  let total = 0;
  try {
    while (true) {
      const { done, value } = await reader.read();
      if (done) break;
      total += value.byteLength;
      if (total > MAX_BYTES) {
        await reader.cancel();
        throw new Error("response exceeded the 1 MiB limit");
      }
      chunks.push(Buffer.from(value));
    }
  } finally {
    reader.releaseLock();
  }
  return Buffer.concat(chunks).toString("utf8");
}

let dispatcher;
try {
  dispatcher = new ProxyAgent(proxyUrl);
} catch {
  throw new Error("PROXY_URL is invalid");
}
try {
  for (const url of [TARGET, EXIT_CHECK]) {
    const response = await fetch(url, {
      dispatcher,
      redirect: "manual",
      signal: AbortSignal.timeout(15_000),
    });
    if (response.status !== 200) {
      await response.body?.cancel();
      throw new Error(`request returned HTTP ${response.status}`);
    }
    const body = await readBounded(response);
    if (url === TARGET && !body.includes(EXPECTED_MARKER)) {
      throw new Error("target marker was absent");
    }
    let exitIp;
    if (url === EXIT_CHECK) {
      const result = JSON.parse(body);
      exitIp = result.ip;
      if (typeof exitIp !== "string" || isIP(exitIp) === 0) {
        throw new Error("IP-check response did not contain a valid IP address");
      }
    }
    console.log(`status=${response.status} body_bytes=${Buffer.byteLength(body)}`);
    if (url === EXIT_CHECK) console.log(`exit_ip=${exitIp}`);
  }
} catch {
  throw new Error("request failed; inspect proxy configuration without logging secrets");
} finally {
  await dispatcher.close();
}

Run it with the runtime-provided secret binding:

node proxy-fetch.mjs

Node's AbortSignal.timeout() aborts each request after 15 seconds; it does not impose a total deadline across both requests. The body reader enforces an application memory limit, not a provider traffic quota. HTTP proxy CONNECT challenges and target responses are different phases, so diagnose HTTP 407 at the proxy boundary and do not retry rejected credentials.

Illustrative output. The IP is replaced with a documentation address; body byte counts can vary:

status=200 body_bytes=559
status=200 body_bytes=23
exit_ip=203.0.113.10

For a regional storefront check, set TARGET to the authorized page and replace Example Domain with the expected locale, currency, or product marker. Bind the provider’s region-specific endpoint through PROXY_URL, then check the returned page separately from its observed exit IP. See the geotargeting guide.

Interpret the route check

Observation Likely boundary Next check
HTTP 407 or proxy connection failure Proxy authentication or CONNECT Check scheme, host, port, and credentials without printing the error cause
HTTP 401 or 403 Target policy Check target-side authorization
HTTP 502, 503, or 504 Gateway or target availability Defer; retry only under an explicit bounded policy that honors Retry-After
Timeout or body over 1 MiB Request or local resource limit Stop this attempt and review endpoint behavior before raising limits
HTTP 200 without the marker Application validation Reject the response and inspect the expected content

The IP-check service reports the public address it sees for its request. This confirms that request used an exit route, but it does not prove location, anonymity, or a stable address on a rotating proxy. See the HTTP 407 guide when proxy negotiation fails.

Sources and further reading

ProxyLaneTraffic never expires

Premium residential proxies
From $2/GB

Scale your workflows with the tools you already use. Rotating or sticky sessions up to 72 hours.

Try 350 MB for $1.95 View plans Proxies for web scraping

Keep reading

Amazon Scraper API vs Proxy: Choose a Product Data Source

Compare seller APIs, licensed Amazon product data and proxy page checks by record, access scope and reuse rights.

Read guide

Antidetect browser proxy setup: choose a profile guide

Choose the right anti-detect browser proxy guide, map the shared verification steps, and keep account access separate from the network route.

Read guide

Best Proxies for Market Research: Choose by Sample and Cost

Our picks for market research: ProxyLane for small, intermittent projects, plus options for regional tracking, monthly collection and managed extraction.

Read guide