fix(proxy): split rate limiting into page and static zones
The single server-scope limit_req (30r/s) treated a page load and a room load as the same thing. Loading a Nitro room fires several hundred gamedata icons in one burst, which that zone answered with 503s, so icons showed up late in the client. Add a separate static zone (1000r/s, burst 1000, nodelay) for the gamedata and client asset locations, and apply the page-rate zone explicitly on the main route instead of at server scope. Connection limit stays server-wide. Measured: 900 icon requests in burst now all return 200, while 200 parallel requests on / are still rejected.
This commit is contained in:
1 parent
4a1211a931
commit
f0dcf440a7
2 files changed
+19
-8
No files matched your search
@@ -192,11 +192,10 @@ 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;
|
||||
# Abuse limits. Deliberately NOT set at server scope: a room load and a page
|
||||
# load are not the same request profile, so each location picks its own zone.
|
||||
# Locations without an explicit limit_req inherit nothing and are unlimited —
|
||||
# the page/API routes below carry the budget instead.
|
||||
limit_conn cms_conn_per_ip 30;
|
||||
|
||||
# Traefik health-check route herstellen
|
||||
@@ -210,6 +209,7 @@ server {
|
||||
location ^~ /client/ {
|
||||
alias /var/www/Octane/dist/;
|
||||
try_files $uri $uri/ =404;
|
||||
limit_req zone=cms_static_per_ip burst=1000 nodelay;
|
||||
|
||||
location ~* \.(js|json|css|html|wasm|ttf|woff|woff2|gif|webp|png|jpg|jpeg|svg|dat)$ {
|
||||
add_header Cache-Control "public, max-age=2592000";
|
||||
@@ -223,6 +223,7 @@ server {
|
||||
location ^~ /nitro-client/ {
|
||||
alias /var/www/Octane/dist/;
|
||||
try_files $uri $uri/ =404;
|
||||
limit_req zone=cms_static_per_ip burst=1000 nodelay;
|
||||
|
||||
location ~* \.(js|json|css|html|wasm|ttf|woff|woff2|gif|webp|png|jpg|jpeg|svg|dat)$ {
|
||||
add_header Cache-Control "public, max-age=2592000";
|
||||
@@ -265,6 +266,7 @@ server {
|
||||
add_header Cache-Control "public, max-age=300, must-revalidate";
|
||||
access_log off;
|
||||
add_header Cache-Tag "cms-gamedata";
|
||||
limit_req zone=cms_static_per_ip burst=1000 nodelay;
|
||||
|
||||
add_header Access-Control-Allow-Origin $http_origin always;
|
||||
add_header Access-Control-Allow-Methods "GET, OPTIONS" always;
|
||||
@@ -280,6 +282,7 @@ server {
|
||||
add_header Cache-Control "public, max-age=3600, must-revalidate";
|
||||
access_log off;
|
||||
add_header Cache-Tag "cms-gamedata";
|
||||
limit_req zone=cms_static_per_ip burst=1000 nodelay;
|
||||
|
||||
add_header Access-Control-Allow-Origin $http_origin always;
|
||||
add_header Access-Control-Allow-Methods "GET, OPTIONS" always;
|
||||
@@ -454,6 +457,7 @@ server {
|
||||
# ─── Hoofd-routering ───
|
||||
location / {
|
||||
proxy_pass http://cms_app;
|
||||
limit_req zone=cms_req_per_ip burst=60 nodelay;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
|
||||
@@ -44,10 +44,17 @@ 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.
|
||||
# Rate limiting per client IP.
|
||||
#
|
||||
# Two zones, because a room load and a page load are not the same thing.
|
||||
# Loading a Nitro room fires several hundred gamedata icons in one burst;
|
||||
# at the page rate that produced 503s on real players. Static assets
|
||||
# therefore get their own, much higher allowance. These are small immutable
|
||||
# files, so a request rate is not what protects them anyway — nginx already
|
||||
# serves them with must-revalidate, and the CMS upstream stays behind
|
||||
# cms_req_per_ip for the expensive routes.
|
||||
limit_req_zone $binary_remote_addr zone=cms_req_per_ip:10m rate=30r/s;
|
||||
limit_req_zone $binary_remote_addr zone=cms_static_per_ip:10m rate=1000r/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
|
||||
|
||||
Reference in new issue
Block a user