Prox is a lightweight reverse proxy built in Rust, implementing a hexagonal architecture (also known as ports and adapters architecture) for maintainability, testability, and flexibility.
- Modern Protocol Support with complete HTTP/1.1, HTTP/2, and HTTP/3 (QUIC) implementation
- β HTTP/3 (QUIC) with automatic Alt-Svc advertisement and protocol negotiation
- β WebSocket Support with first-class proxying and configurable frame/message sizes
- β TLS Integration with seamless certificate sharing between HTTP/2 and HTTP/3
- β Unified Server Architecture supporting both TCP (HTTP/1.1, HTTP/2) and UDP (HTTP/3)
- Advanced Proxy Features
- Static file serving with configurable directories
- HTTP redirects with custom status codes
- Load balancing (round-robin and random strategies)
- Path rewriting for proxy and load-balanced routes
- Health checking for backend services with configurable intervals
- Enterprise-Grade Features
- Rate limiting (by IP, header, or route-wide) with multiple algorithms
- Configuration Validation with detailed error reporting and CLI validation command
- Production-grade monitoring with Prometheus metrics and Grafana dashboards
- Automatic TLS Certificate Management with ACME/Let's Encrypt integration
- Graceful Shutdown with connection tracking and zero-downtime restarts
- Developer Experience
- Configurable via YAML with hot-reload support
- Custom error handling with type safety
- Browser-like request headers for improved compatibility
- Request and Response Manipulation (Headers & Body) with conditional logic
π See also: Value-Adding Ideas and Implementation Status
π HTTP/3 Setup: HTTP/3 Implementation Guide
Prox follows a hexagonal architecture pattern, which separates the application into three main areas:
- Core Domain - Contains the business logic of the application
- Ports - Interfaces that define how the core interacts with the outside world
- Adapters - Implementations of the ports that connect to external systems
src/
βββ lib.rs # Library crate definition and re-exports
βββ main.rs # Application entry point
βββ config/ # Configuration handling
β βββ loader.rs # Configuration loading logic
β βββ models.rs # Configuration data structures with builder pattern
β βββ validation.rs # Configuration validation with detailed error reporting
β βββ mod.rs
βββ core/ # Domain logic
β βββ proxy.rs # Core proxy service logic
β βββ backend.rs # Backend health tracking
β βββ load_balancer.rs # Load balancing strategies
β βββ rate_limiter.rs # Rate limiting logic
β βββ mod.rs
βββ ports/ # Interfaces
β βββ http_server.rs # HTTP server interface
β βββ http_client.rs # HTTP client interface with type aliases
β βββ file_system.rs # File system interface
β βββ mod.rs
βββ adapters/ # Implementations of the ports
β βββ http/ # HTTP server implementation
β β βββ server.rs # Hyper server implementation
β β βββ mod.rs
β βββ acme.rs # ACME/Let's Encrypt certificate management
β βββ http_handler.rs # HTTP request handler
β βββ http_client.rs # HTTP client implementation
β βββ file_system.rs # Static file handling
β βββ health_checker.rs # Health checking implementation
β βββ mod.rs
βββ utils/ # Utility functions
β βββ connection_tracker.rs # Connection tracking utilities
β βββ graceful_shutdown.rs # Graceful shutdown handling
β βββ health_checker_utils.rs # Utilities for health checking
β βββ mod.rs
- Testability: Core domain logic can be tested independently of HTTP, file systems, etc.
- Flexibility: Components can be replaced without affecting the rest of the system
- Separation of Concerns: Clean boundaries between different parts of the application
- Domain Focus: Business logic is isolated from technical details
Before running the proxy server, you can validate your configuration file to catch any errors:
# Validate your configuration file
./prox validate --config config.yaml
# Or validate the default config.yaml file
./prox validateExample validation output (success):
$ ./prox validate
π Validating configuration file: config.yaml
β
YAML parsing: OK
β
Configuration validation: OK
π Configuration Summary:
β’ Listen Address: 127.0.0.1:3000
β’ Routes: 5
β’ TLS Enabled: true
β’ Health Checks: true
π Configuration is valid and ready to use!Example validation output (with errors):
$ ./prox validate
π Validating configuration file: config.yaml
β
YAML parsing: OK
β Configuration validation failed:
Found 2 validation error(s):
1. Invalid URL in field 'route '/api' proxy target': not_a_url - Invalid URL format: relative URL without a base
2. Invalid listen address: invalid_address - Must be in format 'IP:PORT' (e.g., '127.0.0.1:3000' or '0.0.0.0:8080')
π‘ Common fixes:
β’ Ensure all URLs start with http:// or https://
β’ Check that file paths exist
β’ Verify listen address format (e.g., '127.0.0.1:3000')
β’ Ensure rate limit periods use valid units (s, m, h)The validation feature checks:
- β Listen address format
- β Route configuration (proxy, load balance, static, redirect)
- β URL validity for all targets
- β Rate limiting configuration
- β TLS certificate and ACME settings
- β File existence for static routes and certificates
- β Route conflict detection
-
Generate self-signed certificates for HTTPS (or use your own):
mkdir -p certs openssl req -x509 -newkey rsa:4096 -keyout certs/key.pem -out certs/cert.pem -days 365 -nodes -subj '/CN=localhost' -
Configure your reverse proxy in
config.yamlwith manual certificates -
Validate your configuration:
./prox validate --config config.yaml -
Run the proxy:
cargo runor./prox serve --config config.yaml
-
Configure your reverse proxy in
config.yamlwith ACME settings -
Validate your configuration:
./prox validate --config config.yaml -
Ensure your domain points to your server and port 443 is accessible
-
Run the proxy:
cargo runor./prox serve --config config.yaml
Note: For ACME to work, your server must be publicly accessible on port 443, and the domains you specify must point to your server for HTTP-01 challenge validation.
All configuration examples below can be validated using ./prox validate before starting the server.
listen_addr: "0.0.0.0:443"
# Manual TLS configuration
tls:
cert_path: "./certs/cert.pem"
key_path: "./certs/key.pem"
# Health check configuration
health_check:
enabled: true
interval_secs: 10
timeout_secs: 5
path: "/health"
unhealthy_threshold: 3
healthy_threshold: 2listen_addr: "0.0.0.0:443"
# ACME configuration for automatic certificate management
tls:
acme:
enabled: true
domains:
- "example.com"
- "www.example.com"
email: "admin@example.com"
staging: false # Set to true for testing
storage_path: "./acme_storage"
renewal_days_before_expiry: 30
# Health check configuration
health_check:
enabled: true
interval_secs: 10
timeout_secs: 5
path: "/health"
unhealthy_threshold: 3
healthy_threshold: 2
# Backend-specific health check paths
backend_health_paths:
"https://httpbin.org": "/get"
"https://postman-echo.com": "/get"
routes:
"/": # Root route that redirects to /static
type: "redirect"
target: "/static"
status_code: 302
"/static":
type: "static"
root: "./static"
"/redirect":
type: "redirect"
target: "https://www.example.com"
status_code: 302
"/proxy":
type: "proxy"
target: "https://httpbin.org"
path_rewrite: "/anything" # Example: /proxy/foo rewrites to /anything/foo
rate_limit: # Example: Limit by IP, 10 requests per minute
by: "ip"
requests: 10
period: "1m"
# status_code: 429 # Optional: defaults to 429
# message: "Too many requests from your IP. Please try again later." # Optional: defaults to "Too Many Requests"
# algorithm: "token_bucket" # Optional: defaults to token_bucket (current default)
request_headers:
add:
"X-My-Custom-Header": "MyValue"
"X-Forwarded-By": "Prox"
"X-Real-IP": "{client_ip}"
remove: ["User-Agent", "Referer"]
response_headers:
add:
"Server": "Prox"
remove: ["X-Powered-By"]
request_body: # Example: Modify the request body
condition: # Only apply if the request path contains "/special"
path_matches: "/special"
set_text: "This is a modified request body."
response_body: # Example: Modify the response body if it's a 404
condition:
# Assuming your service might return a specific header for identifiable errors
# or you might match on status code if that becomes a condition option.
# For now, let's imagine a header condition.
has_header:
name: "X-Error-Type"
value_matches: "NotFound"
set_json:
error: "Resource not found"
message: "The requested resource was not found on the server."
status: 404
"/api/v1": # Example for API versioning
type: "proxy"
target: "http://internal-service"
path_rewrite: "/" # Example: /api/v1/users rewrites to /users
rate_limit: # Example: Limit by a specific header X-API-Key, 5 requests per 30 seconds
by: "header"
header_name: "X-API-Key"
requests: 5
period: "30s"
status_code: 403 # Custom status code
message: "Rate limit exceeded for your API key." # Custom message
"/balance":
type: "load_balance"
targets:
- "https://httpbin.org"
- "https://postman-echo.com"
strategy: "round_robin"
path_rewrite: "/anything" # Example: /balance/bar rewrites to /anything/bar
rate_limit: # Example: Route-wide limit, 1000 requests per hour
by: "route"
requests: 1000
period: "1h"When using automatic TLS certificate management with ACME (Let's Encrypt), you can configure the following options:
enabled: Set totrueto enable ACME certificate managementdomains: List of domains to include in the certificate (first domain is the primary)email: Contact email for Let's Encrypt account registrationstaging: Set totrueto use Let's Encrypt staging environment for testing (optional, defaults tofalse)ca_url: Custom ACME CA URL (optional, defaults to Let's Encrypt production)storage_path: Directory to store certificates and account data (optional, defaults to./acme_storage)renewal_days_before_expiry: Days before expiry to renew certificates (optional, defaults to 30)
- Public accessibility: Your server must be publicly accessible on port 443
- Domain DNS: All domains in your configuration must point to your server's IP address
- HTTP-01 challenge: Prox automatically handles HTTP-01 challenges by serving files from
./static/.well-known/acme-challenge/
- Certificates are automatically checked daily for renewal
- Renewal occurs when the certificate expires within the configured threshold (default: 30 days)
- The renewal process runs in the background without interrupting service
You can test the proxy using curl:
# Test static content
curl -k https://127.0.0.1:3000/static
# Test redirection
curl -k -L https://127.0.0.1:3000/
# Test proxy
curl -k https://127.0.0.1:3000/proxy/get
# Test proxy with path rewriting
curl -k https://127.0.0.1:3000/proxy/test/path # Assuming /proxy has path_rewrite: "/anything"
# Expected: httpbin.org receives a request for /anything/test/path
# Test request header addition (X-My-Custom-Header: MyValue)
curl -k -H "X-My-Custom-Header: OriginalValue" https://127.0.0.1:3000/proxy/headers -v
# Expected: Prox adds/overwrites X-My-Custom-Header, removes User-Agent, adds X-Real-IP
# Test request body modification (POST to /proxy/special/anything)
# This will trigger the request_body.set_text action due to path_matches: "/special"
curl -k -X POST -d '{"original": "data"}' https://127.0.0.1:3000/proxy/special/post -H "Content-Type: application/json"
# Expected: httpbin.org receives "This is a modified request body."
# Test response header removal (X-Powered-By) and addition (Server: Prox)
curl -k -I https://127.0.0.1:3000/proxy/get
# Expected: X-Powered-By header (if present from httpbin) is removed, Server: Prox is added.
# Test load balancing (run multiple times to see round-robin in action)
curl -k https://127.0.0.1:3000/balance/getNote: The -k flag is used to skip certificate validation for self-signed certificates.
Prox supports several CLI commands for different operations:
# Start the proxy server (default behavior)
./prox serve --config config.yaml
./prox serve # Uses default config.yaml
# Legacy format (still supported)
./prox --config config.yaml# Validate a specific configuration file
./prox validate --config config.yaml
# Validate the default config.yaml file
./prox validate
# Exit codes:
# 0 = Configuration is valid
# 1 = Configuration has errors or file not foundBenefits of configuration validation:
- π Early Error Detection: Catch configuration issues before deployment
- π CI/CD Integration: Validate configs in automated pipelines
- π Self-Documenting: Clear error messages explain requirements
- π‘οΈ Production Safety: Prevent server startup with invalid configuration
Prox includes built-in Prometheus metrics and can be easily monitored with a complete Grafana dashboard setup.
Prox exposes metrics at https://localhost:3000/metrics including:
- Request rates and response times
- Active connections and backend health
- Error rates and status code distributions
- Rate limiting statistics
Set up production-grade monitoring with Prometheus and Grafana:
π Complete Monitoring Stack Setup Guide
This guide includes:
- Docker Compose setup for Prometheus and Grafana
- Pre-configured dashboards and panels
- Alerting rules and best practices
- Troubleshooting and performance tuning
- Production deployment considerations
Quick Start:
# Start monitoring stack
docker-compose up -d
# Start Prox with metrics
cargo run
# Access Grafana dashboards
open http://localhost:3001 # admin/adminKey metrics exposed by Prox:
prox_requests_total- Total number of requests by endpoint, method, and statusprox_request_duration_seconds- Request duration histogramprox_active_connections- Current active connectionsprox_backend_health_status- Backend server health statusprox_rate_limit_hits_total- Rate limiting statistics