server { listen 80; listen [::]:80; server_name localhost; # Point root to the frontend directory where static assets reside inside the container root /var/www/html; index index.html; # Enable detailed error logging error_log /var/log/nginx/error.log debug; access_log /var/log/nginx/access.log; # Global settings gzip on; gzip_types text/plain text/css application/javascript application/json; client_max_body_size __NGINX_MAX_BODY_SIZE_CONFIG_VALUE__; # Configurable global limit # Add CORS headers globally add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE'; add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization'; # MIME types - fixed duplicate js entry types { text/html html htm shtml; text/css css; application/javascript js; image/png png; image/jpeg jpg jpeg; image/gif gif; image/svg+xml svg svgz; application/pdf pdf; image/x-icon ico; application/json json; application/manifest+json webmanifest manifest; } # Specific location for locales JSON files location /locales/ { alias /var/www/html/locales/; try_files $uri =404; add_header Content-Type "application/json; charset=utf-8"; add_header Cache-Control "no-cache, no-store, must-revalidate"; expires -1; } # API requests - proxy to backend (fixed upstream host) # Using ^~ to ensure this prefix match takes precedence over regex extension matches location ^~ /api/ { proxy_pass http://127.0.0.1:5000; # Removed trailing slash to pass /api/ prefix 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; client_max_body_size __NGINX_MAX_BODY_SIZE_CONFIG_VALUE__; # Configurable limit for API location # Pass Authorization header to backend proxy_set_header Authorization $http_authorization; # Enhanced proxy settings for file handling proxy_buffering off; # Disable buffering for file downloads to prevent content-length mismatches proxy_request_buffering off; # Disable request buffering for uploads proxy_read_timeout 300s; # Increased timeout for large file transfers proxy_connect_timeout 30s; proxy_send_timeout 300s; # Prevent proxy from modifying response headers that could cause issues proxy_set_header Connection ""; proxy_http_version 1.1; # Add debug headers to see what's happening add_header X-Debug-Message "API request proxied to backend" always; # CORS for API add_header 'Access-Control-Allow-Origin' '*' always; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always; add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always; # CORS preflight if ($request_method = 'OPTIONS') { add_header 'Access-Control-Allow-Origin' '*' always; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always; add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always; add_header 'Access-Control-Max-Age' 1728000 always; add_header 'Content-Type' 'text/plain charset=UTF-8' always; add_header 'Content-Length' 0 always; return 204; } } # Uploads - serve files from uploads directory location /uploads/ { return 403 "Access forbidden"; } # HTML files - ensure proper content type location ~ \.html$ { add_header Content-Type "text/html; charset=utf-8"; add_header Cache-Control "no-cache, no-store, must-revalidate"; } # Favicon - specific handling location = /favicon.ico { log_not_found off; access_log off; try_files $uri =404; expires 7d; add_header Cache-Control "public, max-age=604800"; } # Service Worker - no cache to ensure updates location = /sw.js { try_files $uri =404; add_header Cache-Control "no-cache, no-store, must-revalidate"; expires -1; } # Static assets (CSS, JS, images) location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg)$ { try_files $uri =404; expires 7d; add_header Cache-Control "public, max-age=604800"; } # Test uploads path location = /test-uploads { add_header Content-Type text/plain; return 200 "Uploads directory exists: $document_root\n"; } # Specific handling for manifest.json location = /manifest.json { alias /var/www/html/manifest.json; # Serve from this specific path # The global types block should set the correct Content-Type # (application/manifest+json for 'manifest' extension) # If not found by alias, it will result in a 404, which is correct. # No need for try_files here if alias is used and file must exist. # If you want to be explicit about 404 if alias target doesn't exist: # if (!-f /var/www/html/manifest.json) { return 404; } # However, alias itself should handle this. # Add caching headers if desired, e.g.: # expires 1d; # add_header Cache-Control "public, must-revalidate"; } # Default location location / { try_files $uri $uri/ /index.html; } }