From f449215bf9bb50d912120db55609b409ee24d479 Mon Sep 17 00:00:00 2001 From: fen Date: Thu, 10 Sep 2026 11:12:53 -0500 Subject: [PATCH] Add rate-limits page (anti-spoofing verification, shared egress IP tradeoff, issue 144) --- rate-limits.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 rate-limits.md diff --git a/rate-limits.md b/rate-limits.md new file mode 100644 index 0000000..8af3171 --- /dev/null +++ b/rate-limits.md @@ -0,0 +1,18 @@ +# Rate Limits + +Palette rate limits paste creation per client IP: burst of 5, then 1 request per second. Exceeding it returns `429` with a `Retry-After` header (see [API](API.md)). + +## Client IP resolution + +`clientIP` takes the **rightmost** entry in `X-Forwarded-For`, i.e. the address added by the trusted reverse proxy in front of Palette. This means spoofed forwarding headers cannot bypass or poison the rate-limit bucket. + +Verified against palette-dev (2026-09-09, issue #144): + +1. Six rapid `POST /api/pastes` from one IP: `201` x5, then `429`. +2. Repeated while sending spoofed headers `X-Forwarded-For: 1.2.3.4`, `X-Real-Ip: 9.9.9.9`, `True-Client-Ip: 3.3.3.3`: all requests still received `429`. No bypass, and the spoofed values did not shift the bucket. + +## Known tradeoff: shared egress IPs + +Because the bucket is keyed solely by client IP, everyone behind one egress IP (office network, VPN, carrier NAT) shares a single burst-5 / 1rps create budget. One heavy user can rate-limit everyone behind that IP. + +This is an inherent property of IP-keyed limiting without accounts, accepted for now. If it becomes a real problem, candidate mitigations include raising the create budget or adding per-session (cookie) buckets on top of the IP bucket.