# hiai-docs Caddyfile # Service targets use canonical container ports; host port overrides do not # apply inside the Compose network. :80 { # For local development / Docker internal reverse_proxy web:50701 encode gzip log header { # Prod-safe CSP — the :80 catch-all must not hardcode dev origins. # Developers: override in a local Caddyfile if you need dev-specific # connect-src (e.g. http://localhost:50700 ws://localhost:50700). Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; worker-src 'self' blob:; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https:; connect-src 'self' https:// wss://; font-src 'self' data:; frame-ancestors 'none'; form-action 'self'" # HSTS — harmless on plain HTTP, enforced once TLS is provisioned Strict-Transport-Security "max-age=31536000; includeSubDomains" X-Content-Type-Options "nosniff" X-Frame-Options "DENY" Referrer-Policy "strict-origin-when-cross-origin" } # PWA asset cache-control (service worker must always be re-fetched) @sw path /sw.js header @sw Cache-Control "no-cache, no-store, must-revalidate" @manifest path /manifest.webmanifest header @manifest Cache-Control "public, max-age=3600" @immutable path /_app/immutable/* header @immutable Cache-Control "public, max-age=31536000, immutable" @images path /pwa-*.png /apple-touch-icon.png /maskable-icon.png header @images Cache-Control "public, max-age=86400" } # Public object-storage endpoint used by presigned import/download URLs. Keep # it on a separate host so S3 object paths never overlap application routes. storage.docs.{$DOMAIN:localhost} { reverse_proxy seaweedfs:8333 } docs.{$DOMAIN:localhost} { # For production — Caddy auto-provisions TLS reverse_proxy web:50701 encode gzip log rate_limit { zone dynamic { key {remote_host} events 100 window 1m } } header { # Restrictive CSP — SvelteKit requires 'unsafe-inline' in style-src # because it injects styles at runtime and cannot use nonces. # This is an accepted trade-off for SvelteKit SPAs. Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; worker-src 'self' blob:; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https:; connect-src 'self' wss:// https://; font-src 'self' data:; frame-ancestors 'none'; form-action 'self'" # HSTS — 1 year, include subdomains, preload-ready Strict-Transport-Security "max-age=31536000; includeSubDomains" X-Content-Type-Options "nosniff" X-Frame-Options "DENY" Referrer-Policy "strict-origin-when-cross-origin" } # PWA asset cache-control (service worker must always be re-fetched) @sw path /sw.js header @sw Cache-Control "no-cache, no-store, must-revalidate" @manifest path /manifest.webmanifest header @manifest Cache-Control "public, max-age=3600" @immutable path /_app/immutable/* header @immutable Cache-Control "public, max-age=31536000, immutable" @images path /pwa-*.png /apple-touch-icon.png /maskable-icon.png header @images Cache-Control "public, max-age=86400" }