# Static files EDRI's API Listener serves files from a directory on disk — stylesheets, scripts, images, fonts — for any request URL that no `@api(...)` event claims. This page describes how a request reaches a static file, what the response contains, and where the limits are. --- ## Quick start Point `EDRI_ASSETS_PATH` at a directory (default: `assets`, relative to the process working directory): ```bash export EDRI_ASSETS_PATH=assets ``` ``` assets/ └── static/ ├── css/ │ └── style.css ├── js/ │ └── app.js └── images/ └── logo.png ``` The URL path maps one-to-one onto the directory: | URL | File | |-----------------------------|------------------------------------| | `/static/css/style.css` | `assets/static/css/style.css` | | `/static/js/app.js` | `assets/static/js/app.js` | | `/static/images/logo.png` | `assets/static/images/logo.png` | Reference them from templates with root-absolute paths: ```html+jinja Logo ``` Relative paths (`css/style.css`) resolve against the current page URL and break on nested routes such as `/user/group/456/12`. There is no template tag for static URLs; `{% url %}` only generates URLs for events. --- (static-files-resolution)= ## How a request reaches a static file ```mermaid flowchart TD Req["HTTP request
GET /static/css/style.css"] --> Route{"URL matches an
@api route?
(any method)"} Route -->|yes| Event["event handling
(405 if method not registered)"] Route -->|no| Json{"JSON client?
X-Requested-With: XMLHttpRequest
or Accept lists application/json"} Json -->|yes| J404["404 (JSON)"] Json -->|no| Path{"path valid and
inside EDRI_ASSETS_PATH?"} Path -->|no| E400["400 (HTML)"] Path -->|yes| File{"regular file?"} File -->|no| E404["404 (HTML)"] File -->|yes| Method{"GET or HEAD?"} Method -->|yes| Serve["200 + file contents
(headers only for HEAD)"] Method -->|no| E405["405 (HTML)
Allow: GET, HEAD"] ``` The lookup happens in the Listener, before any event is created. Routes always win, and the static fallback has three consequences: * **A route on the path shadows the file.** If an event is registered at `/css` for `POST`, a `GET /css` returns `405 Method Not Allowed`, not a file named `assets/css`. * **A dynamic segment shadows a whole level.** With an event at `/docs/{slug}`, the URL `/docs/readme.txt` is routed to that event with `slug="readme.txt"`; `assets/docs/readme.txt` is never served. Keep assets under a prefix no event uses (for example `/static/`). * **JSON clients never get static files.** A request carrying `X-Requested-With: XMLHttpRequest`, or whose `Accept` header lists `application/json` (including `application/json, text/plain, */*`, the default of axios), receives a JSON `404` even when the file exists. A browser navigation, ``, `