Skip to content

Serve over HTTPS

A production deployment needs TLS. Outside development mode the session cookie is marked Secure, and a browser does not send it over plain HTTP, so without TLS nobody can stay signed in. Sift ships an overlay, docker-compose.tls.yml, that makes the console’s nginx serve HTTPS with a certificate you supply. You can instead put your own TLS front end in front of Sift.

Sift does not issue or renew certificates. That is your PKI’s job.

You need, in PEM form:

FileContents
tls.crtThe certificate’s full chain, leaf first.
tls.keyThe private key, unencrypted.

Put both in ./tls beside docker-compose.yml, or in the directory SIFT_TLS_DIR names. nginx mounts the directory read-only and does not start without both files.

To try the overlay with a throwaway self-signed certificate:

Terminal window
mkdir -p tls && openssl req -x509 -newkey rsa:2048 -nodes -days 30 \
-subj "/CN=localhost" -keyout tls/tls.key -out tls/tls.crt
  1. Place tls.crt and tls.key as above.

  2. Add the overlay to the compose files in .env, so every make and docker compose command uses it:

    Terminal window
    COMPOSE_FILE=docker-compose.yml:docker-compose.tls.yml
  3. Apply it:

    Terminal window
    make up

To use the overlay for one command instead, name both files:

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

COMPOSE_FILE replaces Compose’s default file list. If you also use a docker-compose.override.yml, it is read only when COMPOSE_FILE names it too.

Without the overlayWith the overlay
SIFT_HTTPS_PORT (443)not publishedThe console, the API and these docs over HTTPS.
SIFT_WEB_PORT (8080)The console and the API over HTTP.A 301 redirect to https://<host>/, and nothing else.
ProtocolsTLS 1.2 and 1.3, HTTP/2, session tickets off.
Strict-Transport-Securitynever sentmax-age=31536000 on every HTTPS response.

The redirect names no port, so it lands on 443. If you publish HTTPS on another port with SIFT_HTTPS_PORT, give people the full https://host:port/ address.

Every response, over either listener, also carries X-Content-Type-Options: nosniff, X-Frame-Options: DENY and Referrer-Policy: no-referrer. HSTS leaves out includeSubDomains and preload, which would commit every host under your domain to HTTPS; that is your decision to make.

Replace tls.crt and tls.key in place, then restart the web container so nginx reads them:

Terminal window
docker compose restart sift-web

Nothing else needs restarting.

Sift’s login throttle limits failed sign-ins per client address. It takes the address from X-Forwarded-For, counting SIFT_TRUSTED_PROXY_HOPS entries from the right: the address the outermost proxy you trust recorded, never what the client wrote.

Your setupSIFT_TRUSTED_PROXY_HOPS
Clients reach Sift’s nginx directly1 (the default)
A load balancer or TLS terminator in front of Sift’s nginx also appends to X-Forwarded-For2
Nothing proxies the API0

Set too low, every client shares the load balancer’s budget. Set too high, a client can choose its own address again. The count is sound only while the API cannot be reached around those proxies, which is one reason the stack never publishes the API’s own port.

You can terminate TLS at a proxy or load balancer of your own and leave the overlay off. Point it at SIFT_WEB_PORT, set SIFT_TRUSTED_PROXY_HOPS=2 if it appends to X-Forwarded-For, and keep the plain port reachable only from that proxy. The proxy must not buffer text/event-stream responses, and its read timeout should allow for slow answers: Sift’s own nginx waits up to 300 seconds for the API, and up to 900 seconds under /api/v1/copilot/, where a request can wait on a model. The assistant’s message stream sends a keepalive every 15 seconds while it waits.