Skip to main content

Function Signature

function createProxyServer(options: ProxyServerOptions): ProxyServer
Creates an HTTP proxy server that routes requests based on the Host header. Returns a ProxyServer (either http.Server or net.Server when TLS is enabled).

Parameters

options
ProxyServerOptions
required
Configuration object for the proxy server

Return Value

ProxyServer
http.Server | net.Server
Returns a Node.js server instance that can be started with .listen(). When TLS is enabled, returns a net.Server that wraps both HTTP/2 and HTTP/1.1 servers.

Features

HTTP/2 Support

When TLS configuration is provided, the proxy server automatically:
  • Enables HTTP/2 with multiplexing for better performance
  • Falls back to HTTP/1.1 for WebSocket connections
  • Accepts both plain HTTP and HTTPS on the same port

WebSocket Support

The proxy transparently handles WebSocket upgrades:
  • Forwards the complete handshake including Sec-WebSocket-Accept headers
  • Preserves subprotocol negotiation
  • Maintains bidirectional streaming

Loop Detection

The proxy automatically detects and prevents forwarding loops:
  • Tracks request hops using the X-Portless-Hops header
  • Rejects requests after 5 hops with HTTP 508
  • Provides helpful error messages suggesting changeOrigin: true for dev server proxies

Automatic Header Forwarding

The proxy adds standard forwarding headers to all proxied requests:
  • X-Forwarded-For: Client IP address
  • X-Forwarded-Proto: Original protocol (http/https)
  • X-Forwarded-Host: Original Host header
  • X-Forwarded-Port: Original port number

Example: Basic HTTP Proxy

import { createProxyServer } from "portless";

const routes = [
  { hostname: "api.localhost", port: 3000 },
  { hostname: "app.localhost", port: 4000 },
];

const server = createProxyServer({
  getRoutes: () => routes,
  proxyPort: 8080,
});

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

Example: HTTPS Proxy with TLS

import { createProxyServer } from "portless";
import * as fs from "node:fs";

const server = createProxyServer({
  getRoutes: () => store.loadRoutes(),
  proxyPort: 443,
  tls: {
    cert: fs.readFileSync("/path/to/cert.pem"),
    key: fs.readFileSync("/path/to/key.pem"),
  },
});

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

Example: Dynamic Routing

import { createProxyServer, RouteStore } from "portless";

const store = new RouteStore("/tmp/portless");
store.ensureDir();

const server = createProxyServer({
  getRoutes: () => store.loadRoutes(),
  proxyPort: 8080,
  onError: (msg) => console.error(`[ERROR] ${msg}`),
});

server.listen(8080);

// Routes can be added/removed dynamically
store.addRoute("new-app.localhost", 5000, process.pid);

// The next request will see the updated routes

Error Handling

The proxy server handles several error conditions automatically:
Status CodeConditionBehavior
400Missing Host headerReturns plain text error
404No route for hostnameReturns HTML page listing active routes
502Backend connection failedReturns HTML error page
508Loop detectedReturns HTML error with fix suggestion

Type Definitions

type ProxyServer = http.Server | net.Server;

interface ProxyServerOptions {
  getRoutes: () => RouteInfo[];
  proxyPort: number;
  onError?: (message: string) => void;
  tls?: {
    cert: Buffer;
    key: Buffer;
    SNICallback?: (
      servername: string,
      cb: (err: Error | null, ctx?: import("node:tls").SecureContext) => void
    ) => void;
  };
}

interface RouteInfo {
  hostname: string;
  port: number;
}

RouteStore

Manage route mappings with persistence

Type Definitions

Complete type reference

Build docs developers (and LLMs) love