Utility Handlers
These smaller crates handle HTTP-level concerns that most applications encounter. They're each a separate crate so you only compile what you use.
Compression
Compresses response bodies using zstd, brotli, or gzip (in that preference order), selected based on the client's Accept-Encoding header. Place it early in your handler chain so it wraps all downstream responses.
use trillium_compression::Compression;
trillium_smol::run((
Compression::new(),
|conn: trillium::Conn| async move { conn.ok("this body will be compressed") },
));
Caching headers
Handles ETag, Last-Modified, If-None-Match, and If-Modified-Since headers. When a downstream handler sets an etag or last-modified timestamp, this handler automatically returns 304 Not Modified for unchanged resources.
use trillium_caching_headers::CachingHeaders;
trillium_smol::run((
CachingHeaders::new(),
// downstream handlers set ETags or Last-Modified values
));
Conn ID
Assigns each request a unique identifier. The ID is accessible via the ConnIdExt trait extension on Conn, and can be added to log output via a formatter for trillium-logger.
use trillium_conn_id::{ConnId, ConnIdExt, log_formatter::conn_id};
use trillium_logger::{Logger, apache_combined};
trillium_smol::run((
Logger::new().with_formatter(apache_combined(conn_id, "-")),
ConnId::new(),
|conn: trillium::Conn| async move {
let id = conn.id().to_string();
conn.ok(format!("request id: {id}"))
},
));
Head
Handles HEAD requests by running the full handler chain as if it were a GET, then stripping the response body before sending. Without this, HEAD requests return a 404 unless a handler explicitly checks for them.
use trillium_head::Head;
trillium_smol::run((Head::new(), my_handler));
Method override
Rewrites the HTTP method based on a _method query parameter. HTML forms only support GET and POST; this handler lets them submit DELETE, PATCH, and PUT requests.
use trillium_method_override::MethodOverride;
trillium_smol::run((MethodOverride::new(), my_handler));
// POST /items/42?_method=DELETE arrives at my_handler as DELETE /items/42
Forwarding
When Trillium runs behind a reverse proxy, the remote IP and protocol in Conn reflect the proxy, not the actual client. This handler reads Forwarded and X-Forwarded-* headers from trusted proxies and corrects those values.
use trillium_forwarding::Forwarding;
trillium_smol::run((
Forwarding::trust_always(), // or configure trusted IP ranges
my_handler,
));
Basic auth
Enforces HTTP Basic Authentication. Requests without valid credentials receive a 401 Unauthorized response with a WWW-Authenticate: Basic challenge.
use trillium_basic_auth::BasicAuth;
trillium_smol::run((
BasicAuth::new("admin", "s3cr3t").with_realm("My App"),
|conn: trillium::Conn| async move { conn.ok("authenticated") },
));