{ email {$ACME_EMAIL} admin off } {$SITE_DOMAIN} { encode zstd gzip request_body { max_size 10M } @immutable path /_astro/* header @immutable Cache-Control "public, max-age=31536000, immutable" @brandAssets path /favicon.svg /favicon-32.png /apple-touch-icon.png /icon-192.png /icon-512.png /icon-maskable-192.png /icon-maskable-512.png /og-rf4spotter.png header @brandAssets Cache-Control "public, max-age=86400, stale-while-revalidate=604800" @manifest path /site.webmanifest header @manifest Cache-Control "public, max-age=3600" @admin path /admin/* handle @admin { basic_auth { {$ADMIN_BASIC_USER} {$ADMIN_BASIC_PASSWORD_HASH} } reverse_proxy web:4321 } @adminApi path /api/v1/admin/* handle @adminApi { # FastAPI verifies the Bearer token. Basic Auth here would reject the # browser's Bearer Authorization header before it reached the API. reverse_proxy api:8000 } # Astro owns /api/report and /api/report-screenshot; only the versioned # backend namespace and health endpoints belong to FastAPI. @api path /api/v1/* /health /ready handle @api { reverse_proxy api:8000 } handle { reverse_proxy web:4321 } header { -Server Strict-Transport-Security "max-age=31536000; includeSubDomains" X-Content-Type-Options "nosniff" Referrer-Policy "strict-origin-when-cross-origin" Permissions-Policy "camera=(), microphone=(), geolocation=()" X-Frame-Options "DENY" Cross-Origin-Opener-Policy "same-origin" Content-Security-Policy "default-src 'self'; base-uri 'self'; object-src 'none'; frame-ancestors 'none'; form-action 'self'; connect-src 'self'; img-src 'self' data: https://{$FILES_DOMAIN}; font-src 'self'; media-src 'self'; manifest-src 'self'; script-src 'self' 'unsafe-inline'; script-src-attr 'none'; style-src 'self'; style-src-attr 'unsafe-inline'; upgrade-insecure-requests" } } {$FILES_DOMAIN} { encode zstd gzip reverse_proxy minio:9000 header { -Server Strict-Transport-Security "max-age=31536000; includeSubDomains" X-Content-Type-Options "nosniff" Referrer-Policy "no-referrer" X-Frame-Options "DENY" Content-Security-Policy "default-src 'none'; img-src 'self' data:; style-src 'unsafe-inline'; sandbox" } }