unjs/httpxy

πŸ”€ A Full-Featured HTTP and WebSocket Proxy for Node.js

344

stars

174

commits

TypeScript

primary language

Sep 10, 2026

updated

README

πŸ”€ httpxy

npm version npm downloads bundle Codecov

A Full-Featured HTTP and WebSocket Proxy for Node.js

Proxy Fetch

proxyFetch is a proxy utility with web standard (Request/Response) interfaces. It forwards requests to a specific server address (TCP host/port or Unix socket), bypassing the URL's hostname.

import { proxyFetch } from "httpxy";

// TCP β€” using a URL string
const res = await proxyFetch("http://127.0.0.1:3000", "http://example.com/api/data");
console.log(await res.json());

// Unix socket β€” using a URL string
const res2 = await proxyFetch("unix:/tmp/app.sock", "http://localhost/health");
console.log(await res2.text());

// Or use an object for more control
const res3 = await proxyFetch({ host: "127.0.0.1", port: 3000 }, "http://example.com/api/data");

// Using a Request object
const req = new Request("http://example.com/api/data", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ key: "value" }),
});
const res4 = await proxyFetch("http://127.0.0.1:3000", req);

// Using a URL string with RequestInit
const res5 = await proxyFetch("http://127.0.0.1:3000", "http://example.com/api/data", {
  method: "PUT",
  headers: { Authorization: "Bearer token" },
  body: JSON.stringify({ updated: true }),
});

It accepts the same input and init arguments as the global fetch, including Request objects and streaming bodies, and returns a standard Response. Redirects are handled manually by default.

Proxy Upgrade

proxyUpgrade is a standalone WebSocket upgrade proxy. It forwards upgrade requests to a target server without needing a ProxyServer instance β€” the WebSocket counterpart to proxyFetch.

import { createServer } from "node:http";
import { proxyUpgrade } from "httpxy";

const server = createServer((req, res) => {
  // Handle regular HTTP requests...
});

server.on("upgrade", (req, socket, head) => {
  proxyUpgrade("http://127.0.0.1:8080", req, socket, head);
});

server.listen(3000);

It accepts the same addr formats as proxyFetch ("http://host:port", "unix:/path", or { host, port } / { socketPath }), and returns a Promise<Socket> that resolves with the upstream proxy socket once the WebSocket connection is established.

// With options
server.on("upgrade", (req, socket, head) => {
  proxyUpgrade({ host: "127.0.0.1", port: 8080 }, req, socket, head, {
    // changeOrigin: true, // rewrite Host header
    // xfwd: false, // disable x-forwarded-* headers (enabled by default)
  });
});

Proxy Server

[!NOTE] Proxy server was originally forked from http-party/node-http-proxy.

Create proxy:

import { createServer } from "node:http";
import { createProxyServer } from "httpxy";

const proxy = createProxyServer({});

const server = createServer(async (req, res) => {
  try {
    await proxy.web(req, res, {
      target: address /* address of your proxy server here */,
    });
  } catch (error) {
    console.error(error);
    res.statusCode = 500;
    res.end("Proxy error: " + error.toString());
  }
});

server.listen(3000, () => {
  console.log("Proxy is listening on http://localhost:3000");
});

Options

