events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; # Use Docker's internal DNS resolver with short TTL to handle container IP changes resolver 127.0.0.11 valid=10s ipv6=off; # Rate limiting limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s; limit_req_zone $binary_remote_addr zone=auth:10m rate=10r/s; server { listen 80; server_name localhost; # Security headers add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Content-Type-Options "nosniff" always; add_header X-XSS-Protection "1; mode=block" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; # Frontend — serve static files directly, fall back to index.html for SPA routes root /usr/share/nginx/html; index index.html; location / { limit_req zone=general burst=20 nodelay; try_files $uri $uri/ /index.html; } # Backend API location /api/ { limit_req zone=general burst=20 nodelay; set $backend_upstream http://backend:3001; proxy_pass $backend_upstream; 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_http_version 1.1; proxy_set_header Connection ""; } # Auth endpoints - stricter rate limiting location /api/auth/ { limit_req zone=auth burst=5 nodelay; set $backend_upstream http://backend:3001; proxy_pass $backend_upstream; 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; } # Health check location /health { set $backend_upstream http://backend:3001; proxy_pass $backend_upstream/api/health; access_log off; } } }