# Automatic HTTPS from Let's Encrypt. The hostname has to resolve publicly to # this server, or issuance fails. # # Passkeys hang off exactly this name (GW_RP_ID). Changing it later invalidates # every registered passkey. {$GW_HOSTNAME} { encode zstd gzip # The application sets its own security headers (src/middleware.ts): CSP with # a nonce, frame-ancestors, nosniff, referrer and permissions policy, HSTS. # For a long time this comment claimed it did -- it did not, and the comment # was the reason nobody looked. # # HSTS stays here as well: this block is where TLS actually terminates, and # it applies to responses that never reach the application at all. header { Strict-Transport-Security "max-age=31536000; includeSubDomains" } # Access logs with an end date, because the privacy policy names one: it says # server logs are deleted after 14 days, and Caddy's default is to write to # stdout, where Docker keeps them until the disk says otherwise. `roll_keep_for` # is that sentence in the configuration rather than in a document. log { output file /var/log/caddy/access.log { roll_size 10MiB roll_keep 20 roll_keep_for 336h } } # Live collaboration: its own process, its own port, the same hostname. Caddy # forwards the WebSocket upgrade unchanged -- without this block it would end # up at the Next server, which cannot serve it. handle /collab* { reverse_proxy app:3001 } reverse_proxy app:3000 { # Next.js streams server components; buffering would make the page appear # only in one piece. flush_interval -1 } }