# Kaiser Natron — SPA static serve config. # Mounted into /etc/nginx/conf.d/default.conf by the Dockerfile. server { listen 80 default_server; listen [::]:80 default_server; server_name _; root /usr/share/nginx/html; index index.html; # ── Compression ─────────────────────────────────────────────────────── gzip on; gzip_vary on; gzip_min_length 1024; gzip_comp_level 6; gzip_types text/plain text/css text/javascript application/javascript application/json image/svg+xml font/woff2; # ── Security headers ───────────────────────────────────────────────── # Mirror the hardening we observed on the live site and tighten where possible. add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Content-Type-Options "nosniff" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always; # Keep CSP loose enough for Vue + Tailwind inline styles. Tighten once the # backend is wired and we know which origins we need to whitelist. add_header Content-Security-Policy "default-src 'self'; img-src 'self' data: blob:; style-src 'self' 'unsafe-inline'; script-src 'self'; font-src 'self' data:; connect-src 'self'; frame-ancestors 'self'; base-uri 'self'; form-action 'self'" always; # ── Healthcheck ────────────────────────────────────────────────────── location = /health { access_log off; add_header Content-Type text/plain; return 200 "ok\n"; } # ── Hashed build assets — long-cache ───────────────────────────────── # Vite emits filenames like assets/foo-.js, safe to cache for a year. location /assets/ { access_log off; expires 1y; add_header Cache-Control "public, max-age=31536000, immutable"; try_files $uri =404; } # ── Everything else: SPA fallback ──────────────────────────────────── # index.html must never be cached so users pick up new asset hashes on deploy. location = /index.html { add_header Cache-Control "no-store, must-revalidate" always; } location / { # $uri.html lets the static handoff docs resolve at clean, # extensionless URLs (/dev-doc → /dev-doc.html) before the SPA # history fallback takes over for app routes. try_files $uri $uri.html $uri/ /index.html; } # Don't serve dotfiles that somehow end up in the doc root. location ~ /\. { deny all; return 404; } }