undici-proxy-env
v1.0.0
Published
Make Node's built-in fetch honor HTTP(S)_PROXY — and survive the Clash/corporate-proxy response mangling that silently breaks LLM SDKs.
Maintainers
Readme
undici-proxy-env
Your OpenAI / Anthropic / any-SDK call silently fails behind a proxy — but curl works fine? It's because Node's built-in fetch (undici) ignores HTTPS_PROXY / HTTP_PROXY. One call fixes it — plus the two response-mangling gotchas that local proxies like Clash introduce.
npm i undici-proxy-envimport { configureProxyFromEnv } from "undici-proxy-env";
// Call once, before any fetch/SDK use. Reads HTTPS_PROXY/HTTP_PROXY.
configureProxyFromEnv(); // → true if a proxy is now in effect
// ...now the Anthropic/OpenAI SDK, LangChain, plain fetch, etc. go through it.The three problems it solves
1. fetch ignores the proxy env vars. Node's http-module clients and curl honor HTTPS_PROXY; fetch/undici do not. So the SDK (which uses fetch) times out or 403s behind a corporate/region proxy while everything else works. configureProxyFromEnv() installs a global undici ProxyAgent from the env — every subsequent fetch goes through it.
2. Clash strips Content-Encoding but sends gzip. Some local proxies (Clash, others) drop the Content-Encoding: gzip header through CONNECT tunnels while still sending gzipped bytes — undici then hands your SDK raw gzip that fails to JSON-parse. We force Accept-Encoding: identity outbound so the upstream returns plain text.
3. Clash drops the response Content-Type. Without it, an SDK treats a JSON body as a string and messages.create() returns text instead of a parsed object. proxyAwareFetch re-injects application/json when the body looks like JSON:
import { configureProxyFromEnv, proxyAwareFetch } from "undici-proxy-env";
import Anthropic from "@anthropic-ai/sdk";
configureProxyFromEnv();
const client = new Anthropic({ fetch: proxyAwareFetch }); // survives header-stripping proxies(If your proxy doesn't strip headers you don't need proxyAwareFetch — the global agent from step 1 is enough. It's a safe pass-through either way.)
API
| Export | What it does |
|---|---|
| configureProxyFromEnv(opts?) | Install the global proxy from env. opts.url forces a URL; opts.log(msg) gets a one-line notice. Idempotent. Returns boolean (is a proxy now active). |
| isProxyInstalled() | true iff a proxy was installed. |
| proxyAwareFetch | A fetch drop-in that repairs a missing response Content-Type. Pass as an SDK's fetch option. |
Node ≥ 18. Zero config beyond the standard HTTPS_PROXY / HTTP_PROXY env vars.
License
MIT © oratis
