# ospab.host Nginx Configuration # Production configuration for React SPA + Express Backend # Upstream для бэкенда upstream backend_api { server 127.0.0.1:5000; keepalive 32; } # Rate limiting zones limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s; limit_req_zone $binary_remote_addr zone=login_limit:10m rate=5r/m; # Redirect HTTP to HTTPS server { listen 80; listen [::]:80; server_name ospab.host www.ospab.host; # Let's Encrypt challenge location /.well-known/acme-challenge/ { root /var/www/certbot; } location / { return 301 https://$host$request_uri; } } # Main HTTPS server server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name ospab.host www.ospab.host; # SSL Configuration ssl_certificate /etc/apache2/ssl/ospab.host.fullchain.crt; ssl_certificate_key /etc/apache2/ssl/ospab.host.key; ssl_session_timeout 1d; ssl_session_cache shared:SSL:50m; ssl_session_tickets off; # Modern SSL configuration ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; ssl_prefer_server_ciphers off; # HSTS add_header Strict-Transport-Security "max-age=63072000" always; # OCSP Stapling ssl_stapling on; ssl_stapling_verify on; resolver 8.8.8.8 8.8.4.4 valid=300s; resolver_timeout 5s; # Root directory for frontend build root /var/www/ospab-host/frontend/dist; index index.html; # Gzip compression gzip on; gzip_vary on; gzip_proxied any; gzip_comp_level 6; gzip_types text/plain text/css text/xml application/json application/javascript application/xml application/rss+xml image/svg+xml; # 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; add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always; # API proxy to backend location /api/ { limit_req zone=api_limit burst=20 nodelay; proxy_pass https://backend_api; 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; proxy_read_timeout 60s; proxy_connect_timeout 60s; # Error handling - redirect to custom error page proxy_intercept_errors on; error_page 502 503 504 = @error_page; } # Auth endpoints - stricter rate limiting location /api/auth/login { limit_req zone=login_limit burst=5 nodelay; proxy_pass https://backend_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; proxy_intercept_errors on; error_page 502 503 504 = @error_page; } location /api/auth/register { limit_req zone=login_limit burst=3 nodelay; proxy_pass https://backend_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; proxy_intercept_errors on; error_page 502 503 504 = @error_page; } # WebSocket support for real-time features location /ws { proxy_pass https://backend_api; 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_read_timeout 86400; } # Uploaded files (checks images) location /uploads/ { alias /var/www/ospab-host/backend/uploads/; expires 30d; add_header Cache-Control "public, immutable"; try_files $uri =404; } # Static assets with long cache location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { expires 1y; add_header Cache-Control "public, immutable"; try_files $uri =404; } # Manifest and service worker location ~* \.(webmanifest|manifest\.json)$ { expires 1d; add_header Cache-Control "public"; types { application/manifest+json webmanifest; } } # Health check endpoint location /health { access_log off; return 200 "OK"; add_header Content-Type text/plain; } # Robots.txt location = /robots.txt { access_log off; try_files $uri =404; } # Sitemap location = /sitemap.xml { access_log off; try_files $uri =404; } # Custom error page handler location @error_page { internal; rewrite ^ /error?code=$upstream_status redirect; } # Error pages error_page 404 /index.html; error_page 500 502 503 504 /error?code=$status; # SPA fallback - all other routes go to index.html location / { try_files $uri $uri/ /index.html; } # Block common attacks location ~ /\. { deny all; access_log off; log_not_found off; } location ~* /(\.git|\.env|\.htaccess|\.htpasswd|node_modules)/ { deny all; return 404; } # Block sensitive files location ~* \.(sql|bak|backup|log|ini|conf)$ { deny all; return 404; } # Access and error logs access_log /var/log/nginx/ospab.host.access.log; error_log /var/log/nginx/ospab.host.error.log; } # Redirect www to non-www server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name www.ospab.host; ssl_certificate /etc/apache2/ssl/ospab.host.fullchain.crt; ssl_certificate_key /etc/apache2/ssl/ospab.host.key; return 301 https://ospab.host$request_uri; } # API server server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name api.ospab.host; # SSL Configuration ssl_certificate /etc/letsencrypt/live/api.ospab.host/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/api.ospab.host/privkey.pem; ssl_session_timeout 1d; ssl_session_cache shared:SSL:50m; ssl_session_tickets off; # Modern SSL configuration ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; ssl_prefer_server_ciphers off; # HSTS add_header Strict-Transport-Security "max-age=63072000" always; # OCSP Stapling ssl_stapling on; ssl_stapling_verify on; resolver 8.8.8.8 8.8.4.4 valid=300s; resolver_timeout 5s; # Proxy to backend location / { proxy_pass https://backend_api; 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; proxy_read_timeout 60s; proxy_connect_timeout 60s; } }