Tiny Proxy
Lightweight, embeddable HTTP reverse proxy written in Rust with Caddy-like configuration syntax.
Features
- Embeddable Library: Use as a library in your Rust applications or run as standalone CLI
- Caddy-like Configuration: Simple, human-readable configuration format
- Path-based Routing: Pattern matching with wildcard support
- Header Manipulation: Add, modify, or remove headers
- URI Rewriting: Replace parts of request URIs
- HTTP/HTTPS Backend Support: Full support for both HTTP and HTTPS backends
- TLS Termination: HTTPS on the frontend with SNI-based multi-domain support
- Method-based Routing: Different behavior for different HTTP methods
- Direct Responses: Respond with custom status codes and bodies
- Authentication Module: Token validation and header substitution
- Management API: REST API for runtime configuration management (optional feature)
Installation
As CLI
# Install via cargo
# Or build and run directly
As Library
Add to your Cargo.toml:
[]
= "0.4"
Docker
Quick Start
# Pull from GitHub Container Registry
# Run with a local config
# With TLS
Docker Compose
services:
proxy:
image: ghcr.io/denislituev/tiny-proxy:latest
ports:
- "8080:8080"
volumes:
- ./config.caddy:/etc/tiny-proxy/config.caddy:ro
See docker-compose.yml for a full example with TLS + echo backends.
Build from Source
The image is based on Alpine Linux with CA certificates (~7 MB), so HTTPS backends work out of the box.
Usage
CLI Mode
Run as standalone server:
# Auto-detect listeners from config (recommended)
# Or specify a single listen address
CLI Arguments
--config, -c: Path to configuration file (default:./file.caddy)--addr, -a: Optional. Bind a single listener on this address (plainstart()). When omitted, auto-detect mode (start_all()): one listener per site address in config. TLS sites → HTTPS with SNI; non-TLS → HTTP. In auto-detect mode only, each TLS port also gets an HTTP→HTTPS redirect listener (redirect_port = tls_port - 443 + 80, e.g. 443→80, 8443→8080). With--addr, only the specified listener runs — no automatic redirect server.
Library Mode
Basic Example
use ;
async
Background Execution
Run proxy in background while doing other work:
use ;
use Arc;
async
Hot-Reload Configuration
Update configuration at runtime without restart. The proxy uses Arc<RwLock<Config>> internally,
so routing and directive changes take effect immediately for new connections.
TLS certificates: cert/key files and
TlsAcceptorare loaded when a listener starts. Hot-reload updates site routing and directives, but not TLS certificates — to pick up new certs or keys, restart the proxy (or the TLS listener).
Example:
use ;
use Arc;
use RwLock;
async
Or use the built-in update_config method:
let new_config = from_file?;
proxy.update_config.await;
Configuration
tiny-proxy uses a Caddy-like configuration format.
Basic Syntax
site_address {
directive1 arg1 arg2
directive2 {
nested_directive
}
}
Supported Directives
reverse_proxy
Forward requests to a backend server. Supports optional block syntax for timeout configuration.
# Simple
localhost:8080 {
reverse_proxy http://backend:3000
}
# With timeouts (for LLM/SSE backends)
localhost:8080 {
reverse_proxy http://llm-backend:8000 {
connect_timeout 10s
read_timeout 600s
}
}
Timeout values support duration suffixes: 30s, 5m, 2h, 1d, or plain numbers (seconds).
tls
Enable HTTPS on the frontend with TLS termination. Specify paths to the certificate chain and private key (PEM format).
# Single domain with TLS
example.com:443 {
tls /etc/ssl/cert.pem /etc/ssl/key.pem
reverse_proxy backend:8080
}
# Multiple domains on port 443 (SNI-based routing)
example.com:443 {
tls /etc/ssl/example.com/cert.pem /etc/ssl/example.com/key.pem
reverse_proxy backend:8080
}
api.example.com:443 {
tls /etc/ssl/api.example.com/cert.pem /etc/ssl/api.example.com/key.pem
reverse_proxy api-backend:3000
}
Auto-detect mode (no --addr, uses start_all()):
- One HTTPS listener per TLS site address, with SNI-based certificate selection
- HTTP→HTTPS redirect per TLS port:
redirect_port = tls_port - 443 + 80(443→80, 8443→8080) - Correct
X-Forwarded-Proto: httpssent to backends
Single-address mode (--addr): only the given listener is started — no automatic redirect server.
Use this when you bind one port manually; use auto-detect for full multi-site + redirect setup.
If the redirect port is already in use, HTTPS continues to work; redirect is skipped with a warning.
Host header: on default ports (443/80), browsers omit the port (
Host: example.com). On non-default TLS ports (e.g. 8443), browsers include it (Host: example.com:8443) — config keys must match. Seefind_sitedocs inhandler.rsfor details.
Known limitation: TLS certs are loaded at listener startup; hot-reload does not reload them (see Hot-Reload above).
handle_path
Match paths with pattern (supports wildcard *).
localhost:8080 {
handle_path /api/* {
reverse_proxy api-service:8000
}
}
uri_replace
Replace part of the request URI.
localhost:8080 {
uri_replace /old-path /new-path
reverse_proxy backend:3000
}
header
Add, modify, or remove request headers.
localhost:8080 {
# Add header with placeholder
header X-Request-ID {uuid}
# Add static header
header X-Custom-Header custom-value
# Remove header (prefix with -)
header -Accept-Encoding
reverse_proxy backend:3000
}
method
Apply directives based on HTTP method.
localhost:8080 {
method GET HEAD {
respond 200 "OK"
}
reverse_proxy backend:3000
}
strip_prefix
Remove a prefix from the request URI path.
localhost:8080 {
strip_prefix /api
reverse_proxy http://backend:3000
}
Request /api/users/123 → backend receives /users/123.
redirect
Return a redirect response with Location header.
localhost:8080 {
# Permanent redirect (default 301)
redirect https://new-domain.com
# Temporary redirect
redirect 302 /maintenance
}
Supported status codes: 301 (permanent), 302 (temporary), 307, 308.
respond
Return a direct response with custom status and body.
localhost:8080 {
respond 200 "Service is healthy"
}
Configuration Examples
Simple Reverse Proxy
localhost:8080 {
reverse_proxy http://backend:3000
}
Multi-site Configuration
api.example.com {
reverse_proxy http://api-service:8000
}
static.example.com {
reverse_proxy http://static-service:8001
}
API with Versioning
localhost:8080 {
handle_path /api/v1/* {
handle_path /users/* {
reverse_proxy http://user-service:8001
}
reverse_proxy http://api-service:8000
}
reverse_proxy http://default-backend:3000
}
Headers and URI Rewriting
localhost:8080 {
header X-Forwarded-For {header.X-Forwarded-For}
header X-Request-ID {uuid}
uri_replace /api /backend
reverse_proxy http://backend:3000
}
Health Check Endpoint
localhost:8080 {
method GET HEAD {
respond 200 "OK"
}
reverse_proxy http://backend:3000
}
Placeholders
Use placeholders in header values:
{header.Name}- Value of request header with that name{env.VAR}- Value of environment variable{uuid}- Random UUID
Features
Default Features
cli- Command-line interface supporttls- HTTPS backend supportapi- Management API for runtime configuration
Optional Features
# Minimal - core proxy only (for embedding in other applications)
[]
= { = "0.4", = false }
# With HTTPS backend support
[]
= { = "0.4", = false, = ["tls"] }
# With management API
[]
= { = "0.4", = false, = ["tls", "api"] }
# Full standalone (same as default)
[]
= "0.4"
cli (default)
Enable CLI dependencies and tiny-proxy binary.
tls (default)
Enable TLS support — both frontend TLS termination (HTTPS listeners) and backend HTTPS connections:
- Frontend:
rustls+tokio-rustlsfor HTTPS listeners with SNI-based routing - Backend:
hyper-rustlsfor proxying to HTTPS backends rustls-pemfilefor loading PEM certificate chains and private keys
api (default)
Management API for runtime configuration:
use api;
use Arc;
use RwLock;
let config = new;
start_api_server.await?;
API Documentation
See the module documentation for detailed API reference.
Main Types
Config- Configuration containerProxy- Proxy instanceDirective- Configuration directivesSiteConfig- Per-site configuration
Main Functions
Config::from_file(path)- Load configuration from fileConfig::from_str(content)- Parse configuration from stringProxy::new(config)- Create proxy instanceProxy::from_shared(config)- Create proxy from sharedArc<RwLock<Config>>Proxy::start(addr)- Start proxy serverProxy::shared_config()- GetArc<RwLock<Config>>for external config updatesProxy::config_snapshot()- Read current configuration as owned valueProxy::update_config(config)- Update configuration at runtime (async)
Testing
Run all tests:
Run specific tests:
# Specific test
Run tests with logging:
RUST_LOG=debug
Benchmarking
Run benchmarks:
Run specific benchmark:
Development
Project Structure
tiny-proxy/
├── src/
│ ├── main.rs # CLI entry point
│ ├── lib.rs # Library entry point
│ ├── cli/ # CLI module
│ ├── config/ # Configuration parsing
│ ├── proxy/ # Proxy logic
│ ├── auth/ # Authentication (optional)
│ └── api/ # Management API (optional)
├── examples/ # Usage examples
├── benches/ # Benchmarks
Build with Features
# Default (CLI + TLS + API)
# Library only (no CLI dependencies)
# Library with HTTPS support
# Library with API for config management
# CLI without API
Run Examples
# Basic example
# Background execution
Roadmap
Current Status
- ✅ Library mode
- ✅ CLI mode
- ✅ Configuration parsing
- ✅ Reverse proxy
- ✅ Path-based routing
- ✅ Header manipulation
- ✅ URI rewriting
- ✅ Method-based routing
- ✅ Direct responses
- ✅ Authentication module (basic)
- ✅ Management API with hot-reload
Planned Features
- ⏳ Static file serving
- ⏳ Try files (SPA support)
- ⏳ Buffering control
- ✅ TLS/SSL termination (SNI, multi-domain, HTTP→HTTPS redirect)
- ⏳ WebSocket support
- ⏳ Rate limiting
- ✅ Structured access log with X-Request-ID (method, path, host, status, duration, bytes_sent)
- ⏳ Metrics and monitoring
Contributing
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests
- Run
cargo testandcargo clippy - Submit a pull request
License
See LICENSE file.