Custom Domain & HTTPS
CompassDocs ships a ready-to-use Caddy reverse-proxy stack that serves the app on your own domain with TLS. You configure everything — the domain and the certificate mode — from the web UI: right in the setup wizard on a fresh install, or any time under Settings → Domain & HTTPS. CompassDocs pushes the configuration to the proxy at runtime, so there are no config files to edit and no restarts.
Prerequisites
Section titled “Prerequisites”- A domain (or subdomain) whose DNS A/AAAA record points at your server.
- Ports 80 and 443 reachable from the internet (for Let’s Encrypt).
- The
deploy/docker-compose.tls.ymlanddeploy/Caddyfilefiles (included in the repo).
1. Start the TLS stack
Section titled “1. Start the TLS stack”You only need POSTGRES_PASSWORD in your .env — the domain is set later in the
UI.
docker compose -f docker-compose.tls.yml up -dThis runs the app behind Caddy. Until you set a domain, the app is served over plain HTTP so you can reach it and finish setup.
2. Configure your domain in the UI
Section titled “2. Configure your domain in the UI”Open the app, sign in as an admin, and go to Settings → Domain & HTTPS:
- Enter your domain (e.g.
docs.example.com). - Choose an HTTPS mode (see below).
- Click Save & apply.
The page shows whether the reverse proxy is connected, and applies your choice live. Open https://your-domain — Caddy obtains the certificate on first request. The app itself is not exposed on the host; all traffic flows through Caddy on 80/443.
HTTPS / TLS modes
Section titled “HTTPS / TLS modes”| Mode | When to use |
|---|---|
| Automatic HTTPS (Let’s Encrypt) | Public domain. Free certificate, auto-renewed. Optionally add a contact email for renewal notices. |
| Self-signed (internal CA) | LAN or testing. Browsers show a warning unless you trust Caddy’s local CA. |
| Bring your own certificate | You already have a certificate — paste the PEM certificate (with any chain) and private key. You handle renewals. |
| Plain HTTP | A load balancer or Cloudflare already terminates HTTPS in front of this server. |
The database is the source of truth: your settings are re-applied automatically whenever the app or the proxy restarts.
How it works
Section titled “How it works”The bundled Caddy container exposes its admin API only on the private Docker network (never on the host). CompassDocs translates your choices into a Caddy configuration and posts it to that API, so changes take effect without a restart. Bring-your-own certificates are stored by the app and written to a volume the proxy reads.
- Secure cookies work out of the box — the browser’s connection to Caddy is HTTPS and the app runs in production mode.
- Already behind another proxy or load balancer? Either choose Plain HTTP
here, or skip Caddy entirely and point your proxy at the app’s port from the
plain
docker-compose.yml.
Behind your own reverse proxy (nginx)
Section titled “Behind your own reverse proxy (nginx)”If you terminate HTTPS with your own reverse proxy instead of the bundled Caddy
stack, your proxy must forward the original scheme and host to CompassDocs.
The app builds absolute URLs — password-reset links, SCIM endpoints, and
especially the MCP connector’s OAuth discovery —
from these headers. A proxy that terminates HTTPS but forwards the request to
the app as plain http without X-Forwarded-Proto makes the app believe it’s
running over HTTP, so it advertises http:// URLs that OAuth clients reject.
A short set of forwarding headers is all you need. For nginx, put them
in the proxied location block:
location / { proxy_pass http://127.0.0.1:3000; # your app's host:port
proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; # critical: resolves to "https" proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Real-IP $remote_addr;}That’s the whole requirement — CompassDocs (including the MCP endpoint) speaks
ordinary request/response HTTP, so you don’t need WebSocket Upgrade
headers, proxy_http_version 1.1, or extended proxy_read_timeout values. If
your proxy already sends those for other apps they do no harm, but they aren’t
required here.
If a TLS-terminating layer sits in front of your nginx (Cloudflare’s proxy in
Flexible mode, or an outer load balancer speaking plain HTTP to nginx), then
$scheme at nginx is http and the forwarded value will be wrong. Either set
Cloudflare’s SSL/TLS mode to Full (strict) so it connects to your origin
over HTTPS, or hardcode proxy_set_header X-Forwarded-Proto https; on that host
(safe when it’s only ever served publicly over HTTPS).
Verify from outside your network:
curl https://your-domain/.well-known/oauth-authorization-serverEvery URL in the JSON should read https://your-domain/….
