Nginx – Rate Limiting

Everything Linux, A.I, IT News, DataOps, Open Source and more delivered right to you.
Subscribe
"The best Linux newsletter on the web"

In this article, we’re going to see how to apply rate limiting to our websites using Nginx. Rate limiting can be an effective way to prevent denial of service attacks as well as throttle brute force attempts for pages like login forms.

Defining a rate limit zone

The first thing we need to do is, within the http context of our nginx configuration, define the limit_req_zone directive. The first argument it takes is on what basis the rate limits can be applied. Some useful values for
the first argument can be:

  • $remote_addr – The IP address of the remote client in string format
  • $binary_remote_addr – The IP address of the remote client in binary
    (big-endian integer) format
  • $request_uri – The URI used to access the resource

The next argument is, like with caches in my previous articles, defining a zone. This is done with the zone argument; e.g., zone=RATE_LIMIT:10m, where 10m is the size for storing the states. And the last argument is the rate such as rate=10r/s. Here the rate is 10 requests per second above which the rate limiting will be applied.

A sample directive would be:

limit_req_zone $request_uri zone=STATES:10m rate=100r/s;

Using the zone in location blocks

Now we can use the STATES zone in our location blocks with the limit_req directive. For example:

location /login.php {
    limit_req STATES;
}

Now the login.php resource can be requested by 100 different clients (regardless of the IP) within one second. If a 101th request is made within the same second, then it will be rejected.

To test it out, I’ve installed a tool called siege on my client system. You can download it here.

Basic rate limiting

I have given the following flags to siege:

  • -c: number of concurrent requests
  • -r: number of times the requests are to be repeated

Here, siege sends 5 requests to the server, and we see that the first one is served but the additional 4 are rejected since all of the 5 requests occurred within one second.

If this is not what we want then the limit_req directive can be supplied a few additional arguments. Note that these arguments can also be supplied globally within the limit_req_zone directive.

burst

This specifies the number of requests to be fulfilled along with the value of rate. In other words, if requests exceed the limit specified by rate, then they won’t be immediately rejected. Instead, they’ll be kept in a queue and responded to according to the rate limit. If we use option with a rate=1r/s, like so:

   location /login.php {
       limit_req STATES burst=5;
   }

Note: I’m using the rate of 1r/s here.

Now nginx will keep 5 requests which exceed the 1r/s limit, and they’ll be responded to complying (i.e. with the required delay) that limit. So in total nginx will serve 6 requests in total per second.

Rate limit with burst

Here, we see that the 7th request is rejected whereas the first one is served immediately, the other 5 suffering some delay.

nodelay

This is applicable to an already defined burst limit. Basically, it allows us to serve the burst requests as quickly as possible but it will allocate slots for the responded requests. The slots won’t be freed until the
configured rate limit is over.

Rate limit with burst and nodelay


In this screenshot, the first 6 requests were served immediately. I re-ran the command after some time and we see that the first request was freed as one slot must have been freed.

That’s basically all we need for rate-limiting. You can read more from this article. Thank you for reading.

Everything Linux, A.I, IT News, DataOps, Open Source and more delivered right to you.
Subscribe
"The best Linux newsletter on the web"
Pratik Devkota
Pratik Devkota
Software engineering student interested in UNIX (GNU/Linux, *BSD, etc.), security as well as free and open-source software.

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Latest articles

Join us on Facebook