This repository is a result of a vibe-coding experiment, meaning that all the code (even the README contents below) were AI-generated. Please don't use this in production, we recommend using Ferron web server instead.
Also, disregard the MIT license, since this code would be practically public domain, because fully AI-generated works wouldn't be elligble for copyright... The real license would be Unlicense (or some other public domain equivalent) instead...
Anyway, let's proceed to the README proper...
A high-performance web server written in Rust with automatic HTTPS, static file serving, and a reverse proxy with load balancing.
- Automatic HTTPS — Integrates with Let's Encrypt for zero-touch certificate acquisition and renewal. Falls back to a self-signed certificate for development.
- Static file serving — Serve directories with configurable index files. Path-traversal protected.
- Reverse proxy — Route requests to backend services with load balancing (round-robin, random) and health checks.
- Simple configuration — Single TOML file with sensible defaults.
- Modular design — Each concern (TLS, routing, proxy, config) is an independent module.
# warpgate.toml
host = "0.0.0.0"
port = 443
http_port = 80
[tls]
domain = "example.com"
email = "admin@example.com"
staging = false
cache_dir = "./letsencrypt"
[[static]]
path = "/"
root = "/var/www/public"
index = ["index.html"]
[[proxy]]
path = "/api"
upstreams = ["http://127.0.0.1:3000", "http://127.0.0.1:3001"]
balancer = "round-robin"
health_path = "/health"$ warpgate -c warpgate.tomlcargo build --release
./target/release/warpgate -c warpgate.tomlRequires Rust 1.75 or later.
| Key | Default | Description |
|---|---|---|
host |
"0.0.0.0" |
Address to bind the server to |
port |
443 |
HTTPS port |
http_port |
80 |
HTTP port (for ACME challenges and redirects) |
Three modes are supported. The server picks the mode automatically:
| Mode | Config required | Behaviour |
|---|---|---|
| Let's Encrypt (ACME) | domain + email |
Obtains and renews certificates automatically via ACME HTTP-01 challenges |
| Custom certificates | cert_path + key_path |
Uses the provided PEM files |
| Self-signed | No [tls] section |
Generates a self-signed certificate (development only) |
| Key | Default | Description |
|---|---|---|
domain |
— | Domain name for the certificate (required for ACME) |
email |
— | Email for the Let's Encrypt account (required for ACME) |
staging |
false |
Use Let's Encrypt staging environment (avoids rate limits during testing) |
cache_dir |
"./letsencrypt" |
Directory to store ACME account keys and certificates |
cert_path |
— | Path to a PEM certificate file (for custom certs) |
key_path |
— | Path to a PEM private key file (for custom certs) |
Each table defines a route that serves files from a directory. You can define multiple static routes.
| Key | Default | Description |
|---|---|---|
path |
— | URL path prefix (e.g. "/", "/assets") |
root |
— | Filesystem directory to serve files from |
index |
["index.html"] |
Index files to try when a directory is requested |
The server prevents path traversal: requested paths are canonicalised and checked against the configured root.
Example — multiple static routes:
[[static]]
path = "/"
root = "/var/www/public"
index = ["index.html", "index.htm"]
[[static]]
path = "/assets"
root = "/var/www/assets"Each table defines a route that proxies requests to one or more upstream servers. Supports load balancing and optional health checks.
| Key | Default | Description |
|---|---|---|
path |
— | URL path prefix to match (e.g. "/api") |
upstreams |
— | List of upstream URLs (e.g. ["http://127.0.0.1:3000"]) |
balancer |
"round-robin" |
Load balancing strategy: "round-robin" or "random" |
health_path |
— | Path to check for health (e.g. "/health"). If set, health checks run in the background |
health_interval |
30 |
Seconds between health checks |
health_timeout |
5 |
Seconds per health check request |
Example — proxy with load balancing and health checks:
[[proxy]]
path = "/api"
upstreams = [
"http://10.0.0.1:3000",
"http://10.0.0.2:3000",
"http://10.0.0.3:3000",
]
balancer = "round-robin"
health_path = "/health"
health_interval = 10
health_timeout = 3Client ───► Port 443 (HTTPS) ───► TLS handshake ───► HTTP router
│ │
│ ┌─────┴──────┐
│ │ │
│ Static Proxy
│ handler handler
│ │ │
│ Filesystem Upstreams
│
Client ───► Port 80 (HTTP) ───► ACME challenges + redirect to HTTPS
- HTTP server (port 80) handles ACME HTTP-01 challenges and redirects everything else to HTTPS.
- HTTPS server (port 443) terminates TLS via rustls, then routes requests.
- The router checks ACME challenges first, then static routes, then proxy routes.
- The proxy handler selects a healthy upstream via the configured load balancer and forwards the request.
- On first start with ACME configured,
warpgateorders a certificate from Let's Encrypt, serves the HTTP-01 challenge on port 80, and saves the certificate tocache_dir. - On subsequent starts, it reads the cached certificate.
- A background task checks expiry daily and renews when fewer than 30 days remain.
Each module is a single file with a clear interface:
| Module | File | Key types |
|---|---|---|
| Config | config.rs |
Config, TlsConfig, StaticConfig, ProxyConfig |
| TLS | tls.rs |
ChallengeStore, CertificateSource (enum) |
| Handler | handler.rs |
Router, StaticRoute |
| Proxy | proxy.rs |
Balancer, ProxyTarget, Backend |
| Server | server.rs |
run() — the main async entry point |
To add a new handler (e.g. WebSocket, CGI, middleware):
- Add the routing check in
handle_request()inhandler.rs. - Implement your handler function.
- Add config fields if needed.
MIT