server { listen 8080; server_name _; root /usr/share/nginx/html; index index.html; # Long-lived connections for the live-tail NDJSON streams # (snapshot + live events can hold open for minutes at a time). proxy_read_timeout 300s; proxy_send_timeout 300s; # Claims files can be large; 50 MiB matches what FastAPI accepts. client_max_body_size 50m; # API + auth: proxy to backend over the compose-managed bridge network. location /api/ { proxy_pass http://cyclone-backend:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # The backend sets the cookie with Path=/api/. Rewrite to / so # the browser sends it on every subsequent request to the SPA # (which never has a /api/ prefix on the URL). proxy_cookie_path /api/ /; } # SPA fallback: serve index.html for any non-/api route so deep-links # to /claims/123, /admin/users, etc. round-trip through reload. location / { try_files $uri $uri/ /index.html; } # Don't cache index.html — the SPA references hashed asset filenames, # so index.html is the only file that needs to be revalidated. location = /index.html { add_header Cache-Control "no-cache, no-store, must-revalidate" always; expires off; } }