OptionTypeDefaultDescription
targetstring | URL | ProxyTargetDetailedβ€”Target server URL
forwardstring | URLβ€”Forward server URL (pipes request without the target's response)
agenthttp.Agent | falsekeep-aliveShared keep-alive agent by default. Set false to disable connection reuse
sslhttps.ServerOptionsβ€”Object passed to https.createServer()
wsbooleanfalseEnable WebSocket proxying
xfwdbooleanfalseAdd x-forwarded-* headers
securebooleanβ€”Verify SSL certificates
toProxybooleanfalsePass absolute URL as path (proxy-to-proxy)
prependPathbooleantruePrepend the target's path to the proxy path
ignorePathbooleanfalseIgnore the incoming request path
localAddressstringβ€”Local interface to bind for outgoing connections
changeOriginbooleanfalseChange the Host header to match the target URL
preserveHeaderKeyCasebooleanfalseKeep original letter case of response header keys
authstringβ€”Basic authentication ('user:password') for Authorization header
hostRewritestringβ€”Rewrite the Location hostname on redirects (301/302/307/308)
autoRewritebooleanfalseRewrite Location host/port on redirects based on the request
protocolRewritestringβ€”Rewrite Location protocol on redirects ('http' or 'https')
cookieDomainRewritefalse | string | objectfalseRewrite domain of Set-Cookie headers
cookiePathRewritefalse | string | objectfalseRewrite path of Set-Cookie headers
headersobjectβ€”Extra headers to add to target requests
proxyTimeoutnumber120000Timeout (ms) for the proxy request to the target
timeoutnumberβ€”Timeout (ms) for the incoming request
selfHandleResponsebooleanfalseDisable automatic response piping (handle proxyRes yourself)
followRedirectsboolean | numberfalseFollow HTTP redirects from target. true = max 5 hops; number = custom max
bufferstream.Streamβ€”Stream to use as request body instead of the incoming request

[!NOTE] Redirect rewriting (hostRewrite, autoRewrite, protocolRewrite) only applies when the Location header host matches the target host, so cross-origin redirects are never altered. A protocol-relative Location (//host/path) is kept protocol-relative when only the host is rewritten (hostRewrite/autoRewrite), so the client resolves the scheme itself. Setting protocolRewrite explicitly opts into a concrete scheme and absolutizes such values (e.g. //host/path β†’ https://host/path).

Events

EventArgumentsDescription
error(err, req, res, target)An error occurred during proxying
proxyReq(proxyReq, req, res, options)Before request is sent to target (modify headers here)
proxyRes(proxyRes, req, res)Response received from target
proxyReqWs(proxyReq, req, socket, options, head)Before WebSocket upgrade request is sent
open(proxySocket)WebSocket connection opened
close(proxyRes, proxySocket, proxyHead)WebSocket connection closed
start(req, res, target)Proxy processing started
end(req, res, proxyRes)Proxy request completed

Examples

HTTP Proxy

import { createServer } from "node:http";
import { createProxyServer } from "httpxy";

const proxy = createProxyServer({});

const server = createServer(async (req, res) => {
  await proxy.web(req, res, { target: "http://localhost:8080" });
});

server.listen(3000);

WebSocket Proxy

import { createServer } from "node:http";
import { createProxyServer } from "httpxy";

const proxy = createProxyServer({ target: "http://localhost:8080", ws: true });

const server = createServer(async (req, res) => {
  await proxy.web(req, res);
});

server.on("upgrade", (req, socket, head) => {
  proxy.ws(req, socket, { target: "http://localhost:8080" }, head);
});

server.listen(3000);

Modify Request Headers

import { createServer } from "node:http";
import { createProxyServer } from "httpxy";

const proxy = createProxyServer({ target: "http://localhost:8080" });

proxy.on("proxyReq", (proxyReq) => {
  proxyReq.setHeader("X-Forwarded-By", "httpxy");
});

const server = createServer(async (req, res) => {
  await proxy.web(req, res);
});

server.listen(3000);

HTTPS Proxy

import { readFileSync } from "node:fs";
import { createProxyServer } from "httpxy";

const proxy = createProxyServer({
  ssl: {
    key: readFileSync("server-key.pem", "utf8"),
    cert: readFileSync("server-cert.pem", "utf8"),
  },
  target: "https://localhost:8443",
  secure: false, // allow self-signed certificates
});

proxy.listen(3000);

Standalone Proxy Server

import { createProxyServer } from "httpxy";

const proxy = createProxyServer({
  target: "http://localhost:8080",
  changeOrigin: true,
});

proxy.listen(3000);

Development

  • Clone this repository
  • Install latest LTS version of Node.js
  • Enable Corepack using corepack enable
  • Install dependencies using pnpm install
  • Run interactive tests using pnpm dev

Acknowledgements

Performance optimizations in httpxy were inspired by analysis of fast-proxy and @fastify/http-proxy.

License

Made with πŸ’›

Published under MIT License.

Contributors

pi0

92 commits

renovate[bot]

51 commits

SukkaW

6 commits

unjs/httpxy

πŸ”€ A Full-Featured HTTP and WebSocket Proxy for Node.js

344

stars

174

commits

TypeScript

primary language

Sep 10, 2026

updated

README

πŸ”€ httpxy

npm version npm downloads bundle Codecov

A Full-Featured HTTP and WebSocket Proxy for Node.js

Proxy Fetch

proxyFetch is a proxy utility with web standard (Request/Response) interfaces. It forwards requests to a specific server address (TCP host/port or Unix socket), bypassing the URL's hostname.

import { proxyFetch } from "httpxy";

// TCP β€” using a URL string
const res = await proxyFetch("http://127.0.0.1:3000", "http://example.com/api/data");
console.log(await res.json());

// Unix socket β€” using a URL string
const res2 = await proxyFetch("unix:/tmp/app.sock", "http://localhost/health");
console.log(await res2.text());

// Or use an object for more control
const res3 = await proxyFetch({ host: "127.0.0.1", port: 3000 }, "http://example.com/api/data");

// Using a Request object
const req = new Request("http://example.com/api/data", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ key: "value" }),
});
const res4 = await proxyFetch("http://127.0.0.1:3000", req);

