upstream php-fpm { server 127.0.0.1:9000; } server { listen 80; server_name _; root /var/www/html/public; index index.php index.html index.htm; if ($http_x_forwarded_proto = 'http') { return 301 https://$host$request_uri; } # Security server_tokens off; # Disable access to hidden files location ~ /\. { deny all; access_log off; log_not_found off; } # Health check endpoint location /health { access_log off; return 200 "healthy\n"; add_header Content-Type text/plain; } # API routes location /api/ { limit_req zone=api burst=10 nodelay; try_files $uri $uri/ /index.php?$query_string; location ~ \.php$ { fastcgi_pass php-fpm; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; # Security headers for API add_header Access-Control-Allow-Origin "*" always; add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always; add_header Access-Control-Allow-Headers "Authorization, Content-Type, X-Requested-With" always; # Handle preflight requests if ($request_method = 'OPTIONS') { add_header Access-Control-Allow-Origin "*"; add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"; add_header Access-Control-Allow-Headers "Authorization, Content-Type, X-Requested-With"; add_header Access-Control-Max-Age 86400; add_header Content-Length 0; add_header Content-Type text/plain; return 204; } } } # Authentication endpoints with stricter rate limiting location ~ ^/api/(login|register|reset-password) { limit_req zone=login burst=5 nodelay; try_files $uri $uri/ /index.php?$query_string; location ~ \.php$ { fastcgi_pass php-fpm; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } } # Static files for Flutter web app location / { try_files $uri $uri/ /index.html; # Cache static assets location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { expires 1y; add_header Cache-Control "public, immutable"; add_header Vary Accept-Encoding; } # Cache HTML files for shorter period location ~* \.(html|htm)$ { expires 1h; add_header Cache-Control "public, must-revalidate"; } } # PHP files (for any remaining PHP endpoints) location ~ \.php$ { try_files $uri =404; fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass php-fpm; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; # Security fastcgi_param HTTP_PROXY ""; fastcgi_param SERVER_NAME $host; fastcgi_param REQUEST_SCHEME $scheme; # Timeouts fastcgi_connect_timeout 60s; fastcgi_send_timeout 60s; fastcgi_read_timeout 60s; } # File upload handling location /upload { client_max_body_size 10M; client_body_timeout 60s; try_files $uri $uri/ /index.php?$query_string; location ~ \.php$ { fastcgi_pass php-fpm; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } } # WebSocket proxy (for future real-time features) location /ws { proxy_pass http://127.0.0.1:8080; 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; proxy_cache_bypass $http_upgrade; } # Deny access to sensitive files location ~ /\.(htaccess|htpasswd|env|git) { deny all; access_log off; log_not_found off; } # Deny access to composer files location ~ /(composer\.(json|lock)|package\.(json|lock)) { deny all; access_log off; log_not_found off; } # Logging access_log /var/log/nginx/access.log main; error_log /var/log/nginx/error.log warn; }