Skip to main content

trillium proxy

A reverse and forward proxy with upstream load-balancing, an in-memory response cache, compression, WebSocket passthrough, rate limiting, HTTPS, and HTTP/3.

trillium proxy [UPSTREAM]...

Proxy all traffic to a single upstream:

trillium proxy http://localhost:4000

Upstream URLs accept http://, https://, or a bare host:port (which defaults to http://). The upstream list also reads from the UPSTREAM environment variable.

Load balancing

Pass several upstreams to spread traffic across them. The default strategy is round-robin:

trillium proxy http://app-1:4000 http://app-2:4000
trillium proxy http://app-1:4000 http://app-2:4000 --strategy connection-counting
StrategyBehavior
round-robin(default) cycle through the upstreams in order
connection-countingsend each request to the upstream with the fewest open connections
randompick an upstream at random per request
forwardclassic forward proxy — pass no upstreams (see below)

-s / --strategy also reads the STRATEGY environment variable.

Forward-proxy mode

With --strategy forward and no upstreams, trillium proxy acts as a classic forward proxy: it forwards absolute-form requests and tunnels HTTPS via CONNECT.

trillium proxy --strategy forward
curl -x http://localhost:8080 https://example.com # use it as a proxy

Passing upstreams together with --strategy forward is an error, as is omitting upstreams for any other strategy.

Caching

The proxy ships with an in-memory response cache that honors standard caching headers (Cache-Control, ETag, …). It is on by default — appropriate for a proxy fronting cacheable content. (The gateway equivalent is opt-in, because a gateway shouldn't silently cache dynamic upstreams.)

# 1 GiB cache, evict entries idle for 5 minutes
trillium proxy http://localhost:4000 \
--cache-capacity 1GiB --cache-time-to-idle 5m
FlagDefaultNotes
--no-cachedisable caching entirely
--cache-capacity256MiBmaximum total in-memory cache size (e.g. 256MiB, 1GB)
--cache-max-body16MiBlargest cacheable body; bigger responses stream uncached
--cache-time-to-idleevict entries not read within this duration (e.g. 5m)
--cache-time-to-liveevict entries this long after they are stored (e.g. 1h)

The cache flags conflict with --no-cache.

Listening, HTTPS, and HTTP/3

The proxy listens like serve: -o / --host (env HOST, default localhost) and -p / --port (env PORT, default 8080). Provide a certificate and key to terminate TLS — and, with the default h3 feature, HTTP/3 over QUIC on the same port:

trillium proxy http://localhost:4000 --cert ./cert.pem --key ./key.pem
# or via the environment:
CERT=./cert.pem KEY=./key.pem trillium proxy http://localhost:4000
FlagEnvNotes
--certCERTlistener certificate (PEM); requires --key
--keyKEYlistener private key (PEM); requires --cert
--tlsTLSacceptor backend: rustls (default), native, openssl

Connecting to HTTPS upstreams

When an upstream is https://, the proxy needs a client TLS backend (separate from the listener's --tls acceptor):

trillium proxy https://api.internal --client-tls rustls
trillium proxy https://localhost:4000 -k # self-signed dev cert
FlagNotes
-c, --client-tlsclient backend for https:// upstreams: rustls/native/openssl
-k, --insecureskip upstream certificate verification (rustls only) — dangerous

--insecure disables authentication of the upstream entirely; use it only against hosts you control.

Compression, WebSockets, and 404s

  • Responses are compressed (gzip / brotli / zstd) based on the client's Accept-Encoding. Disable with --no-compress.
  • WebSocket upgrades are passed through to the upstream transparently.
  • Upstream 404 Not Found responses are forwarded to the client as-is. (This is the opposite of serve --forward, where a 404 falls back to local files.)
  • Proxied responses carry a Via: trillium-proxy header.

Rate limiting

The same controls as serve: --rate-limit RATE caps requests per client network, --rate-limit-burst permits short spikes. Off unless --rate-limit is given.

trillium proxy http://localhost:4000 --rate-limit 1000/min

Full flag reference

trillium proxy [OPTIONS] [UPSTREAM]...

Arguments:
[UPSTREAM]... Upstream URLs [env: UPSTREAM=]

Options:
-s, --strategy <STRATEGY> [env: STRATEGY=] [default: round-robin]
-o, --host <HOST> [env: HOST=] [default: localhost]
-p, --port <PORT> [env: PORT=] [default: 8080]
--cert <CERT> [env: CERT=]
--key <KEY> [env: KEY=]
--tls <TLS> [env: TLS=] [default: rustls]
-c, --client-tls <CLIENT_TLS> [default: rustls]
-k, --insecure
--no-compress
--rate-limit <RATE>
--rate-limit-burst <BURST> (requires --rate-limit)
-v, --verbose...
-q, --quiet...
-h, --help

Cache:
--no-cache
--cache-capacity <CACHE_CAPACITY> [default: 256MiB]
--cache-max-body <CACHE_MAX_BODY> [default: 16MiB]
--cache-time-to-idle <CACHE_TIME_TO_IDLE>
--cache-time-to-live <CACHE_TIME_TO_LIVE>

For a config-driven proxy that can mix in static files, redirects, header rewriting, and virtual hosts across several listeners, see gateway.