Skip to main content

Routing & directives

Within a binding (or a virtual host), ordered route blocks dispatch requests by path. Each route names a pattern and holds a stack of directivesfiles, proxy, redirect, headers, rewrite-html — compiled, in document order, into a single handler for that path.

binding ":8080" {
route "/api/*" {
proxy {
upstream "http://127.0.0.1:9000"
}
}
route "/*" {
files root="./public" index="index.html"
}
}

Route patterns

Patterns are routefinder patterns, the same syntax trillium's router uses everywhere:

  • /* — a wildcard matching any path (the catch-all).
  • /api/* — everything under /api.
  • /users/:id — a named segment.

Routes match for all HTTP methods (including HEAD, OPTIONS, CONNECT, TRACE), so dispatch is purely by path. When several routes could match, the most specific pattern wins — order in the file doesn't decide specificity.

Prefix stripping

The matched prefix is stripped before the directive stack sees the request, exactly like a static file root. A /api/* route proxying to an upstream forwards /users (not /api/users) for a request to /api/users. To forward with the prefix intact, give the upstream its own base path — see proxy below.

files

Serve static files from a directory.

route "/*" {
files root="./public" index="index.html" directory-listing=true
}
PropertyNotes
root(required) directory to serve
indexindex filename for directory requests (e.g. index.html)
directory-listingtrue renders an HTML listing for directories with no index

This is the same static handler as serve; see that page for how index files and directory listings interact.

proxy

Reverse-proxy the route to one or more upstreams.

route "/api/*" {
proxy strategy="round-robin" {
upstream "http://127.0.0.1:9000"
upstream "http://127.0.0.1:9001"
}
}
Property / childNotes
strategyround-robin (default), connection-counting, or random
upstream "url"one or more upstream targets

Upstream 404s are forwarded to the client (a proxy route is terminal). WebSocket upgrades pass through, and responses carry a Via: trillium-gateway header. With a top-level cache node, proxied responses are cached.

Path forwarding

The forwarded path is the prefix-stripped path concatenated onto each upstream URL's own base path. This gives you explicit control over the prefix:

// request /api/users → upstream gets /users (route prefix stripped)
route "/api/*" {
proxy {
upstream "http://backend:9000"
}
}

// request /api/users → upstream gets /api/users (prefix preserved via base path)
route "/api/*" {
proxy {
upstream "http://backend:9000/api"
}
}

redirect

Respond with a Location redirect and halt.

route "/old/*" {
redirect "https://example.com/new" status=308
}
Argument / propertyNotes
(first argument)(required) target URL
statusredirect status code; defaults to 302 Found

headers

Mutate response headers. Operations apply in order and run late (in before_send), so they override headers set by the route's terminal handler — including removing headers added by a proxied upstream (like Server).

route "/*" {
files root="./public"
headers {
add "X-Served-By" "trillium"
set "Cache-Control" "public, max-age=3600"
remove "Server"
}
}
OperationNotes
add "Name" "value"append, keeping any existing values for that header
set "Name" "value"replace any existing values
remove "Name"remove the header

Directive ordering

Directives run in the order written. A body-producing directive (files / proxy) is terminal for the response body; place response-shaping directives like rewrite-html after it, and headers anywhere (it runs late regardless).

route "/*" {
proxy {
upstream "http://127.0.0.1:9000"
}
rewrite-html {
select "title" {
set-text "Proxied by trillium"
}
}
headers {
add "X-Served-By" "trillium"
}
}