// Using a URL string with RequestInit
const res5 = await proxyFetch("http://127.0.0.1:3000", "http://example.com/api/data", {
  method: "PUT",
  headers: { Authorization: "Bearer token" },
  body: JSON.stringify({ updated: true }),
});

It accepts the same input and init arguments as the global fetch, including Request objects and streaming bodies, and returns a standard Response. Redirects are handled manually by default.

Proxy Upgrade

proxyUpgrade is a standalone WebSocket upgrade proxy. It forwards upgrade requests to a target server without needing a ProxyServer instance β€” the WebSocket counterpart to proxyFetch.

import { createServer } from "node:http";
import { proxyUpgrade } from "httpxy";

const server = createServer((req, res) => {
  // Handle regular HTTP requests...
});

server.on("upgrade", (req, socket, head) => {
  proxyUpgrade("http://127.0.0.1:8080", req, socket, head);
});

server.listen(3000);

It accepts the same addr formats as proxyFetch ("http://host:port", "unix:/path", or { host, port } / { socketPath }), and returns a Promise<Socket> that resolves with the upstream proxy socket once the WebSocket connection is established.

// With options
server.on("upgrade", (req, socket, head) => {
  proxyUpgrade({ host: "127.0.0.1", port: 8080 }, req, socket, head, {
    // changeOrigin: true, // rewrite Host header
    // xfwd: false, // disable x-forwarded-* headers (enabled by default)
  });
});

Proxy Server

[!NOTE] Proxy server was originally forked from http-party/node-http-proxy.

Create proxy:

import { createServer } from "node:http";
import { createProxyServer } from "httpxy";

const proxy = createProxyServer({});

const server = createServer(async (req, res) => {
  try {
    await proxy.web(req, res, {
      target: address /* address of your proxy server here */,
    });
  } catch (error) {
    console.error(error);
    res.statusCode = 500;
    res.end("Proxy error: " + error.toString());
  }
});

server.listen(3000, () => {
  console.log("Proxy is listening on http://localhost:3000");
});

Options

