Rate Limiting
In short: A technique for limiting how often a user/an IP address may make requests within a period of time — protects against abuse (e.g. brute-force logins, spam) and overload.
In more detail: Often implemented via a fast cache such as Redis (a counter per user/IP with an expiry time). If rate limiting is applied to EVERY request (including harmless read-only pages), each page view costs an additional network round trip — noticeable in the loading time.
Our context: At first it ran globally on every request at Emzett; it was then restricted so that pure GET/HEAD read-only pages without forms are exempt (login, checkout, chat and admin remain fully protected), to save loading time and Redis load.
In Depth
The most common rate-limiting method is the “sliding window”: instead of simply counting “how many requests since the last full minute”, a sliding time window is considered (e.g. “how many requests in the last 60 seconds, counted from now”) — this prevents someone from using the full limit just before and just after a minute boundary and thereby effectively getting twice as many requests through as allowed. Libraries such as @upstash/ratelimit implement this serverlessly via Redis, without having to write the time-window logic yourself:
const ratelimit = new Ratelimit({
redis,
limiter: Ratelimit.slidingWindow(100, "1 m"), // 100 requests per minute
});
const { success } = await ratelimit.limit(`edge:${ip}`);
if (!success) {
return new Response("Too many requests", { status: 429 });
}What matters is WHICH identifier is used for counting: a plain IP address is simple, but problematic behind shared NATs/corporate networks (many real users share one visible IP) and easy to bypass via IP rotation; a combination of IP + user account (where available) is more precise. In globally distributed edge environments (such as Vercel Edge Functions), the counter store itself must also be globally consistent — a local in-memory counter per server instance would effectively multiply the limit when several instances run in parallel, which is why a central store such as Redis is needed.
See also: Upstash Redis