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:
openhands committed 2026-10-01 17:49:41 +02:00
1 parent 4a1211a931
commit f0dcf440a7
2 files changed
+19 -8

No files matched your search

+10 -3
View File
@@ -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