Skip to content

Put the backend behind HTTPS

The backend speaks plain HTTP and has no login. A reverse proxy in front of it handles the certificate and decides who may connect. This guide serves the backend and the web chat at one HTTPS address, https://ambrogio.example.com: the proxy sends /api/* to the backend and everything else to the web chat.

  • The installation from Self-host Ambrogio. Run the commands from ~/ambrogio.
  • Caddy or nginx on the same host, and a name for the address with a certificate for it.

1. Keep the backend and the web chat private

Section titled “1. Keep the backend and the web chat private”

In compose.yaml, publish both ports on the loopback interface only, so that clients have to go through the proxy:

backend:
ports:
- "127.0.0.1:3000:3000"
web:
ports:
- "127.0.0.1:8080:8080"

If the proxy runs in a container, put it on the same Docker network as backend and web instead, and publish neither port.

Forward only /api/* to the backend: it carries the HTTP API and the WebSocket upgrade on /api/session. Other paths of the backend, such as /mcp/memory, stay inside the server.

Caddy forwards WebSocket upgrades on its own:

Caddyfile
ambrogio.example.com {
@outside not remote_ip 192.168.1.0/24
respond @outside 403
handle /api/* {
reverse_proxy 127.0.0.1:3000
}
handle {
reverse_proxy 127.0.0.1:8080
}
}

The @outside rule turns away anyone outside your home network. Adjust the range to yours.

nginx needs the upgrade headers spelled out:

/etc/nginx/conf.d/ambrogio.conf
server {
listen 443 ssl;
server_name ambrogio.example.com;
ssl_certificate /etc/ssl/ambrogio/fullchain.pem;
ssl_certificate_key /etc/ssl/ambrogio/privkey.pem;
allow 192.168.1.0/24;
deny all;
location /api/ {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
}
}

The default read timeout of nginx, 60 seconds, is enough: the backend pings every session every 30 seconds.

In the environment section of the backend service in compose.yaml, set:

AMBROGIO_ALLOWED_ORIGINS: https://ambrogio.example.com
AMBROGIO_PUBLIC_HTTPS: "true"
AMBROGIO_HSTS_MAX_AGE_SECONDS: "86400"

Start with one day of HSTS; raise it to a year (31536000) once you are sure HTTPS stays. If your proxy already sends a Strict-Transport-Security header, remove it there or here. See Backend configuration.

In web/config.json, set:

"backendUrl": "https://ambrogio.example.com"

Then recreate the containers:

Terminal window
docker compose up -d

5. Point the other clients at the new address

Section titled “5. Point the other clients at the new address”
  • Terminal client: pnpm start --server https://ambrogio.example.com
  • Satellite: AMBROGIO_BACKEND_URL: https://ambrogio.example.com

Open https://ambrogio.example.com and send a message. If it ends with “Cannot connect to the backend.”, the origin in AMBROGIO_ALLOWED_ORIGINS differs from the address bar, or the proxy does not forward the WebSocket upgrade on /api/session.