{ # Global options admin off http_port 80 https_port 443 } # API subdomain api.{$DOMAIN} { reverse_proxy backend:8080 { header_up Host {host} header_up X-Real-IP {remote} header_up X-Forwarded-For {remote} header_up X-Forwarded-Proto {scheme} } # Security headers header { Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" X-Content-Type-Options "nosniff" X-Frame-Options "DENY" Referrer-Policy "strict-origin-when-cross-origin" Permissions-Policy "geolocation=(), microphone=(), camera=()" } # Rate limiting at proxy level (backup to app-level) @auth { path /api/v1/auth/* } rate_limit @auth { zone auth rate 10/minute key {remote} } } # Frontend main domain {$DOMAIN} { reverse_proxy frontend:3000 { header_up Host {host} header_up X-Real-IP {remote} header_up X-Forwarded-For {remote} header_up X-Forwarded-Proto {scheme} } # Security headers header { Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" X-Content-Type-Options "nosniff" X-Frame-Options "DENY" Referrer-Policy "strict-origin-when-cross-origin" Permissions-Policy "geolocation=(), microphone=(), camera=()" Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://api-maps.yandex.ru; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' wss: https://api.{$DOMAIN} https://api-maps.yandex.ru; frame-ancestors 'none';" } # Static assets caching @static { path /_next/static/* path /static/* path /images/* path /favicon.ico } header @static Cache-Control "public, max-age=31536000, immutable" # Compression encode zstd gzip } # MinIO console (optional, restricted) minio.{$DOMAIN} { reverse_proxy minio:9001 { header_up Host {host} header_up X-Real-IP {remote} header_up X-Forwarded-For {remote} header_up X-Forwarded-Proto {scheme} } # Basic auth for MinIO console (add credentials to env) basicauth { {$MINIO_CONSOLE_USER} {$MINIO_CONSOLE_PASSWORD} } }