trillium serve
A static file server with optional reverse-proxy fallback, HTTPS, HTTP/3, compression, rate limiting, and directory listings.
trillium serve [ROOT]
The simplest invocation serves the current directory on
http://localhost:8080:
trillium serve
Pick a directory, host, and port:
trillium serve ./public --host 0.0.0.0 --port 3000
Listening
| Flag | Env | Default |
|---|---|---|
-o, --host | HOST | localhost |
-p, --port | PORT | 8080 |
Bind to all interfaces with --host 0.0.0.0, or to a specific interface with
its address. The env-var fallbacks let you drive trillium serve from a
process manager or .env file with no flags at all.
HTTPS and HTTP/3
Provide a TLS certificate and key to serve over HTTPS, and — with the default
h3 feature — over HTTP/3 on the same port:
trillium serve --cert ./cert.pem --key ./key.pem
# or via the environment:
CERT=./cert.pem KEY=./key.pem trillium serve
| Flag | Env | Notes |
|---|---|---|
--cert | CERT | path to the certificate (PEM); requires --key |
--key | KEY | path to the private key (PEM); requires --cert |
--tls | TLS | acceptor backend: rustls (default), native, openssl |
For local development, mkcert or
rcgen will generate a trusted cert/key pair. Test an HTTPS + h3 server with
curl -k https://localhost:8080.
Which backends are available depends on Cargo features. The default build
includes rustls + h3. See Installing.
Index files and directory listings
By default a request that resolves to a directory looks for an index file
configured with --index (env INDEX):
trillium serve ./public --index index.html
If there is no index file, the request is treated as a 404. Pass
-l / --directory-listing (env DIRECTORY_LISTING=1) to instead render a
clickable HTML index of the directory's contents, with column headers that
sort by name, size, or modification time:
trillium serve ./files --directory-listing
Listings are off by default because they expose file names and structure.
When an --index file is configured it takes precedence — listings only
appear for directories without one.
Single-page apps and reverse-proxy fallback
-f / --forward (env FORWARD) puts a reverse proxy in front of the static
handler. Every request goes upstream first; local files only respond when
the upstream returns a 404. That makes it a drop-in for serving a built SPA
while letting an API backend handle its own routes:
trillium serve ./dist --forward http://localhost:4000
# or via the environment
FORWARD=http://localhost:4000 trillium serve ./dist
Upstream URLs accept http://, https://, or a bare host:port (which
defaults to http://). http+unix:// is not yet supported. For HTTPS
upstreams with self-signed certs, see the trillium-cli client and proxy
pages for the relevant --client-tls controls — serve --forward always uses
the default TLS backend.
It might seem more natural to serve files first and proxy on a local miss.
Going upstream first means the upstream owns its own URL space — any path it
chooses to handle (including dynamic routes that look like file paths) wins
over any matching file on disk. The static handler fills the gaps where the
upstream 404s. For an API-only backend that 404s everywhere except /api/*,
the two orderings behave the same way; the upstream-first ordering only
matters when the upstream and the static tree overlap.
Compression
Responses are compressed (gzip / brotli / zstd) automatically based on the
client's Accept-Encoding header. Compression applies to both static and
proxied responses. Disable it with:
trillium serve --no-compress
Rate limiting
Cap requests per client network with --rate-limit RATE. Over-quota requests
get 429 Too Many Requests with a Retry-After header, and every metered
response advertises the standard RateLimit and RateLimit-Policy headers:
trillium serve --rate-limit 100/min # sustained 100 req/min per network
trillium serve --rate-limit 10/s --rate-limit-burst 50 # allow short spikes
Rates are written COUNT/WINDOW, where WINDOW is s, min, or h (or
their longer forms — seconds, minutes, hours). --rate-limit-burst
permits short spikes above the sustained rate; it defaults to the rate
itself.
Rate limiting is off unless you pass --rate-limit.
Logging
-v increases the log level, -q decreases it. Logs are formatted with the
module path dimmed and the message in the default color:
trillium serve -v # info
trillium serve -vv # debug
trillium serve -vvv # trace
trillium serve -q # warn only
Quinn (the HTTP/3 implementation) is silenced by default at every log level so its internals don't drown out the request log.
Full flag reference
trillium serve [OPTIONS] [ROOT]
Arguments:
[ROOT] Filesystem path to serve (default: cwd)
Options:
-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]
-f, --forward <FORWARD> [env: FORWARD=]
-i, --index <INDEX> [env: INDEX=]
--no-compress
-l, --directory-listing [env: DIRECTORY_LISTING=]
--rate-limit <RATE>
--rate-limit-burst <BURST> (requires --rate-limit)
-v, --verbose...
-q, --quiet...
-h, --help
Always check trillium serve --help against the version you've installed —
this page documents the current stable release.