Extensions for JavaScript HTTP libraries to support sending and receiving custom proxy headers during HTTPS CONNECT tunneling.
When making HTTPS requests through a proxy, the connection is established via a CONNECT tunnel. During this process:
-
Sending headers to the proxy - Most JavaScript HTTP libraries don't provide a way to send custom headers (like
X-ProxyMesh-Country) to the proxy server during the CONNECT handshake. -
Receiving headers from the proxy - The proxy's response headers from the CONNECT request are typically discarded, making it impossible to read custom headers (like
X-ProxyMesh-IP) that the proxy sends back.
This library solves both problems for popular JavaScript HTTP libraries.
| Library | Subpath export | Notes |
|---|---|---|
| axios | javascript-proxy-headers/axios |
Widely used client |
| node-fetch | javascript-proxy-headers/node-fetch |
Fetch API on Node |
| got | javascript-proxy-headers/got |
Ergonomic API |
| undici | javascript-proxy-headers/undici |
Node’s fast HTTP stack |
| superagent | javascript-proxy-headers/superagent |
Chaining API |
| ky | javascript-proxy-headers/ky |
Tiny fetch wrapper |
| wretch | javascript-proxy-headers/wretch |
Fetch wrapper (sets wretch’s global fetch polyfill) |
| make-fetch-happen | javascript-proxy-headers/make-fetch-happen |
npm-style fetch (cache, retries, proxy) |
| needle | javascript-proxy-headers/needle |
Lean HTTP client |
| typed-rest-client | javascript-proxy-headers/typed-rest-client |
Azure / DevOps–style REST client |
urllib is not integrated yet: it expects an undici Dispatcher, not a Node Agent. See notes/urllib-integration-deferred.md in this repo for a possible approach.
npm install javascript-proxy-headersThen install the HTTP client(s) you use (for example axios, got, ky, wretch, make-fetch-happen, needle, or typed-rest-client). Each is an optional peer dependency.
Note: This package has no runtime dependencies by default—install only the adapters you need.
import { createProxyAxios } from 'javascript-proxy-headers/axios';
const client = createProxyAxios({
proxy: 'http://user:pass@proxy.example.com:8080',
proxyHeaders: { 'X-ProxyMesh-Country': 'US' }
});
const response = await client.get('https://httpbin.org/ip');
// Proxy headers are merged into response.headers
console.log(response.headers['x-proxymesh-ip']);import { proxyFetch } from 'javascript-proxy-headers/node-fetch';
const response = await proxyFetch('https://httpbin.org/ip', {
proxy: 'http://user:pass@proxy.example.com:8080',
proxyHeaders: { 'X-ProxyMesh-Country': 'US' }
});
// Proxy headers available on response
console.log(response.proxyHeaders.get('x-proxymesh-ip'));import { createProxyGot } from 'javascript-proxy-headers/got';
const client = createProxyGot({
proxy: 'http://user:pass@proxy.example.com:8080',
proxyHeaders: { 'X-ProxyMesh-Country': 'US' }
});
const response = await client('https://httpbin.org/ip');
console.log(response.headers['x-proxymesh-ip']);import { request } from 'javascript-proxy-headers/undici';
const { statusCode, headers, body, proxyHeaders } = await request(
'https://httpbin.org/ip',
{
proxy: 'http://user:pass@proxy.example.com:8080',
proxyHeaders: { 'X-ProxyMesh-Country': 'US' }
}
);
console.log(proxyHeaders.get('x-proxymesh-ip'));Uses a custom fetch built from node-fetch + ProxyHeadersAgent (ky.create({ fetch })).
import { createProxyKy } from 'javascript-proxy-headers/ky';
const api = await createProxyKy({
proxy: 'http://user:pass@proxy.example.com:8080',
proxyHeaders: { 'X-ProxyMesh-Country': 'US' }
});
const response = await api('https://httpbin.org/ip');
console.log(response.proxyHeaders.get('x-proxymesh-ip'));Registers the same custom fetch as wretch’s fetch polyfill. Use the normal wretch chain (for example .get().res()).
import { createProxyWretch } from 'javascript-proxy-headers/wretch';
const wretch = await createProxyWretch({
proxy: 'http://user:pass@proxy.example.com:8080',
proxyHeaders: { 'X-ProxyMesh-Country': 'US' }
});
const response = await wretch('https://httpbin.org/ip').get().res();
console.log(response.proxyHeaders.get('x-proxymesh-ip'));Passes a ProxyHeadersAgent as agent; @npmcli/agent uses it as-is when set.
import { createProxyMakeFetchHappen } from 'javascript-proxy-headers/make-fetch-happen';
const fetch = createProxyMakeFetchHappen({
proxy: 'http://user:pass@proxy.example.com:8080',
proxyHeaders: { 'X-ProxyMesh-Country': 'US' }
});
const response = await fetch('https://httpbin.org/ip');
console.log(response.proxyHeaders.get('x-proxymesh-ip'));import { proxyNeedleGet } from 'javascript-proxy-headers/needle';
const res = await proxyNeedleGet('https://httpbin.org/ip', {
proxy: 'http://user:pass@proxy.example.com:8080',
proxyHeaders: { 'X-ProxyMesh-Country': 'US' }
});
// CONNECT response headers merged onto res.headers where missing
console.log(res.headers['x-proxymesh-ip']);Uses a subclass of HttpClient that routes HTTPS through ProxyHeadersAgent (no tunnel agent).
import { createProxyRestClient } from 'javascript-proxy-headers/typed-rest-client';
const client = createProxyRestClient({
userAgent: 'my-app',
proxy: 'http://user:pass@proxy.example.com:8080',
proxyHeaders: { 'X-ProxyMesh-Country': 'US' }
});
await client.get('https://httpbin.org/ip');
console.log(client.proxyAgent.lastProxyHeaders?.get('x-proxymesh-ip'));For direct control, use the core ProxyHeadersAgent:
import { ProxyHeadersAgent } from 'javascript-proxy-headers';
import https from 'https';
const agent = new ProxyHeadersAgent('http://proxy.example.com:8080', {
proxyHeaders: { 'X-ProxyMesh-Country': 'US' },
onProxyConnect: (headers) => {
console.log('Proxy IP:', headers.get('x-proxymesh-ip'));
}
});
https.get('https://httpbin.org/ip', { agent }, (res) => {
// Handle response
});Integration tests need a real proxy (set PROXY_URL or HTTPS_PROXY):
export PROXY_URL='http://user:pass@proxy.example.com:8080'
npm test # all adapters (see package.json "test")
node run_tests.js -v # same harness from repo root
npm run test:ts # same checks via tsx + TypeScript harness
npm run test:types # `tsc --noEmit` only (no network)
# Limit modules
node test/test_proxy_headers.js -v axios kyVerbose (-v) prints captured header values. See test/test_proxy_headers.js --help.
- Node.js >= 18.0.0
- One or more supported HTTP libraries
- python-proxy-headers - Same functionality for Python
- proxy-examples - Example code for using proxies
Created by ProxyMesh to help our customers use custom headers to control proxy behavior. Works with any proxy that supports custom headers.
MIT License