TLS 1.2+ · SNI ROUTING · ~560KB BINARY

An HTTPS reverse proxy.
One small binary.

TLS termination with SNI multi-domain routing, active health checks, an LRU response cache, per-IP rate limiting, WebSocket tunnelling and a Prometheus /metrics endpoint — from one TOML file. This is the proxy in front of every domain here.

Get started GitHub
~560KB
single binary
13
domains on one IP, plus a wildcard
1
TOML config file
128
upstreams per file
Simple config

One file to rule them all

Define upstreams, TLS certs, and limits in a single TOML file.

# proxy.toml
[server]
listen      = 443
workers     = 64
tls_cert    = "/etc/letsencrypt/live/example.com/fullchain.pem"
tls_key     = "/etc/letsencrypt/live/example.com/privkey.pem"
rate_limit  = 100
access_log  = "/var/log/nyx-proxy/access.log"

[upstream.0]
name        = "web"
host        = "127.0.0.1"
port        = 3000
hostname    = "example.com"

[upstream.1]
name        = "api"
host        = "127.0.0.1"
port        = 3001
hostname    = "api.example.com"

# every <customer>.example.com not listed above
[upstream.2]
name        = "tenants"
host        = "127.0.0.1"
port        = 3002
hostname    = "*.example.com"
From bare OS to production

Install and go live

Six steps take a fresh Linux box to a TLS-terminating proxy running under systemd on port 443. This is the exact shape that fronts every domain here.

1 · Install the toolchain and build

nyx-proxy is a Nyx library. Install the compiler, clone, build the single binary.

$ curl -sSf https://nyxlang.com/install.sh | sh
$ sudo apt install -y clang certbot        # build + TLS deps
$ git clone https://github.com/nyxlang-dev/nyx-proxy
$ cd nyx-proxy && nyx build              # produces ./nyx-proxy

2 · Point DNS at the box

Create A records for each domain you'll serve, pointing at the server's public IP. Confirm they resolve before issuing certs.

$ dig +short example.com        # must return your server IP
$ dig +short api.example.com

3 · Issue TLS certificates

Let's Encrypt via certbot's standalone mode (it binds :80 briefly — make sure nothing else holds it yet). One cert per domain, or one multi-domain cert.

$ sudo certbot certonly --standalone \
      -d example.com -d api.example.com
# certs land in /etc/letsencrypt/live/example.com/

4 · Write proxy.toml

Point tls_cert / tls_key at the issued cert, list your upstreams by hostname (see the config above), and turn on caching, metrics and rate limiting as needed.

[server]
listen   = 443
workers  = 64
tls_cert = "/etc/letsencrypt/live/example.com/fullchain.pem"
tls_key  = "/etc/letsencrypt/live/example.com/privkey.pem"

[cache]
enabled  = 1

[metrics]
enabled  = 1
port     = 9090

5 · Run it under systemd

A unit keeps it alive across reboots and crashes. It needs to bind :443, so it runs as root (or with CAP_NET_BIND_SERVICE).

# /etc/systemd/system/nyx-proxy.service
[Unit]
Description=nyx-proxy
After=network-online.target

[Service]
ExecStart=/srv/nyx-proxy/nyx-proxy /srv/nyx-proxy/proxy.toml
WorkingDirectory=/srv/nyx-proxy
Restart=always
AmbientCapabilities=CAP_NET_BIND_SERVICE

[Install]
WantedBy=multi-user.target
$ sudo systemctl enable --now nyx-proxy
$ systemctl status nyx-proxy      # active (running)

6 · Verify and watch

Hit each domain over HTTPS, confirm a cache HIT, and scrape metrics. Certbot's timer renews certs automatically — reload the proxy after a renewal.

$ curl -sI https://example.com | head -1        # HTTP/2 200
$ curl -sD - https://example.com -o /dev/null | grep X-Nyx-Cache
X-Nyx-Cache: HIT
$ curl -s http://127.0.0.1:9090/metrics | head
Cert renewal

certbot installs a systemd timer that renews before expiry. Add a deploy hook — --deploy-hook "systemctl reload nyx-proxy" — so the proxy picks up the new cert without a manual restart.

Features

Everything you need

Multi-domain TLS with SNI
Serve multiple domains on a single IP. Each domain presents its own TLS certificate via SNI. This server routes 13 domains and a wildcard through one nyx-proxy.
Wildcard hostnames
hostname = "*.example.com" sends every one-label subdomain to one upstream — new tenants go live without touching the config. Exact hostnames always win over the wildcard, whatever their order; like a wildcard certificate, it covers neither the bare domain nor a.b.example.com.
Health checks
Active TCP health checks every N seconds. Failed backends are automatically removed from rotation and restored when they recover.
Access logs
Structured log format: timestamp, method, path, status, latency. Grep-friendly, mutex-protected writes.
Rate limiting
Per-IP sliding window counter. Returns HTTP 429 with Retry-After header. Configurable in proxy.toml.
Connection pooling
HTTP keep-alive between proxy and backends. Reduces connection overhead for high-throughput scenarios.
Response cache (LRU + TTL)
Caches GET 200 responses under HOST:PATH and honours upstream Cache-Control (no-store / no-cache / private bypass; max-age overrides). Hits are marked X-Nyx-Cache: HIT.
Prometheus /metrics
Request counts, status classes, latency and upstream health exposed on a separate admin port — scrape it straight into Grafana.
X-Forwarded-For, -Host, -Proto
Injects the real client IP, the hostname it asked for and the original scheme, so your backends can log by origin and serve several tenants from one process.
HTTP redirect
Automatic HTTP-to-HTTPS redirect on port 80. All traffic securely upgraded.
Observability

Structured access logs

2026-04-02 10:15:23 GET / 200 12ms
2026-04-02 10:15:24 GET /api/users 200 3ms
2026-04-02 10:15:24 POST /api/data 201 8ms
2026-04-02 10:15:25 GET /assets/style.css 200 1ms
2026-04-02 10:15:30 GET /api/spam 429 0ms
Comparison

Replaces Nginx

For simple reverse proxy setups, nyx-proxy does the same job in a fraction of the size.

Nginx
~1.5MB binary. Complex configuration syntax. Module system. Dozens of dependencies.
nyx-proxy
~560KB binary. Simple TOML config. TLS, wildcard routing, health checks, rate limiting, LRU cache, /metrics and access logs built-in. Links only OpenSSL, Boehm GC and zlib; no module system.
Ecosystem

Part of the Nyx ecosystem

NyxKV
Redis-compatible KV store — 64 commands, Pub/Sub.
redis-compatible
nyx-serve
The web framework — WebSockets, templates, sessions.
9,971 req/s
nyx-queue
Persistent message queue with ACK/redelivery.
3,252 msg/s