# Routing

What Ceryx does with a request, from the moment it arrives until it reaches your service.

## The life of a request

1. **Ceryx reads the hostname** from the request's `Host` header. For HTTPS, the TLS handshake comes first, and Ceryx picks the [certificate](/docs/concepts/certificates) for the hostname the client asked for.
2. **It finds the route** for that hostname. Hostnames match exactly: a route for `example.com` doesn't serve `www.example.com`. Without an exact match, Ceryx uses the catch-all route, whose source is `$wildcard`. Without either, it answers `503 Service Unavailable`.
3. **It applies the route's settings.** If the request is plain HTTP and the route has `enforce_https`, Ceryx redirects it to HTTPS. If the route requires client certificates, Ceryx checks the client's certificate. See [API protection with mTLS](/docs/use-cases/mtls).
4. **It proxies or redirects** the request to the route's target, depending on the route's `mode`.

## Targets

A target is the URL of a service, such as `http://app:8000`. Ceryx adds the request's path and query string to it:

| Target | Request | Goes to |
| --- | --- | --- |
| `http://app:8000` | `https://example.com/users?page=2` | `http://app:8000/users?page=2` |
| `http://app:8000/v2` | `https://example.com/users` | `http://app:8000/v2/users` |
| `app:8000` | `https://example.com/` | `http://app:8000/` |

A target without a scheme gets `http://`, and a trailing slash is dropped. Hostnames in targets are resolved with Docker's DNS, so containers on the `ceryx` network are reachable by name. Set `CERYX_DNS_RESOLVER` to use another DNS server.

## Proxy and redirect modes

In `proxy` mode, the default, Ceryx forwards the request to the target and returns the response. It forwards the original `Host` header and adds `X-Forwarded-For`, `X-Real-IP` and `X-Forwarded-Proto`. WebSocket upgrades pass through, and clients can use HTTP/2 over HTTPS.

In `redirect` mode, Ceryx answers with a permanent redirect (`301`) to the target, with the request's path and query string added. Use it to move visitors from one domain to another, for example from `www.example.com` to `example.com`.

## Where routes live

Routes are stored in a SQLite database in the `/var/lib/ceryx` volume, together with certificates. Ceryx caches route lookups in memory for a few seconds. Changes made through the API clear the cache, so they apply to the very next request, with no reload. Hostnames served by the catch-all route can briefly keep its previous target after you change it. See [Architecture](/docs/concepts/architecture#how-changes-take-effect).

## Errors

| Status | Cause |
| --- | --- |
| `503 Service Unavailable` | No route for the hostname, and no catch-all route. |
| `502 Bad Gateway` | Ceryx found the route but couldn't reach the target. Check that the service is running, and on the `ceryx` network if it's a container. |
| `413 Content Too Large` | The request body is larger than `CERYX_MAX_REQUEST_BODY_SIZE`. |
| `403 Forbidden` and `421 Misdirected Request` | The route requires client certificates. See [API protection with mTLS](/docs/use-cases/mtls#what-clients-see). |
