Build a Dynamic Reverse Proxy with Node.js
This article explains how to build and configure a dynamic reverse proxy using Node.js to intercept, rewrite, and route incoming HTTP requests. You will learn how to set up a lightweight proxy server, define dynamic routing rules based on request paths or headers, and rewrite request URLs on the fly before forwarding them to backend services.
Understanding the Concept
A reverse proxy acts as an intermediary between client devices and backend servers. While a standard proxy routes requests based on static configurations, a dynamic reverse proxy inspects incoming requests and programmatically alters their destination or path in real-time. This is highly useful for microservices architectures, API versioning, load balancing, and A/B testing.
Prerequisites and Setup
To implement this solution, you need Node.js installed on your
machine. Start by initializing a new Node.js project and installing the
http-proxy package, which simplifies request
forwarding.
mkdir node-dynamic-proxy
cd node-dynamic-proxy
npm init -y
npm install http-proxyStep-by-Step Implementation
The core of a dynamic reverse proxy consists of an HTTP server that
evaluates the req.url or headers, modifies them, and
forwards the payload to a target destination using the
http-proxy library.
Create a file named proxy.js and add the following
code:
const http = require('http');
const httpProxy = require('http-proxy');
// Create the proxy server instance
const proxy = httpProxy.createProxyServer({});
// Handle proxy errors to prevent the process from crashing
proxy.on('error', (err, req, res) => {
console.error('Proxy Error:', err);
res.writeHead(502, { 'Content-Type': 'text/plain' });
res.end('Bad Gateway: The target server is unreachable.');
});
// Create the main HTTP server
const server = http.createServer((req, res) => {
let target = 'http://localhost:3000'; // Default fallback target
// Dynamic Routing and Path Rewriting Logic
if (req.url.startsWith('/api/v1')) {
// Rewrite path: /api/v1/users -> /legacy/users
req.url = req.url.replace('/api/v1', '/legacy');
target = 'http://localhost:4001';
} else if (req.url.startsWith('/api/v2')) {
// Rewrite path: /api/v2/users -> /v2/users
req.url = req.url.replace('/api/v2', '/v2');
target = 'http://localhost:4002';
} else if (req.headers['x-use-beta'] === 'true') {
// Route based on request headers
target = 'http://localhost:5000';
}
console.log(`Proxying ${req.method} ${req.url} to -> ${target}`);
// Forward the request to the dynamically selected target
proxy.web(req, res, { target: target, changeOrigin: true });
});
// Start the proxy server
const PORT = 8080;
server.listen(PORT, () => {
console.log(`Dynamic Reverse Proxy running on port ${PORT}`);
});How the Code Works
- Proxy Instantiation:
httpProxy.createProxyServer({})initializes the proxy agent that handles TCP socket streaming and response pipe management. - Path and Header Inspection: Inside the
http.createServercallback, the server reads the request properties (req.urlandreq.headers). - Dynamic Path Rewriting: JavaScript’s
String.prototype.replace()is used to dynamically modify the path structure before sending it to the backend. This ensures the target API receives the formatted path it expects. - Target Redirection: The
targetvariable is re-assigned conditionally depending on the rules matched. - Request Forwarding:
proxy.web(req, res, { target })sends the modified request stream to the chosen destination and pipes the backend’s response back to the client. - Error Handling: The
proxy.on('error')event listener prevents the Node.js application from crashing if a backend server is offline, returning a502 Bad Gatewaystatus instead.