tileserver-rs uses the trailing slash to distinguish API endpoints from the built-in web viewer. The same path with and without a trailing slash can be two different things:
| Path | What it serves |
|---|---|
/styles/protomaps-light | Style JSON (API) |
/styles/protomaps-light/ | Map viewer UI (SPA) |
/data/protomaps | TileJSON (API) |
/data/protomaps/ | Data inspector UI (SPA) |
Normalization rules
Every request path is normalized before routing:
- API paths are trimmed —
/health/,/ping/,/data.json/,/styles.json/,/_openapi/and any other slashed API path serve the same response as their canonical slash-less form. Curl either; both work. - SPA viewer paths are preserved — exactly two families keep their
trailing slash:
/styles/{id}/and/data/{id}/. These fall through to the embedded web UI (thefrontendfeature) instead of the greedy API routes.
This means "Open in viewer" and "Inspect" links from the home page
(/styles/{id}/ and /data/{id}/) are stable, shareable URLs — including
query flags like /styles/{id}/?raster — while every API consumer gets
canonical, slash-insensitive endpoints.
Info
Implementation lives in crates/tileserver-rs/src/trailing_slash.rs — a
selective layer applied outside the router. A blanket trim would collapse the
viewer routes onto the API routes and 404 every viewer link; the selective
layer exists precisely to prevent that regression.