Skip to content

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.

  • 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.yml and deploy/Caddyfile files (included in the repo).

You only need POSTGRES_PASSWORD in your .env — the domain is set later in the UI.

Terminal window
docker compose -f docker-compose.tls.yml up -d

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

Open the app, sign in as an admin, and go to Settings → Domain & HTTPS:

  1. Enter your domain (e.g. docs.example.com).
  2. Choose an HTTPS mode (see below).
  3. 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.

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.

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.

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:

Terminal window
curl https://your-domain/.well-known/oauth-authorization-server

Every URL in the JSON should read https://your-domain/….