- light and robust HTTP router with zero third party dependencies, binary is 12MB or 3MB compressed
- terminates TLS with Let's Encrypt over ACME + auto-renews certificates (default)
- OR terminates TLS with custom cert if auto-renew is not wanted
- OR supports mTLS if set
- forwards requests to upstream services based on the incoming host/path + overrides
- serves "Under Maintenance" page when upstream service is unavailable
- use this systemd simple-http-router.service
# linux amd64 version (check version)
sudo bash -c 'VERSION=2026.04.13; ARCH=x86_64; curl -L -o /usr/local/bin/simple-http-router "https://github.com/codemodify/simple-http-router/releases/download/${VERSION}/simple-http-router_linux-${ARCH}"'
sudo chmod +x /usr/local/bin/simple-http-router
/usr/local/bin/simple-http-router -version
sudo cp sysadmin/simple-http-router.service /etc/systemd/system/
sudo systemctl daemon-reload && sudo systemctl enable simple-http-router && sudo systemctl start simple-http-router- use this Dockerfile
docker build -t sysadmin/simple-http-router .
docker run \
-v /path/to/config.json:/etc/simple-http-router/config.json \
-v /path/to/cert-cache:/var/lib/simple-http-router/cert-cache \
-p 80:80 -p 443:443 \
simple-http-router- fast: just use some of the
config.sample.*.jsonfiles closest to your case - tweak: OR copy config.sample.all.json to
config.jsonand clean it - ports
80and443must be reachable for normal ACME validation and HTTPS traffic to work - for complete options possible either look at the config.sample.all.json or readme-config.md
- how to read
- open
config.sample.all.json - it has everything, diagonal-read it to spot the logic
- it should be easy to pick-up: listeners, routers, defaults and per site overrides
- open
simple-http-router -config config.json- edit + reload config
- by PID:
kill -HUP $(pidof simple-http-router) - by PID:
kill -HUP 12345 systemctl reload simple-http-router- reload updates: routing, upstreams, timeouts, and all per-site config
- listener and TLS changes still require a full restart
- by PID:
- set
sites-config-defaults/use_staging -> true- makes ACME certificate requests go to Let's Encrypt's staging server
https://acme-staging-v02.api.letsencrypt.org/directory - staging issues certificates that browsers don't trust, but it has much higher rate limits
- use it to test that your ACME setup works (config, DNS, ports, permissions) without burning through Let's Encrypt's production rate limits (50 certs per domain per week).
- makes ACME certificate requests go to Let's Encrypt's staging server
- as of 2026-04-11
- perf was not tested against any of the bellow
- features that makes this a bloat are left out for separate components on purpose, see vs-caddy and readme-design
- logging is to STDOUT, no files or multi-output, to be updated
-
sysadmin stuff
- metrics is a concern of another component, coding this in makes it complex
- metrics is sent out
- logging is a concern of another component, coding this in makes it complex
- the logging is sent out, multi-storage, rotation, etc is left for the said component
- load balancing is a concern of another component, coding this in makes it complex
- this includes - Health checks - periodic background probes to upstreams, auto-remove unhealthy backends before traffic hits them
- amdin API is a concern of another component, coding this in makes it complex
- another tool will automate the management of this server
- REST endpoint for pushing config, inspecting state, triggering reloads without SIGHUP
- IP allow/blocklist is a concern of another component, coding this in makes it complex
- metrics is a concern of another component, coding this in makes it complex
-
functions
- Static file serving - is a concern of another component, coding this in makes it complex
- Built-in file server with MIME types, directory listings, index files, range requests
- Retry on upstream failure - is a concern for the load balancing
- Try next backend before returning error page
- Response caching Cache upstream responses with TTL, conditional requests
- Plugin/module system Extend without modifying core - TODO
- Static file serving - is a concern of another component, coding this in makes it complex
-
protocol support
- HTTP/3 (QUIC) - TODO
- UDP-based transport, enabled by default
- WebSocket - passes upgrades through (Caddy handles it explicitly with timeouts, buffering, and upgrade detection)
- gRPC - works for gRPC over h2, but no h2c (cleartext HTTP/2) (Caddy handles gRPC with h2c and trailer support)
- gRPC h2c - Cleartext HTTP/2 for backend gRPC connections
- HTTP/3 (QUIC) - TODO
| tool | strength | Complexity |
|---|---|---|
| Caddy | Simplicity, HTTPS auto | Easy |
| NGINX | Flexibility, ecosystem | Medium |
| HAProxy | Performance, load balancing | Medium |
| Envoy | Microservices, gRPC | Complex |
| Traefik | Dynamic config | Easy |
| Apache | Legacy, modules | Medium |
| simple-http-router | Simplicity, HTTPS auto | Easy |
-
NGINX
- Industry standard for years
- Very fast, battle-tested
- Huge ecosystem
- Config can get complex
- Best for: traditional setups, high traffic, fine control
-
Apache HTTP Server
- Older but extremely flexible
- Tons of modules (mod_proxy, etc.)
- Heavier than modern alternatives
- Best for: legacy systems, compatibility
-
HAProxy
- Focused on load balancing
- Extremely performant (L4 + L7)
- Not as “web server-ish”
- Best for: high-scale load balancing, reliability
-
Envoy
- Built for microservices
- Deep HTTP/2, gRPC support
- Core of service meshes
- Best for: Kubernetes, service mesh, gRPC-heavy systems
-
Traefik
- Auto-config via Docker/Kubernetes
- Dynamic routing (no reloads)
- Very dev-friendly
- Best for: containers, dynamic environments
-
Nginx Unit
- Dynamic config via API
- App server + proxy hybrid
-
H2O
- Designed for HTTP/2 performance
- Lightweight, less common
-
OpenResty
- NGINX + Lua scripting
- Highly programmable proxy
tldr
- Want simple + modern → Caddy or Traefik
- Want control + maturity → NGINX
- Want extreme scale LB → HAProxy
- Want service mesh / gRPC infra → Envoy
