Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/binary-person/rammerhead/llms.txt

Use this file to discover all available pages before exploring further.

RammerheadProxy exposes two ordered middleware pipelines — one for HTTP requests and one for WebSocket upgrade handshakes — along with methods for registering custom HTTP and WebSocket routes. You can insert handlers at any position in the pipeline, inspect or modify requests, and short-circuit further processing by returning true.

How the pipelines work

When a request arrives, RammerheadProxy._onRequest iterates over onRequestPipeline in order. Each handler runs in sequence. If a handler returns true, the iteration stops and the request is not forwarded to the hammerhead rewriting engine. If no handler returns true, the request continues to the proxy. The upgrade pipeline works the same way, but receives the raw TCP socket and head buffer that accompany a WebSocket upgrade, which the request pipeline does not.

addToOnRequestPipeline

proxy.addToOnRequestPipeline(handler, beginning)
ParameterTypeDescription
handlerasync (req, res, serverInfo, isRoute, isWebsocket) => boolean | undefinedThe middleware function. Return true to terminate processing.
beginningboolean (default false)When true, inserts the handler at the front of the pipeline so it runs first. When false (default), inserts at the end.
Handler parameters:
  • req — Node.js http.IncomingMessage
  • res — Node.js http.ServerResponse, or a stream.Duplex if the request is a WebSocket upgrade forwarded through hammerhead
  • serverInfo — Hammerhead ServerInfo object (hostname, port, crossDomainPort, protocol, domain, cacheRequests)
  • isRoutetrue if the URL matches a registered GET/POST/WS route on the proxy (i.e., it is a proxy management endpoint, not a passthrough request)
  • isWebsockettrue if res is a Duplex stream (WebSocket upgrade)
isWebsocket appears in onRequestPipeline handlers even though there is a separate upgrade pipeline. This is because hammerhead forwards upgrade connections through _onRequest without the head buffer. Use onUpgradePipeline when you specifically need the head argument.

addToOnUpgradePipeline

proxy.addToOnUpgradePipeline(handler, beginning)
ParameterTypeDescription
handlerasync (req, socket, head, serverInfo, isRoute) => boolean | undefinedThe middleware function. Return true to terminate processing.
beginningboolean (default false)Inserts at the front when true.
Handler parameters:
  • req — Node.js http.IncomingMessage
  • socketstream.Duplex — the raw TCP socket
  • headBuffer — any data already read from the socket after the HTTP headers
  • serverInfo — Hammerhead ServerInfo object
  • isRoutetrue if the upgrade URL matches a registered WS route

Custom HTTP routes: GET and POST

Register custom HTTP endpoints on the proxy using the same GET and POST methods that hammerhead exposes. These routes are matched before proxy traffic.
proxy.GET('/status', (req, res) => {
  res.writeHead(200, { 'Content-Type': 'application/json' });
  res.end(JSON.stringify({ status: 'ok', sessions: sessionStore.keys().length }));
});

proxy.POST('/admin/reset', async (req, res) => {
  // ... custom logic
  res.end('done');
});

Custom WebSocket routes: WS

const wsServer = proxy.WS(route, handler, websocketOptions)
ParameterTypeDescription
routestring | RegExpURL path or pattern to match WebSocket upgrades against.
handler(ws: WebSocket, req: http.IncomingMessage) => voidCalled when a client connects. ws is the server-side WebSocket connection.
websocketOptionsobjectAny options accepted by ws.Server, except noServer which is always set to true.
WS returns the underlying WebSocket.Server instance so you can attach additional event listeners.
const { WebSocket } = require('ws');

proxy.WS('/live-updates', (ws, req) => {
  ws.send(JSON.stringify({ type: 'welcome' }));

  ws.on('message', (data) => {
    ws.send(data); // echo
  });
});

Modify response headers: rewriteServerHeaders

