# docker/nginx/dev.conf # ============================================================================ # Development Nginx Configuration (HTTPS) # ============================================================================ # This configuration matches production by using HTTPS on port 443 with # self-signed certificates generated by mkcert. Port 80 redirects to HTTPS. # # This allows the dev container to work the same way as production: # - Frontend accessible on https://localhost (port 443) # - Backend API on http://localhost:3001 # - Port 80 redirects to HTTPS # # IMPORTANT: Dual Hostname Configuration (localhost AND 127.0.0.1) # ============================================================================ # The server_name directive includes BOTH 'localhost' and '127.0.0.1' to # prevent SSL certificate errors when resources use different hostnames. # # Problem Scenario: # 1. User accesses site via https://localhost/ # 2. Database stores image URLs as https://127.0.0.1/flyer-images/... # 3. Browser treats these as different origins, showing ERR_CERT_AUTHORITY_INVALID # # Solution: # - mkcert generates certificates valid for: localhost, 127.0.0.1, ::1 # - NGINX accepts requests to BOTH hostnames using the same certificate # - Users can access via either hostname without SSL warnings # # The self-signed certificate is generated in Dockerfile.dev with: # mkcert localhost 127.0.0.1 ::1 # # This creates a certificate with Subject Alternative Names (SANs) for all # three hostnames, allowing NGINX to serve valid HTTPS for each. # # See also: # - Dockerfile.dev (certificate generation, ~line 69) # - docs/FLYER-URL-CONFIGURATION.md (URL configuration details) # - docs/development/DEBUGGING.md (SSL troubleshooting) # ============================================================================ # HTTPS Server (main) server { listen 443 ssl; listen [::]:443 ssl; server_name localhost 127.0.0.1; # SSL Configuration (self-signed certificates from mkcert) ssl_certificate /app/certs/localhost.crt; ssl_certificate_key /app/certs/localhost.key; # Allow large file uploads (matches production) client_max_body_size 100M; # Proxy API requests to Express server on port 3001 location /api/ { proxy_pass http://localhost:3001; 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; } # ============================================================================ # Bugsink Sentry API Proxy (for frontend error reporting) # ============================================================================ # The frontend Sentry SDK cannot reach localhost:8000 directly from the browser # because port 8000 is only accessible within the container network. # This proxy allows the browser to send errors to https://localhost/bugsink-api/ # which NGINX forwards to the Bugsink container on port 8000. # # Frontend DSN format: https://localhost/bugsink-api/ # Example: https://localhost/bugsink-api/2 for Frontend (Dev) project # # The Sentry SDK sends POST requests to /bugsink-api//store/ # This proxy strips /bugsink-api and forwards to http://localhost:8000/api/ # ============================================================================ location /bugsink-api/ { proxy_pass http://localhost:8000/api/; 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; # Allow large error payloads with stack traces client_max_body_size 10M; # Timeouts for error reporting (should be fast) proxy_connect_timeout 10s; proxy_send_timeout 30s; proxy_read_timeout 30s; } # Proxy WebSocket connections for real-time notifications location /ws { proxy_pass http://localhost:3001; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; 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; } # Serve flyer images from static storage location /flyer-images/ { alias /app/public/flyer-images/; expires 7d; add_header Cache-Control "public, immutable"; } # Proxy all other requests to Vite dev server on port 5173 location / { proxy_pass http://localhost:5173; proxy_http_version 1.1; # WebSocket support for Hot Module Replacement (HMR) proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; # Forward real client IP 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; } # Security headers (matches production) add_header X-Frame-Options "SAMEORIGIN" always; add_header X-XSS-Protection "1; mode=block" always; add_header X-Content-Type-Options "nosniff" always; } # HTTP to HTTPS Redirect (matches production) server { listen 80; listen [::]:80; server_name localhost 127.0.0.1; return 301 https://$host$request_uri; }