# nginx reverse proxy for the parking system (production). # # Serves the built SPA (apps/web/dist) and proxies the API to the Fastify # backend on 127.0.0.1:3000. Same-origin: the SPA and API share one origin, so # the HttpOnly auth cookie and SameSite=Strict work without CORS. # # TLS terminates here. The backend runs with NODE_ENV=production, which makes # the auth cookies Secure (HTTPS-only) — so this server MUST be served over # https in production. A minimal http->https redirect block is included. # # Install: copy to /etc/nginx/sites-available/parking, symlink into # sites-enabled, set server_name + cert paths, then `nginx -t && systemctl reload nginx`. upstream parking_backend { server 127.0.0.1:3000; keepalive 16; } # Redirect http -> https. server { listen 80; server_name _; return 301 https://$host$request_uri; } server { listen 443 ssl; http2 on; server_name parking.local; # <-- set to your hostname ssl_certificate /etc/ssl/parking/fullchain.pem; # <-- set ssl_certificate_key /etc/ssl/parking/privkey.pem; # <-- set ssl_protocols TLSv1.2 TLSv1.3; # Built SPA assets. root /opt/parking/apps/web/dist; index index.html; # API + health -> Fastify backend. location ~ ^/(api|health) { proxy_pass http://parking_backend; proxy_http_version 1.1; 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; proxy_set_header Connection ""; # Cookies pass through unchanged (same-origin) — do not rewrite. } # SPA fallback: every other path serves index.html (client-side routing). location / { try_files $uri $uri/ /index.html; } # Long-cache hashed assets. location /assets/ { expires 1y; add_header Cache-Control "public, immutable"; } }