
Quick Answer
Use a proxy in Node.js through an Undici `ProxyAgent`, an Axios `proxy` object, or `undici.request()` with a dispatcher. Keep credentials outside source code, set a timeout, and validate the returned data. Test the observed exit IP before running the client against its intended destination.
A minimal Undici test can read the complete proxy URL from a protected environment variable:
Need proxy infrastructure for this workflow? Compare residential and mobile proxy options.
Key Takeaways
- Node.js Fetch uses a bundled Undici implementation, while the installed Undici package exposes additional client controls.
- Axios accepts an explicit proxy object and can also read conventional proxy environment variables.
- An HTTP proxy reaches an HTTPS destination through CONNECT, while the destination certificate remains independently validated.
- Reusing a client or dispatcher saves connections, but it can also preserve one route longer than expected.
- Proxy validation should check the exit IP, response status, content type, and required response fields.
- Reliable Node.js workloads use bounded concurrency, one retry layer, and task-aware session handling.
Which Node.js Proxy Client Should You Use?
The best Node.js proxy client depends on the response interface and connection control your application needs. Fetch, Axios, and Undici can use the same HTTP proxy through different options.
| Client | Proxy configuration | Best for | Main tradeoff |
|---|---|---|---|
| Node.js Fetch | Undici global dispatcher | Applications using the standard Fetch response interface | The dispatcher change affects the process |
| Axios | Explicit `proxy` object | Existing Axios services, interceptors, and familiar response handling | Axios options do not configure other clients |
| `undici.request()` | Per-request dispatcher | Services needing direct dispatcher and stream control | The response API is more Node-specific |
Node.js Fetch is convenient when an application already uses `Request` and `Response` objects. The current Node.js Fetch documentation confirms that Fetch accepts a compatible Undici dispatcher.
Axios offers its own request configuration, interceptors, transforms, and error objects. Its explicit proxy object keeps routing visible at the client call or instance level.
Undici provides lower-level request methods and direct dispatcher ownership. It suits services that want explicit pooling, body consumption, and connection cleanup without Axios abstractions.
These choices do not create different exit networks by themselves. The provider, access point, and session settings determine routing after the client connects.
Proxy connections and sessions can have different lifetimes. That distinction matters when a reused socket appears to prevent an expected route change.
What Do You Need Before Using a Proxy in Node.js?
A Node.js proxy project needs a supported runtime, current dependencies, proxy credentials, and a public IP-check service. Start with an IP check before sending requests to the intended destination.
We tested these examples with Node.js 24.15.0, Axios 1.21.0, and Undici 8.12.3. Undici 8.12.3 requires Node.js 22.19.0 or newer.
Create the project and install the tested package versions:
Node.js includes Fetch, but its bundled Undici version follows the installed runtime. The separate Undici package comparison explains why installed features can differ.
Set the endpoint details in the current shell. The password prompt avoids placing the secret directly in the command or shell history.
Leave both credential variables unset when the provider authenticates through source-IP allowlisting. Never set only one credential variable because the configuration should fail closed.
The examples query a public IP endpoint and optionally compare its answer with `EXPECTED_PROXY_IP`. Replace the placeholder endpoint values with credentials generated for your account.
A workflow for web scraping with proxies should first prove that direct retrieval works. Add the proxy after verifying the baseline response and required fields.
How Should You Share Proxy Configuration Across the Three Clients?
Shared proxy configuration should validate environment values once and return the format required by each client. The following module creates an Undici agent and an Axios proxy object without logging secrets.
Save this file as `proxy-config.js`:
The module accepts HTTP and HTTPS proxy endpoints. The scheme describes the connection to the proxy, not the destination request. An `http://` endpoint does not encrypt proxy credentials on that first network leg.
Undici receives a clean proxy URI and a separate Basic authentication token. The current Undici ProxyAgent documentation documents this token option and dispatcher behavior.
Axios receives the same protocol, host, port, and credentials as separate fields. Source-IP allowlisting produces both client configurations without an authentication field.
The module rejects partial credentials, invalid ports, embedded paths, and credentials hidden inside the host value. It also sets Undici's response-size limit to 64 KiB for this IP check.
An HTTPS destination can still use an `http` proxy endpoint. The client establishes an HTTP CONNECT tunnel before negotiating destination TLS through that tunnel.
Do not disable certificate checks to make a proxy request pass. Check the proxy connection, destination, hostname, trust store, and any authorized inspection system.
How Do You Use a Proxy With Node.js Fetch?
Node.js Fetch can use an installed Undici `ProxyAgent` through the process-wide dispatcher. Restore the previous dispatcher before closing the agent during application shutdown.
Save this file as `fetch-proxy.js`:
The script changes the dispatcher before calling the built-in `fetch()` function. Node.js documents that `setGlobalDispatcher()` affects both Node.js and installed Undici requests.
`setGlobalDispatcher()` changes routing for compatible Undici requests throughout the process. Configure it during startup, before unrelated requests begin, in a long-running service.
The `finally` block restores the previous dispatcher and closes the proxy agent. A long-running server should close its shared dispatcher during controlled shutdown, not after every request.
Fetch fulfills its promise for HTTP error statuses, so the script checks `response.ok` explicitly. It also rejects redirects, applies a deadline, checks the content type, and validates the parsed IP. A dispatcher can reuse proxy and destination connections.
Reuse improves efficiency, but later requests may remain on an existing route until that connection closes. Use local Undici dispatchers or separate processes when concurrent jobs need different endpoints. One global `ProxyAgent` applies one proxy configuration to compatible requests.
How Do You Use a Proxy With Axios?
Axios uses an explicit `proxy` object containing the endpoint protocol, host, port, and optional authentication. This configuration applies to the request without changing Fetch or Undici dispatchers.
Save this file as `axios-proxy.js`:
The `adapter: 'http'` setting keeps the example on Axios's Node.js HTTP adapter. The Axios request configuration documents explicit proxies and supported environment variables.
`maxRedirects: 0` exposes redirects instead of following them silently. The timeout aborts a request exceeding 15 seconds, while `maxContentLength` bounds the buffered response body.
The example requests text and parses JSON itself. Manual parsing prevents silent Axios behavior from accepting malformed JSON as a usable result.
Axios sends Basic proxy credentials to the proxy, not the destination. Destination authentication uses separate headers or options and should never reuse proxy secrets.
An explicit proxy object is useful when one Axios instance owns one routing policy. Create separate instances when projects need different endpoints, sessions, or location settings.
Do not combine an Axios `proxy` object with a custom proxy agent unless the design requires that agent. When an agent owns routing, set `proxy: false` so Axios does not add another proxy layer.
How Do You Use a Proxy With Undici?
Undici routes `request()` through a `ProxyAgent` supplied as the request dispatcher. This keeps the routing choice local without changing the process-wide dispatcher.
Save this file as `undici-proxy.js`:
`undici.request()` returns the status, headers, and a Node.js `Readable` body stream. The example consumes that body before cleanup. Long-running services must consume bodies before pooled connections can be reused.
The same agent can serve many requests in a long-running application. Create it once, pass it to related tasks, and close it during shutdown. A local dispatcher is useful when different task groups require different proxy endpoints.
Each group can own an agent without changing Fetch behavior elsewhere in the process. The example disables automatic redirects and checks every returned status. Production code can follow approved redirects after validating each destination against an allowlist.
Undici exposes deeper streaming and pooling controls than Fetch. Direct controls matter when response bodies are large or the service handles sustained traffic.
Do not call `body.text()` for an unbounded response. This HTTPS check uses an agent limit and verifies the downloaded text's byte length.
How Do You Validate the Proxy Exit and Returned Data?
Proxy validation should confirm both the network route and the response shape. An observed exit IP proves routing, while content checks prove that the result is usable.
Save this shared validator as `public-ip.js`:
`errorMessage()` retains four unique messages from the error cause chain. This exposes a nested proxy `407` behind Fetch's generic `fetch failed` message.
Run each client with the same endpoint settings:
Each successful command prints the public IP observed by the validation service. Set `EXPECTED_PROXY_IP` when the provider supplies a known exit or a controlled test value.
Do not use the documentation address above as a real expectation. Replace it with the observed address required by your controlled test.
We ran all three clients through an authenticated HTTP proxy and observed the expected exit. Each exposed incorrect credentials as a `407` failure and rejected deliberate exit-IP mismatches.
An IP check does not prove that the intended destination returns correct data. The how to test proxies guide separates route health from target-specific validation.
For an application request, validate required fields, page type, locale, freshness, and source context. HTTP 200 can still contain a challenge, consent page, empty response, or unrelated data.
What Do HTTPS, DNS, Connections, and Sessions Change?
HTTPS, DNS, connections, and sessions affect different parts of a proxied Node.js request. Separating those layers prevents a routing problem from becoming an application guess.
For an HTTPS destination through an HTTP proxy, the client usually sends CONNECT with the destination hostname and port. The destination TLS handshake occurs after the tunnel opens. The proxy normally resolves a hostname carried in that tunnel request.
Exact DNS behavior changes with proxy type, client, endpoint format, and whether an address was resolved earlier. Proxy authentication proves that the client may use the endpoint. Destination authentication remains separate and travels inside the protected destination exchange.
A live connection can carry several requests through connection reuse. A provider session can survive one connection and influence which exit later connections receive. Rotating settings do not promise a unique IP for every request.
Connection reuse, available inventory, session credentials, and provider rules all affect the observed result. Sticky sessions help related requests retain one exit when possible. Cookies, tokens, and application state still belong to the Node.js client or its surrounding application.
This tutorial uses HTTP proxy endpoints because all three clients support them directly. SOCKS5 requires a compatible dispatcher or agent and should use the provider's exact endpoint scheme.
Which Node.js Proxy Errors Should You Check First?
Node.js proxy troubleshooting starts by classifying the failed connection stage. The responder and stage determine whether a status came from the proxy or destination.
| Symptom | Likely layer | First check |
|---|---|---|
| `ENOTFOUND` | Proxy hostname or DNS | Confirm the endpoint hostname and resolver |
| `ECONNREFUSED` | Proxy endpoint | Confirm the host, port, availability, and network policy |
| Timeout or `UND_ERR_CONNECT_TIMEOUT` | Network, proxy, or destination | Measure each connection stage and compare a direct baseline |
| `407 Proxy Authentication Required` | Proxy authentication | Check credentials, encoding, or source-IP allowlisting |
| Proxy `403` before a tunnel | Proxy policy | Confirm the account, destination, and permitted port |
| Destination `403` after a tunnel | Destination policy or request context | Inspect the returned body and authorization |
| `429 Too Many Requests` | Destination rate policy | Honor `Retry-After` and reduce request pressure |
| HTTP 200 with invalid content | Validation or page type | Check content type, required fields, locale, and markers |
| Exit IP does not change | Session or connection reuse | Check credentials, session mode, agent reuse, and provider behavior |
Do not retry `407` with the same rejected credentials. Fix the authentication method or endpoint configuration before another attempt. A CONNECT success does not guarantee a successful destination response.
Record the proxy stage, target status, duration, route label, and validation result separately. Avoid printing Axios configuration objects or proxy URLs during debugging. They can contain usernames, passwords, tokens, and internal endpoint details.
Preserve a small sanitized response sample when content validation fails. The saved sample helps distinguish selector changes, target responses, and routing mistakes.
How Should You Run Proxied Node.js Requests at Scale?
Scaled Node.js proxy workloads need bounded queues, reusable clients, per-target limits, and one controlled retry policy. More simultaneous promises can increase failures without improving valid output.
Create one dispatcher or Axios instance for each routing policy. Reuse it across related requests, then close owned resources during graceful shutdown. Map sticky sessions to task boundaries rather than workers alone.
Related requests should retain their session identifier, cookies, and application state until the task finishes. Use rotating sessions for independent observations that benefit from route diversity. The proxy rotation strategy should define rotation boundaries, stop conditions, and measurements.
Place work into a finite queue and enforce a per-target request rate. Track active requests, queued tasks, retries, response bytes, and accepted results. Retry only temporary failures with a small limit, backoff, and jitter.
Honor `Retry-After`, and keep retries in one layer to prevent attempt multiplication. A managed proxy pool still needs response validation and route health checks. Quarantine failing routes using evidence instead of one ambiguous target response.
Measure cost per valid result, not raw request count. Include bandwidth, retries, rejected responses, parser failures, and downstream processing in that calculation.
Collect only authorized data, respect target-specific rules, and use official APIs when they meet the requirement. Stop when access is withdrawn rather than rotating to defeat that decision.
How Does Proxidize Support Node.js Proxy Workloads?
Proxidize provides managed proxy endpoints that work with standard Node.js clients. Fetch, Axios, and Undici can use the HTTP configuration demonstrated here. No provider-specific Node.js library is required.
Residential Proxies suit global data collection that needs country, city, or ISP targeting. Access points support rotating or sticky sessions through standard proxy credentials.
Best For: Residential Proxies suit global Node.js collection that needs broad location coverage and route diversity.
Use one access point for each routing policy or project boundary. The Node.js application can keep request, parsing, retry, and storage logic independent from location settings.
Mobile Proxies fit authorized tests that specifically require a United States mobile-network route. They should not replace residential routes for general global coverage.
Best For: Mobile Proxies suit Node.js tests that require a United States mobile-network observation.
Proxidize controls eligible routes and session behavior, while the client validates returned data. A sticky session does not store cookies or guarantee permanent access to one exit.
Start with one representative request through the intended access point. Confirm location, session behavior, latency, transferred bytes, and valid output before increasing traffic.
Define the route requirement and confirm the direct client first. Then choose a plan and apply the same validation rules to each route.
What Should You Remember About Proxies in Node.js?
Node.js proxy support works best when one component owns routing and every response is checked. Choose the client interface that already fits the application.
- Use the global dispatcher when built-in Fetch should share one proxy policy.
- Use Axios's explicit proxy object when an Axios instance owns the route.
- Use a local Undici dispatcher when task groups need separate proxy policies.
- Keep proxy and destination authentication separate, and never log either secret.
- Validate the observed exit and required data before counting a request as successful.
- Bound concurrency, retries, response size, and session lifetime before scaling.
Frequently asked questions
Node.js Fetch supports proxies through a compatible Undici dispatcher. An installed `ProxyAgent` can become the global dispatcher before Fetch runs inside the application process. Node.js 24 can also read conventional proxy environment variables when built-in proxy support is enabled at startup.
Pass an `auth` object inside Axios's `proxy` configuration, using the proxy username and password. Keep those values in environment variables or a secret manager. Proxy authentication is separate from destination `Authorization`, and Axios sends those credentials only to the proxy.
Node.js Fetch is powered by a bundled Undici version, but the installed Undici package is separate. Fetch exposes the standard response interface, while the package adds APIs such as `request()` and `ProxyAgent`. Mixing dispatcher versions directly can create compatibility problems, so test the exact runtime and package combination.
Undici 8.12.3 supports SOCKS5 through `ProxyAgent`, which delegates to its experimental `Socks5ProxyAgent` implementation. Verify the installed version and test that path before production use. Use the provider's SOCKS5 endpoint as supplied, and do not relabel an HTTP proxy URL for an HTTPS destination.
The proxy IP may remain unchanged because the client reused a connection or the provider retained a sticky session. Available routes and rotation rules also affect selection. Confirm the session credentials, connection lifecycle, and observed exit before assuming that every request should receive a unique address.
Fetch suits applications using standard web APIs, Axios suits established Axios services, and Undici suits direct dispatcher control. None is universally best for scraping. Choose based on response handling, streaming, interceptors, pooling, and existing code, then apply the same pacing and validation rules.
Proxy credentials should not be committed inside a URL in source code. Read them from environment variables or a secret manager, then build authentication at runtime. Redact errors because configuration objects can expose secrets even when normal logs never print the original environment variables.