Reverse Proxy
In production Klokk runs behind a reverse proxy that terminates HTTPS. Klokk itself listens on plain HTTP, port 8080.
Klokk’s settings
Section titled “Klokk’s settings”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 |
Proxy configuration
Section titled “Proxy configuration”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.
server { listen 443 ssl; http2 on; server_name time.example.com;
# ssl_certificate / ssl_certificate_key, e.g. from certbot
location / { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # Live updates (server-sent events) stay open for a long time. proxy_read_timeout 1h; }}services: klokk: image: klokkme/klokk:stable networks: [proxy] labels: - "traefik.enable=true" - "traefik.http.routers.klokk.rule=Host(`time.example.com`)" - "traefik.http.routers.klokk.entrypoints=websecure" - "traefik.http.routers.klokk.tls.certresolver=letsencrypt" - "traefik.http.services.klokk.loadbalancer.server.port=8080"
networks: proxy: external: trueKlokk then needs no published port (ports: can go).
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.
Real client addresses
Section titled “Real client addresses”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.