compscai
Site Performance
· · 0 responses

What is Rate Limiting?

Site Performance

Rate limiting is the practice of capping how many requests a client can make in a given window of time. Go over the cap and the server starts saying no, usually with an HTTP 429, "Too Many Requests." I ran into it from the building side when I added a comment form to this site and realized that nothing stopped one visitor from submitting a thousand comments a minute. Nothing except a rate limit, anyway.

Why it exists

A server without rate limits trusts every client to be reasonable, and the internet does not reward that kind of trust. The problems it protects against fall into a few buckets:

  • Overload. One buggy script or one aggressive scraper can eat the capacity that everyone else was supposed to share. The limit keeps a single client from becoming everyone's problem.
  • Abuse. Spam submissions, scripted signups, scraping. Anything cheap to send in bulk gets sent in bulk.
  • Guessing. This is the security angle. Brute-forcing a login means trying thousands of passwords fast. A limit of a few attempts per minute turns an hours-long attack into a years-long one. When I wrote about my password endpoint bug, one of the fixes on the list was exactly this: rate limit the endpoint so abuse is slow and visible.

The pattern in all three is the same. Rate limiting doesn't make any single request safer, it makes volume expensive, and most attacks only work at volume.

How the counting works

The idea is simple, "count requests per client, refuse past a threshold," but how you count changes how it feels. Two designs come up constantly.

Fixed window is the obvious version: allow 100 requests per minute, reset the counter at the top of each minute. Easy to build, but it has an edge problem. A client can send 100 requests at 11:59:59 and 100 more at 12:00:01. That's 200 requests in two seconds, and the limiter shrugged.

Sliding window fixes that by counting requests in the last 60 seconds from right now, not since the last reset. No seam to exploit, at the cost of tracking a bit more state.

Token bucket is the one I find most elegant. Each client has a bucket that refills at a steady rate, say one token per second up to a max of ten. Each request spends a token; empty bucket means rejected. The nice property is that it allows bursts. A real person clicking around quickly might spend five tokens in two seconds, then nothing for a minute, and the bucket forgives that. A bot hammering nonstop drains the bucket and stays drained. That distinction, brief bursts fine, sustained pressure blocked, matches how legitimate traffic actually behaves.

Clients are usually identified by IP address, or by API key or account when the traffic is authenticated. None of these are perfect. IPs are shared by everyone behind the same network and can be rotated by a determined attacker, which is why rate limiting is a layer, not the whole defense.

Where to enforce it

Short answer: earlier is cheaper. A request rejected at the edge, by Nginx or a CDN, costs almost nothing. One that reaches your application has already paid for a PHP process and maybe a database query before you turn it away.

But the application knows things the edge doesn't, like which user is asking and how sensitive the endpoint is. So in practice you layer it: a broad, generous limit at the edge to shed floods, and tighter, targeted limits in the app on the endpoints that matter, like login, signup, and anything that writes. The comment form on this site gets a far stricter budget than reading posts does, because the cost of getting each one wrong is nowhere near the same.

The takeaway

Rate limiting is one of those defenses that's invisible when it's working. Legitimate users never hit it, and that's the sign it's tuned right. It won't stop a clever attack on its own, but it takes away the thing most attacks depend on, which is the ability to try things fast and free. Cheap to add, boring to run, and you mostly notice it by the mess that never showed up.

0 responses