proxy.rewriteServerHeaders is a plain object where each key is a response header name (lowercase) and each value is a transform function. The function receives the original header value and should return the new value, or undefined to delete the header. Rammerhead sets these defaults on every proxy instance:
proxy.rewriteServerHeaders = {
  'permissions-policy': (v) => v && v.replace(/sync-xhr/g, 'sync-yes'),
  'feature-policy':     (v) => v && v.replace(/sync-xhr/g, 'sync-yes'),
  'referrer-policy':    () => 'no-referrer-when-downgrade',
  'report-to':          () => undefined,  // deleted — prevents proxy URL leaking
  'cross-origin-embedder-policy': () => undefined  // deleted — fixes reCAPTCHA v3
};
You can extend or override these by assigning new keys:
// Remove x-frame-options so the proxied site can be embedded
proxy.rewriteServerHeaders['x-frame-options'] = () => undefined;

// Downgrade HSTS max-age
proxy.rewriteServerHeaders['strict-transport-security'] = () => 'max-age=0';

// Add a custom header to every response
proxy.rewriteServerHeaders['x-proxied-by'] = () => 'rammerhead';

Examples

Logging middleware

Add a handler at the beginning of the pipeline to log every non-route request with its IP and URL before it reaches the proxy engine.
proxy.addToOnRequestPipeline(async (req, res, serverInfo, isRoute, isWebsocket) => {
  if (!isRoute) {
    const ip = req.socket.remoteAddress;
    console.log(`[${new Date().toISOString()}] ${ip} ${req.method} ${req.url}`);
  }
  // return nothing (undefined) to continue the pipeline
}, true); // insert at the beginning so this runs before all other handlers

Authentication middleware

Block requests from clients that do not supply a valid token. Because this handler returns true, no further pipeline handlers or proxy logic run for unauthorized requests.
const VALID_TOKEN = process.env.AUTH_TOKEN;

proxy.addToOnRequestPipeline(async (req, res, serverInfo, isRoute) => {
  // Let management routes (newsession, deletesession, etc.) pass through
  if (isRoute) return;

  const token = new URL(req.url, 'http://localhost').searchParams.get('token')
    || req.headers['x-auth-token'];

  if (token !== VALID_TOKEN) {
    res.writeHead(403, { 'Content-Type': 'text/plain' });
    res.end('Forbidden');
    return true; // terminate — do not forward to proxy
  }
});

Rate limiting middleware

Use a simple in-memory counter to limit each IP to a maximum number of requests per minute.
const requestCounts = new Map();

setInterval(() => requestCounts.clear(), 60_000);

proxy.addToOnRequestPipeline(async (req, res, serverInfo, isRoute) => {
  if (isRoute) return;

  const ip = req.socket.remoteAddress;
  const count = (requestCounts.get(ip) || 0) + 1;
  requestCounts.set(ip, count);

  if (count > 500) {
    res.writeHead(429, { 'Content-Type': 'text/plain' });
    res.end('Too Many Requests');
    return true;
  }
});

IP restriction in the upgrade pipeline

Reject WebSocket upgrade requests from a blocklisted set of IP addresses before they reach the hammerhead engine or any WS route.
const BLOCKED_IPS = new Set(['192.0.2.1']);

proxy.addToOnUpgradePipeline(async (req, socket, head, serverInfo, isRoute) => {
  const ip = req.socket.remoteAddress;
  if (BLOCKED_IPS.has(ip)) {
    socket.write('HTTP/1.1 403 Forbidden\r\n\r\n');
    socket.destroy();
    return true;
  }
});

Combining pipeline and custom routes

You can mix pipeline middleware and custom routes freely. The pipeline runs on every request, including requests to your custom routes (isRoute will be true for those).
const setupRoutes  = require('rammerhead/src/server/setupRoutes');
const setupPipeline = require('rammerhead/src/server/setupPipeline');

setupPipeline(proxy, sessionStore);  // built-in IP-restriction + header-stripping middleware
setupRoutes(proxy, sessionStore, logger);  // built-in session management routes

// Your custom middleware runs after the built-in pipeline
proxy.addToOnRequestPipeline(myAuthMiddleware);

// Your custom route
proxy.GET('/ping', (_req, res) => res.end('pong'));
Use beginning: true to insert middleware that must run before the built-in pipeline handlers, such as a global request logger or an IP blocklist that should fire before any session lookups.

Build docs developers (and LLMs) love