reverse-http-proxy
A minimal reverse proxy for path-based HTTP routing.
A lightweight, high-performance reverse proxy written in Rust. Routes HTTP requests to different backend servers based on URL paths using efficient bidirectional binary streaming with minimal overhead.
Note: URL path rewriting is available via the --rewrite flag (disabled by default). When disabled, the complete original path is forwarded unchanged to the backend server.
Features
- Path-based routing - Route requests to different backends based on URL paths
- Prefix matching - Supports both exact and prefix-based path matching (longest match wins)
- Optional path rewriting - Strip matched route prefixes from forwarded requests
- Binary streaming - Forwards raw TCP bytes without parsing HTTP bodies
- High performance - Minimal overhead using tokio async I/O
- Protocol agnostic - Supports HTTP/1.1, HTTP/2, WebSockets, and Server-Sent Events
- Default fallback - Unmatched paths route to a default backend
Quick Start
# Basic usage with default backend only
# With path-based routing
In this example:
- Proxy listens on
0.0.0.0:8080 - Requests to
/api/*go to127.0.0.1:4000 - Requests to
/webhook/*go to127.0.0.1:5000 - All other requests go to the default backend
127.0.0.1:3000
Installation
You'll need Rust installed. Get it from rustup.rs.
The binary will be at target/release/reverse-http-proxy.
Usage
Basic Syntax
Arguments
LISTEN_ADDRESS- Address to listen on (format:ip:port)DEFAULT_BACKEND- Default backend address for unmatched paths (format:ip:port)
Options
-r, --route <PATH=BACKEND>- Add a path-based route (can be specified multiple times)- Format:
/path=ip:port - Path must start with
/
- Format:
--rewrite- Enable path rewriting (strips matched route prefix from forwarded requests)
Examples
API Gateway pattern
Microservices routing
Webhook fanout
Routing Behavior
The proxy uses longest prefix matching for routing:
- Exact match - If the path exactly matches a route, use that backend
- Prefix match - If the path starts with a route prefix, use that backend
- Default fallback - If no match, use the default backend
Routing Examples
Given these routes:
Request routing:
GET /→ default backendGET /api→backend1:4000(exact match)GET /api/users→backend1:4000(prefix match)GET /api/v2/users→backend2:5000(longest prefix match wins)GET /webhook/stripe→backend3:6000(prefix match)GET /other→ default backend (no match)
URL Path Rewriting
By default, the complete original path is forwarded to the backend server unchanged. You can enable path rewriting with the --rewrite flag to strip the matched route prefix.
Without path rewriting (default)
With route -r /api=127.0.0.1:4000:
- Client requests:
GET /api/users - Backend receives:
GET /api/users(unchanged)
With path rewriting (--rewrite)
With route -r /api=127.0.0.1:4000 --rewrite:
- Client requests:
GET /api/users - Backend receives:
GET /users(prefix stripped)
Examples:
# Without rewriting (default)
# Request to /api/test -> backend receives /api/test
# With rewriting
# Request to /api/test -> backend receives /test
Path rewriting behavior:
- Strips the matched route prefix from the request path
- Ensures the rewritten path always starts with
/ - Works with both exact and prefix matches
- Only rewrites if a route matches (default backend requests are never rewritten)
Architecture
The proxy operates in these key steps:
- Accept incoming HTTP connections on the specified address
- Parse HTTP request headers to extract the URL path
- Match the path against configured routes (longest prefix match)
- Forward the complete request to the appropriate backend server
- Stream responses bidirectionally between client and backend using raw TCP bytes
By forwarding raw TCP bytes after initial routing, it achieves high performance while supporting any HTTP protocol version transparently.
Error Handling
- 502 Bad Gateway - Returned when the backend server is unreachable
- Connection errors - Logged to stderr
- Parse errors - Logged when HTTP request headers cannot be parsed