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)
| Parameter | Type | Description |
|---|
handler | async (req, res, serverInfo, isRoute, isWebsocket) => boolean | undefined | The middleware function. Return true to terminate processing. |
beginning | boolean (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)
isRoute — true 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)
isWebsocket — true 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)
| Parameter | Type | Description |
|---|
handler | async (req, socket, head, serverInfo, isRoute) => boolean | undefined | The middleware function. Return true to terminate processing. |
beginning | boolean (default false) | Inserts at the front when true. |
Handler parameters:
req — Node.js http.IncomingMessage
socket — stream.Duplex — the raw TCP socket
head — Buffer — any data already read from the socket after the HTTP headers
serverInfo — Hammerhead ServerInfo object
isRoute — true 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)
| Parameter | Type | Description |
|---|
route | string | RegExp | URL path or pattern to match WebSocket upgrades against. |
handler | (ws: WebSocket, req: http.IncomingMessage) => void | Called when a client connects. ws is the server-side WebSocket connection. |
websocketOptions | object | Any 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
});
});
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.