OptionTypeDefaultDescription
targetstring | URL | ProxyTargetDetailedβ€”Target server URL
forwardstring | URLβ€”Forward server URL (pipes request without the target's response)
agenthttp.Agent | falsekeep-aliveShared keep-alive agent by default. Set false to disable connection reuse
sslhttps.ServerOptionsβ€”Object passed to https.createServer()
wsbooleanfalseEnable WebSocket proxying
xfwdbooleanfalseAdd x-forwarded-* headers
securebooleanβ€”Verify SSL certificates
toProxybooleanfalsePass absolute URL as path (proxy-to-proxy)
prependPathbooleantruePrepend the target's path to the proxy path
ignorePathbooleanfalseIgnore the incoming request path
localAddressstringβ€”Local interface to bind for outgoing connections
changeOriginbooleanfalseChange the Host header to match the target URL
preserveHeaderKeyCasebooleanfalseKeep original letter case of response header keys
authstringβ€”Basic authentication ('user:password') for Authorization header
hostRewritestringβ€”Rewrite the Location hostname on redirects (301/302/307/308)
autoRewritebooleanfalseRewrite Location host/port on redirects based on the request
protocolRewritestringβ€”Rewrite Location protocol on redirects ('http' or 'https')
cookieDomainRewritefalse | string | objectfalseRewrite domain of Set-Cookie headers
cookiePathRewritefalse | string | objectfalseRewrite path of Set-Cookie headers
headersobjectβ€”Extra headers to add to target requests
proxyTimeoutnumber120000Timeout (ms) for the proxy request to the target
timeoutnumberβ€”Timeout (ms) for the incoming request
selfHandleResponsebooleanfalseDisable automatic response piping (handle proxyRes yourself)
followRedirectsboolean | numberfalseFollow HTTP redirects from target. true = max 5 hops; number = custom max
bufferstream.Streamβ€”Stream to use as request body instead of the incoming request

[!NOTE] Redirect rewriting (hostRewrite, autoRewrite, protocolRewrite) only applies when the Location header host matches the target host, so cross-origin redirects are never altered. A protocol-relative Location (//host/path) is kept protocol-relative when only the host is rewritten (hostRewrite/autoRewrite), so the client resolves the scheme itself. Setting protocolRewrite explicitly opts into a concrete scheme and absolutizes such values (e.g. //host/path β†’ https://host/path).

Events

EventArgumentsDescription
error(err, req, res, target)An error occurred during proxying
proxyReq(proxyReq, req, res, options)Before request is sent to target (modify headers here)
proxyRes(proxyRes, req, res)Response received from target
proxyReqWs(proxyReq, req, socket, options, head)Before WebSocket upgrade request is sent
open(proxySocket)WebSocket connection opened
close(proxyRes, proxySocket, proxyHead)WebSocket connection closed
start(req, res, target)Proxy processing started
end(req, res, proxyRes)Proxy request completed

Examples

HTTP Proxy

import { createServer } from "node:http";
import { createProxyServer } from "httpxy";

const proxy = createProxyServer({});

const server = createServer(async (req, res) => {
  await proxy.web(req, res, { target: "http://localhost:8080" });
});

server.listen(3000);

WebSocket Proxy

import { createServer } from "node:http";
import { createProxyServer } from "httpxy";

const proxy = createProxyServer({ target: "http://localhost:8080", ws: true });

const server = createServer(async (req, res) => {
  await proxy.web(req, res);
});

server.on("upgrade", (req, socket, head) => {
  proxy.ws(req, socket, { target: "http://localhost:8080" }, head);
});

server.listen(3000);

Modify Request Headers

import { createServer } from "node:http";
import { createProxyServer } from "httpxy";

const proxy = createProxyServer({ target: "http://localhost:8080" });

proxy.on("proxyReq", (proxyReq) => {
  proxyReq.setHeader("X-Forwarded-By", "httpxy");
});

const server = createServer(async (req, res) => {
  await proxy.web(req, res);
});

server.listen(3000);

HTTPS Proxy

import { readFileSync } from "node:fs";
import { createProxyServer } from "httpxy";

const proxy = createProxyServer({
  ssl: {
    key: readFileSync("server-key.pem", "utf8"),
    cert: readFileSync("server-cert.pem", "utf8"),
  },
  target: "https://localhost:8443",
  secure: false, // allow self-signed certificates
});

proxy.listen(3000);

Standalone Proxy Server

import { createProxyServer } from "httpxy";

const proxy = createProxyServer({
  target: "http://localhost:8080",
  changeOrigin: true,
});

proxy.listen(3000);

Development

  • Clone this repository
  • Install latest LTS version of Node.js
  • Enable Corepack using corepack enable
  • Install dependencies using pnpm install
  • Run interactive tests using pnpm dev

Acknowledgements

Performance optimizations in httpxy were inspired by analysis of fast-proxy and @fastify/http-proxy.

License

Made with πŸ’›

Published under MIT License.

Contributors

pi0

92 commits

renovate[bot]

51 commits

SukkaW

6 commits

Languages

TypeScript

91.0%

JavaScript

8.9%