Skip to content

Reverse Proxy

In production Klokk runs behind a reverse proxy that terminates HTTPS. Klokk itself listens on plain HTTP, port 8080.

Set these two variables for Klokk whenever it runs behind HTTPS:

Variable Value Why
BASE_URL https://time.example.com (no trailing slash) Links in e-mails, calendar feeds and the address passkeys are bound to
SESSION_SECURE_COOKIE true (the default in production) Cookies are only sent over HTTPS
Caddyfile
time.example.com {
reverse_proxy klokk:8080
}

Caddy obtains and renews TLS certificates by itself. It passes X-Forwarded-For and X-Forwarded-Proto on its own.

Live updates: Klokk keeps a connection open (/api/events, server-sent events) for notifications and the clock. It tells nginx and Traefik not to buffer it; if a proxy closes it anyway, the browser reconnects by itself.

Klokk logs the client’s IP address (audit log, rate limits, the admin IP allow-list). Behind a proxy it reads X-Forwarded-For — but only from proxies it trusts, so nobody can fake an address by sending the header themselves.

  • One proxy on the same host or in a private network (the usual case): nothing to set. Klokk trusts the direct proxy if its address is private or loopback and takes the entry it appended.
  • Several proxies in a row (for example Cloudflare → a WAF → Klokk): list the outer ones in TRUSTED_PROXIES:
Value Trusts
cloudflare Cloudflare’s published ranges; CF-Connecting-IP is then believed from them
private every private and loopback range — for chains inside a private network
203.0.113.10, 198.51.100.0/24 single addresses or CIDR ranges

Several values are separated by commas, for example TRUSTED_PROXIES=cloudflare,private.