{ email {$ACME_EMAIL} admin off servers { listener_wrappers { proxy_protocol { timeout 2s allow {$TRUST_PROXY} } tls } trusted_proxies static {$TRUST_PROXY} client_ip_headers X-Forwarded-For X-Real-IP } } # Internal only, for the container healthcheck. Never published in compose. :8080 { respond /healthz "ok" 200 respond "not found" 404 } http://{$SITE_ADDRESS} { handle /.well-known/acme-challenge/* { respond "ACME challenge not found" 404 } handle { redir https://{$SITE_ADDRESS}{uri} 308 } } {$SITE_ADDRESS} { handle /api/sync/* { reverse_proxy sync:3001 { header_up X-Pardis-Client-IP {client_ip} } } root * /srv encode zstd gzip # The service worker and the HTML shell must be revalidated on every request. # If a browser is allowed to serve sw.js from its own cache, a new build is # never discovered and the in-app update prompt can never fire — the app would # silently freeze on whatever version it first installed. @volatile path / /index.html /sw.js /manifest.webmanifest /app-version.json header @volatile Cache-Control "no-cache, must-revalidate" # Content-hashed filenames; the name changes whenever the bytes do. @hashed path /assets/* /workbox-*.js header @hashed Cache-Control "public, max-age=31536000, immutable" # Icons keep stable names across builds, so cap them at a day rather than # forever, otherwise a replaced icon never surfaces. @icons path *.png *.svg *.ico header @icons Cache-Control "public, max-age=86400" # Some clients reject a manifest that is not served with this exact type. @manifest path /manifest.webmanifest header @manifest Content-Type "application/manifest+json" header { Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'; form-action 'self'" # Apply HSTS to this site address only; deliberately omit includeSubDomains. Strict-Transport-Security "max-age=31536000" X-Content-Type-Options nosniff X-Frame-Options DENY Referrer-Policy strict-origin-when-cross-origin -Server } # The app routes on the hash, so this only catches someone typing a stray path. handle { try_files {path} /index.html file_server } }