> ## Documentation Index
> Fetch the complete documentation index at: https://docs.asobeast.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Hosting behind TLS and a reverse proxy

> Set AUTH_COOKIE_SECURE to true on any deployment reachable over TLS, and set TRUST_PROXY only when your reverse proxy controls X-Forwarded-For.

One setting decides whether a public deployment is safe: `AUTH_COOKIE_SECURE` must be `true` on any instance reachable over TLS, so the session cookie is never sent over plain HTTP.

## Put asobeast behind TLS

asobeast does not terminate TLS. Put a reverse proxy in front of port 3000 and let it handle certificates.

Caddy is the shortest path, because it obtains and renews certificates on its own.

```caddyfile theme={null}
aso.example.com {
  reverse_proxy 127.0.0.1:3000
}
```

nginx and Traefik work the same way. Whichever you use, the proxy must forward the `Host` header and the request path unchanged, because the web app serves the admin surfaces at identical paths.

Then set the API variables that assume TLS.

```bash theme={null}
AUTH_COOKIE_SECURE=true
TRUST_PROXY=true
WEB_PUBLIC_URL=https://aso.example.com
```

## Set TRUST\_PROXY correctly

`TRUST_PROXY` defaults to `false` and controls one thing: whether the API believes `X-Forwarded-For`.

| Value   | Auth rate limiting keys on                     | Use when                                            |
| ------- | ---------------------------------------------- | --------------------------------------------------- |
| `false` | The address the API sees, which is the proxy   | The instance is directly exposed, or you are unsure |
| `true`  | The real client address from `X-Forwarded-For` | A reverse proxy you control overwrites that header  |

<Warning>
  Setting `TRUST_PROXY=true` on a directly exposed instance lets any caller forge `X-Forwarded-For` and spread login attempts across fabricated addresses, which defeats the auth rate limiter. Enable it only behind a proxy that overwrites the header.
</Warning>

Leaving it `false` behind a proxy is safe but blunt: every client shares the proxy's address for rate limiting purposes, which is fine for a single operator instance.

## Set WEB\_PUBLIC\_URL

`WEB_PUBLIC_URL` is used for exactly one thing: building absolute deep links inside alert payloads. Set it to the origin a person would type.

Unset, the `link` field is `null` rather than a `localhost` guess that would not open for anyone. See [Send alerts](/guides/alerts).

## What does Compose expose?

Only port 3000, published by the web container. The API, PostgreSQL and Redis stay on Docker networks, and the backend network is marked `internal`.

That means the reverse proxy needs to reach port 3000 and nothing else. See [Self host with Docker Compose](/install/docker-compose).

## Checklist before going public

<Steps>
  <Step title="Terminate TLS in front of port 3000">
    Confirm the site loads over HTTPS with a valid certificate.
  </Step>

  <Step title="Set AUTH_COOKIE_SECURE=true">
    Then restart the API and sign in again.
  </Step>

  <Step title="Set TRUST_PROXY to match reality">
    `true` only if your proxy overwrites `X-Forwarded-For`.
  </Step>

  <Step title="Generate AUTH_SECRET properly">
    `openssl rand -hex 32`. Never a memorable string, never reused from development.
  </Step>

  <Step title="Leave registration closed">
    `AUTH_ALLOW_REGISTRATION=false` once the owner account exists.
  </Step>

  <Step title="Schedule and test backups">
    A backup that has never been restored is not a backup. See [Back up PostgreSQL](/operations/backups).
  </Step>
</Steps>

## Related

<CardGroup cols={2}>
  <Card title="Authentication and accounts" icon="lock" href="/security/authentication">
    Sessions, registration and the guards.
  </Card>

  <Card title="The queue dashboard and the OpenAPI surface" icon="shield" href="/security/admin-surfaces">
    Why those routes answer 404.
  </Card>
</CardGroup>
