# Docker DNS resolver (127.0.0.11 = Docker's embedded DNS). # Required for variable-based proxy_pass below to resolve upstream # hostnames on each request instead of caching them at startup. # Without this, when backend containers are recreated (new IP), # nginx keeps pointing to stale IPs → 502 Bad Gateway. # Valid=10s re-resolves at most every 10 seconds to avoid excessive DNS queries. resolver 127.0.0.11 ipv6=off valid=10s; map $http_upgrade $connection_upgrade { default upgrade; '' close; } server { listen 80; server_name imphnen.asepharyana.my.id; # API proxy — matches /api and /api/* location ^~ /api { set $backend_url "http://backend:3000"; proxy_pass $backend_url$uri$is_args$args; 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; } # WebSocket proxy — matches /ws and /ws/* location ^~ /ws { set $backend_url "http://backend:3000"; proxy_pass $backend_url$uri$is_args$args; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $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; # WebSocket-specific: no buffering, longer timeouts proxy_buffering off; proxy_read_timeout 86400s; proxy_send_timeout 86400s; } # Gzip for static assets gzip on; gzip_types text/plain text/css application/json application/javascript application/wasm image/svg+xml; gzip_min_length 256; # Cache static assets (JS/WASM hashed filenames) location /assets/ { root /usr/share/nginx/html; expires 1y; add_header Cache-Control "public, immutable"; } # Frontend SPA fallback — serve static files directly location / { root /usr/share/nginx/html; index index.html; try_files $uri $uri/ /index.html; } }