# Serve static files

`elide serve DIR` serves a directory over HTTP/1.1. It sends files as they are on disk: no reload, compression,
cache headers or TLS. For reload while editing, see [Development server](/docs/dev-server); to build the directory, see
[Static sites](/docs/toolchain/web/static-site).

```bash
elide serve ./dist
elide serve --host 0.0.0.0 --port 4000 ./dist
```

| Flag | Default | Effect |
| --- | --- | --- |
| `--host HOST` | `127.0.0.1` | Bind address; a hostname binds its first address. |
| `--port PORT` | `8080` | Bind port. |
| `--no-tui` | Off | Print the address only; requests are not logged. |

Without a terminal, as in CI or a pipe, `elide serve` behaves as with `--no-tui`. In Tern, `elide serve` opens the
[Server page](/docs/tern-terminal#elide-serve-in-tern), with requests, traces and logs.

With no directory argument, `elide serve` serves the first of:

1. `.dev/sites/main`
2. The only directory in `.dev/sites/`
3. The current directory

A file argument runs as a script instead; see [Run a fetch handler](/docs/dev-server#run-a-fetch-handler).

## Requests and files

| Request | Response |
| --- | --- |
| `/` | `index.html` |
| `/docs` or `/docs/` | `docs/index.html`, without a redirect |
| A directory without `index.html` | `404`; no directory listings |
| `/about` when only `about.html` exists | `404` |
| `/app.js?v=3` | `app.js`; the query is ignored |
| `/my%20page.html` | `my page.html`; paths are percent-decoded |
| `..`, `%2e%2e` or `\` in the path | `404` |
| A dot file or directory, such as `/.env` | `404`; `.well-known` is served |
| A symbolic link | Served if its target is inside the directory, else `404` |
| A FIFO or other non-regular file | `404` |

`/docs` serves `docs/index.html` at the URL `/docs`, so relative links in that page resolve against `/`. Link to
`/docs/` or use root-relative URLs.

| Method | Response |
| --- | --- |
| `GET` | `200` with the file |
| `HEAD` | `200` with the same headers and no body |
| Others | `405` with `Allow: GET, HEAD` |

## Headers and caching

| Header | Value |
| --- | --- |
| `Content-Type` | From the file extension; see below |
| `Content-Length` | File size |
| `Date` | Response time |
| `Server` | `elide` |

- No `ETag`, `Last-Modified`, `Cache-Control` or `Accept-Ranges`. `If-None-Match`, `If-Modified-Since` and `Range`
  requests get a full `200`.
- Responses are not compressed, and precompressed siblings such as `app.js.gz` are only served when requested by name.

| Extension | `Content-Type` |
| --- | --- |
| `html`, `htm` | `text/html; charset=utf-8` |
| `css` | `text/css` |
| `js`, `mjs` | `text/javascript` |
| `json` | `application/json` |
| `xml` | `application/xml` |
| `txt` | `text/plain` |
| `wasm` | `application/wasm` |
| `png`, `gif`, `webp` | `image/png`, `image/gif`, `image/webp` |
| `jpg`, `jpeg` | `image/jpeg` |
| `svg` | `image/svg+xml` |
| `ico` | `image/x-icon` |
| `woff`, `woff2` | `font/woff`, `font/woff2` |
| `pdf`, `zip` | `application/pdf`, `application/zip` |
| Anything else | `application/octet-stream` |

Extensions are case-sensitive: `logo.PNG` is sent as `application/octet-stream`. So are `.mp4`, `.avif`, `.map` and
`.webmanifest`.

## File changes

`elide serve` runs one worker thread per CPU core. Each keeps files up to 4 MiB in memory and checks their size and
modification time at most once a second, so edits and deletions show up within about a second. Larger files are read
from disk on every request.

## In production

- The default host accepts only local connections. Pass `--host 0.0.0.0`, or an interface address, to accept others.
- For TLS, HTTP/2, compression or cache headers, put a reverse proxy or CDN in front.
- For routing, APIs or custom headers, write a handler instead; see
  [Build an HTTP service](/docs/http-service#routes-json-and-streaming).
