Skip to main content

2 posts tagged with "proxy"

View all tags

Node.js fetch ignores proxy env vars? undici doesn't read http_proxy

· 4 min read

In a WSL2 environment with https_proxy properly set, Node.js fetch() still times out when accessing external URLs.

Encountered this issue while building an AI-powered e-commerce tool for a client. Here's the root cause and solution.

TL;DR

Node.js 22+ built-in fetch() is powered by undici, which by design does not read http_proxy/https_proxy environment variables. Solution: install node-fetch@3 + https-proxy-agent, create a proxy-aware fetch instance. Falls back to direct connection when no proxy is configured in production.

Problem

WSL2 environment with https_proxy correctly set. curl works fine:

echo $https_proxy
# http://172.30.224.1:7897

curl -I https://httpbin.org/ip
# HTTP/1.1 200 OK

But Node.js fetch() times out:

await fetch('https://httpbin.org/ip');
// FetchError: fetch failed
// cause: TimeoutError: Headers Timeout Error

If you're also seeing WSL2 proxy completely unreachable (even curl fails), check your firewall settings first.

Root Cause

Node.js v22+ global fetch() is provided by the built-in undici 7.x. undici intentionally does not read http_proxy/https_proxy environment variables — this is by design, not a bug.

Proxy behavior across different HTTP clients:

ClientReads env varsUses proxy
curlAuto-reads https_proxy
Node.js http/https modulesDoes not read
axios / node-fetch@3Reads https_proxy
Node.js built-in fetch() (undici)Does not read

This causes fetch() to time out in environments that require a proxy to access external networks (WSL2, corporate networks).

Solution

Install node-fetch@3 and https-proxy-agent:

npm install node-fetch@3 https-proxy-agent

Create a proxy-aware fetch instance:

import fetch from 'node-fetch';
import { HttpsProxyAgent } from 'https-proxy-agent';

const proxyUrl = process.env.HTTPS_PROXY || process.env.HTTP_PROXY;
const agent = proxyUrl ? new HttpsProxyAgent(proxyUrl) : undefined;

export async function fetchWithProxy(url, options = {}) {
return fetch(url, { ...options, agent });
}

Usage is almost identical to the native fetch():

// Before
const res = await fetch('https://httpbin.org/ip');

// After
const res = await fetchWithProxy('https://httpbin.org/ip');

Why node-fetch instead of undici's ProxyAgent?

Node.js v24 ships with undici 7.x, but the npm [email protected] ProxyAgent is incompatible with the built-in version:

import { ProxyAgent, setGlobalDispatcher } from 'undici';

// Node v24 error: UND_ERR_INVALID_ARG
// npm undici@8 ProxyAgent is incompatible with built-in undici@7 setGlobalDispatcher
setGlobalDispatcher(new ProxyAgent(proxyUrl));

node-fetch@3 + https-proxy-agent is version-agnostic with no compatibility issues. In production without a proxy, agent is undefined and it connects directly.

Caveats

  • Don't try to override global fetch with setGlobalDispatcher — changes don't propagate to worker modules under tsx watch hot reload
  • npm [email protected] FormData types are incompatible with the global FormData, mixing them causes TypeScript compilation errors
  • node-fetch@3 is ESM-only, use import — no require() support

FAQ

Why doesn't Node.js fetch read the http_proxy environment variable?

Node.js 22+ built-in fetch is powered by undici, which by design does not read http_proxy/https_proxy environment variables. Use node-fetch or undici's ProxyAgent to configure proxy manually.

How to make Node.js fetch work through a proxy?

Install node-fetch@3 and https-proxy-agent, then create a fetch instance with proxy support. When no proxy is configured in production, it falls back to direct connection, independent of Node version.

WSL2 has other networking pitfalls — Docker Desktop's host mode also makes container ports unreachable from WSL2. The debugging approach is similar: verify curl connectivity first, then check application-level configuration.

Node version upgrades can introduce other issues too — for example, JWT key format changes in Node 24. Worth checking when upgrading.

Need help with Node.js networking issues?

Get in touch

Fix WSL2 Cannot Access Windows Host Proxy — Three Invisible Pitfalls

· 5 min read

Encountered this issue while using AI coding tools (Claude Code, Roo) in WSL2 that need to access APIs through the Windows host proxy. After fixing the firewall, encountered two more hidden pitfalls. Documenting all root causes and solutions.

TL;DR

WSL2's vEthernet (WSL) virtual NIC is created on every launch. Windows Firewall cannot assign a Network Profile to it, so all inbound rules are ineffective (EnforcementStatus: NotApplicable). Don't add rules — disable the firewall on that interface directly:

# Run in Windows PowerShell (Administrator)
Set-NetFirewallProfile -DisabledInterfaceAliases "vEthernet (WSL)"

Additionally, after fixing the firewall, you may encounter two more pitfalls:

  1. Dynamic IP Issue: Host IP changes after WSL/Windows restart
  2. Config Cache Issue: Stale API keys in ~/.claude.json and ~/.claude/settings.json cause auth conflicts