From bf9266cb9141f63473260f8fd8123b202e9d546c Mon Sep 17 00:00:00 2001 From: kh0pper Date: Wed, 29 Jul 2026 13:31:30 -0500 Subject: [PATCH] Gateway: env-configurable rate-limit skip prefixes for shared-NAT deployments GATEWAY_RATE_LIMIT_SKIP_PREFIXES (comma-separated path prefixes) exempts matching paths from the general per-IP limiter. Needed where many clients sit behind one NAT IP and reach the gateway through an allowlisted reverse proxy: per-IP buckets collapse into one shared bucket and a burst of legitimate clients exhausts it collectively. Default unset; behavior unchanged unless the env var is provided. --- servers/gateway/index.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/servers/gateway/index.js b/servers/gateway/index.js index 716851e1..ea9d5257 100644 --- a/servers/gateway/index.js +++ b/servers/gateway/index.js @@ -325,7 +325,13 @@ app.use(cors({ credentials: true, })); -// Rate limiting — general (skip for --no-auth since it's a local-only bridge) +// Rate limiting — general (skip for --no-auth since it's a local-only bridge). +// GATEWAY_RATE_LIMIT_SKIP_PREFIXES: comma-separated extra path prefixes exempted +// from the general limiter, for deployments where many clients share one NAT IP +// behind an already-allowlisted reverse proxy (per-IP buckets collapse to a +// single shared bucket there). +const rateLimitSkipPrefixes = (process.env.GATEWAY_RATE_LIMIT_SKIP_PREFIXES || "") + .split(",").map((s) => s.trim()).filter(Boolean); if (!noAuth) { app.use(rateLimit({ windowMs: 15 * 60 * 1000, @@ -333,7 +339,8 @@ if (!noAuth) { standardHeaders: true, legacyHeaders: false, message: { error: "Too many requests, please try again later" }, - skip: (req) => req.path.startsWith("/dashboard") || req.path.startsWith("/api/meta-glasses/") || req.path.startsWith("/llm"), + skip: (req) => req.path.startsWith("/dashboard") || req.path.startsWith("/api/meta-glasses/") || req.path.startsWith("/llm") + || rateLimitSkipPrefixes.some((p) => req.path.startsWith(p)), })); }