diff --git a/deployment/proxy/nginx-cms.conf b/deployment/proxy/nginx-cms.conf index 04725b77..e21659da 100644 --- a/deployment/proxy/nginx-cms.conf +++ b/deployment/proxy/nginx-cms.conf @@ -192,6 +192,13 @@ server { keepalive_timeout 30s; send_timeout 10s; + # Abuse limits. Applied per server, not per location, so cached assets and + # proxied API routes are all covered by the same budget. nodelay keeps the + # 60-request burst responsive: allowed requests pass immediately, only the + # excess is rejected with 503 instead of being queued. + limit_req zone=cms_req_per_ip burst=60 nodelay; + limit_conn cms_conn_per_ip 30; + # Traefik health-check route herstellen location = /health { access_log off; diff --git a/deployment/proxy/nginx.conf b/deployment/proxy/nginx.conf index 980ccae0..dae1800f 100644 --- a/deployment/proxy/nginx.conf +++ b/deployment/proxy/nginx.conf @@ -44,6 +44,12 @@ http { client_header_buffer_size 1k; large_client_header_buffers 4 8k; + # Rate limiting per client IP. The Nitro client fetches gamedata and icons in + # bursts when booting a room, so the burst is deliberately generous: it caps + # sustained floods without punishing a normal room load. + limit_req_zone $binary_remote_addr zone=cms_req_per_ip:10m rate=30r/s; + limit_conn_zone $binary_remote_addr zone=cms_conn_per_ip:10m; + # Blue/green cutover: ci-deploy.sh writes the active upstream here, and # `proxy_pass http://cms_app` below follows it via graceful nginx -s reload. upstream cms_app {