# TLS and reverse proxies

> Three ways to serve InfraInbox over HTTPS, from the built-in certificate to your own reverse proxy.

Web page: https://infrainbox.app/docs/self-hosting/reverse-proxy/

Every phone, browser and webhook sender talks to InfraInbox over HTTPS. There are three ways to get a certificate in front of it, in the order most self-hosters want them.

> **Note:** Whichever you pick, `INFRAINBOX_PUBLIC_URL` must match exactly what clients type or resolve — scheme, host and port. A request that arrives under a different `Host` gets an HTTP 421: that's a DNS-rebinding protection, not a bug. Other hostnames the server should also answer to go in `INFRAINBOX_HTTP_TRUSTED_HOSTS` (a comma-separated list). An identity provider's callback URL is built from `INFRAINBOX_PUBLIC_URL` as well, so register it with the address clients use, not the container's; so are the links in account mail (password reset, address confirmation).

## 1. The built-in TLS front door: Caddy and Let's Encrypt

For a server with a public DNS name. The compose file's Caddy override obtains and renews a browser-trusted Let's Encrypt certificate automatically and proxies to the app. Turn it on in `.env`:

```dotenv
COMPOSE_FILE=docker-compose.yml:docker-compose.caddy.yml
CADDY_ACME_EMAIL=you@example.com
```

then `docker compose up -d` as usual. What it needs from the outside world:

| Requirement | Why |
|---|---|
| A DNS `A` record for your host, pointing straight at this machine | Let's Encrypt resolves the name and connects back to it |
| TCP 80 open to the internet | The ACME HTTP-01 challenge, and the plain-HTTP→HTTPS redirect |
| TCP 443 open to the internet | The site itself |

Point the DNS record directly at the machine — no CDN or proxy in front. With this override on, the app itself publishes no port at all; Caddy is the only way in.

## 2. TLS in the app container (the default)

With no override, the app terminates TLS itself. Without a certificate configured it generates a self-signed one on first start, which browsers will warn about — fine for a private LAN instance, less fine for anything else. To use a real certificate you already have, mount it into the container and point these two settings at it:

```dotenv
INFRAINBOX_TLS_CERT_FILE=/certs/fullchain.pem
INFRAINBOX_TLS_KEY_FILE=/certs/privkey.pem
```

There is no proxy in front in this mode, so no `X-Forwarded-*` header is trusted — nothing more to configure.

## 3. Your own reverse proxy

Set `INFRAINBOX_TLS=off` to serve plain HTTP for a proxy you run yourself (Nginx Proxy Manager, HAProxy, an existing Traefik, and so on), and tell InfraInbox to trust it:

```dotenv
INFRAINBOX_TLS=off
INFRAINBOX_HTTP_TRUSTED_PROXIES=192.0.2.10
```

Two things your proxy must do:

- **Pass the original `Host` header through**, not its own. InfraInbox compares it against `INFRAINBOX_PUBLIC_URL`'s host; a proxy that rewrites it gets every request refused with a 421.
- **Never redirect a plain-HTTP hook or API call to HTTPS.** A source's key or a session cookie has already crossed the wire in clear by the time a redirect could save it. Refuse anything but `GET`/`HEAD` on port 80 instead of redirecting it (the Caddy examples below show the pattern).

> **Warning:** Set `INFRAINBOX_HTTP_TRUSTED_PROXIES` to your proxy's exact address or a narrow range it lives in — never the whole subnet it happens to share with other containers. A trusted range that's too wide lets a client forge its own `X-Forwarded-For` and land in someone else's rate-limit bucket or audit entry.

Without `INFRAINBOX_HTTP_TRUSTED_PROXIES` set, every request looks like it came from the proxy, so every per-client rate limit collapses into one shared bucket.

### Ready-made examples

The repository ships three worked Compose overrides under `deploy/examples/`, each an override on the base compose file (so the read-only container, pinned image tags and required passwords still apply):

| Example | What it gives you |
|---|---|
| `caddy/` | Caddy terminating TLS with its own internal CA — no public DNS name or open port 80 needed. Good for a LAN-only instance you still want real HTTPS on; phones pin the CA through the pairing QR code. |
| `traefik/` | Traefik terminating TLS from a certificate you already have, configured entirely from files (no Docker socket mounted into the proxy). |
| `tailscale/` | A Tailscale sidecar that serves the node's own `*.ts.net` name over HTTPS, reachable only from your tailnet. |

Each has its own short README next to the Compose file. Set `INFRAINBOX_TLS=off` and `INFRAINBOX_HTTP_TRUSTED_PROXIES` to the proxy's fixed address, the same as any other reverse proxy — the examples just do it for you.

> **Note:** A `*.ts.net` name is publicly visible in Certificate Transparency logs, so using Tailscale's own certificate makes the existence and hostname of your server public information, even though the server itself stays reachable only over your tailnet.
