Skip to content

TLS and reverse proxies

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.

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

Section titled “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:

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.

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:

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.

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:

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).

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.

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.