# Rate limiting zones — included inside http{} block limit_req_zone $binary_remote_addr zone=auth_limit:10m rate=5r/m; limit_req_zone $binary_remote_addr zone=tv_limit:10m rate=10r/m; server { listen 80; server_tokens off; root /usr/share/nginx/html; index index.html; # 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 Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; connect-src 'self' ws: wss:; font-src 'self' data:;" always; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; # Gzip compression gzip on; gzip_types text/plain text/css application/javascript application/json application/x-javascript text/xml application/xml; gzip_min_length 1024; # Rate-limited auth endpoints (checked before the generic /api/ block) location ~ ^/api/(auth/(login|register)|admin/login)$ { limit_req zone=auth_limit burst=3 nodelay; limit_req_status 429; proxy_pass http://backend:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } # Rate-limited TV dashboard endpoint (public, token-based) location ~ ^/api/dashboard/ { limit_req zone=tv_limit burst=5 nodelay; limit_req_status 429; proxy_pass http://backend:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } # API proxy → FastAPI backend location /api/ { proxy_pass http://backend:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } # WebSocket proxy → FastAPI backend location /ws/ { limit_req zone=tv_limit burst=5 nodelay; limit_req_status 429; proxy_pass http://backend:8000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_read_timeout 3600s; } # Vue Router — all other paths serve index.html (SPA fallback) location / { try_files $uri $uri/ /index.html; } }