---
title: proxy
---
# `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.
```sh
trillium proxy [UPSTREAM]...
```
Proxy all traffic to a single upstream:
```sh
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:
```sh
trillium proxy http://app-1:4000 http://app-2:4000
trillium proxy http://app-1:4000 http://app-2:4000 --strategy connection-counting
```
| `round-robin` | (default) cycle through the upstreams in order |
| `connection-counting` | send each request to the upstream with the fewest open connections|
| `random` | pick an upstream at random per request |
| `forward` | classic 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`.
```sh
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`](./gateway/overview)
equivalent is opt-in, because a gateway shouldn't silently cache dynamic
upstreams.)
```sh
# 1 GiB cache, evict entries idle for 5 minutes
trillium proxy http://localhost:4000 \
--cache-capacity 1GiB --cache-time-to-idle 5m
```
| `--no-cache` | | disable caching entirely |
| `--cache-capacity` | `256MiB` | maximum total in-memory cache size (e.g. `256MiB`, `1GB`) |
| `--cache-max-body` | `16MiB` | largest cacheable body; bigger responses stream uncached |
| `--cache-time-to-idle` | | evict entries not read within this duration (e.g. `5m`) |
| `--cache-time-to-live` | | evict 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`](./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:
```sh
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
```
| `--cert` | `CERT` | listener certificate (PEM); requires `--key` |
| `--key` | `KEY` | listener private key (PEM); requires `--cert` |
| `--tls` | `TLS` | acceptor 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):
```sh
trillium proxy https://api.internal --client-tls rustls
trillium proxy https://localhost:4000 -k # self-signed dev cert
```
| `-c`, `--client-tls` | client backend for `https://` upstreams: `rustls`/`native`/`openssl` |
| `-k`, `--insecure` | skip 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`](./serve#single-page-apps-and-reverse-proxy-fallback),
where a 404 falls back to local files.)
- Proxied responses carry a `Via: trillium-proxy` header.
## Rate limiting
The same controls as [`serve`](./serve#rate-limiting): `--rate-limit RATE` caps
requests per client network, `--rate-limit-burst` permits short spikes.
Off unless `--rate-limit` is given.
```sh
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`](./gateway/overview).