Nginx Rate-limiting with a Whitelist

I recently needed to deploy a rate-limiting solution for a site where certain IP addresses would be white-listed and not subjected to the rate limits. Google found me an example in a blog that turned out not to work, though I’ve no idea whether that’s because the example is incomplete or because nginx has changed since it was written. Here’s the solution I eventually came up with.

First, my servers are behind a proxy, so I needed to pull out the originating IP address and to tell nginx that it should never use whatever IP address the connection actually came from:

real_ip_header X-Forwarded-For;
set_real_ip_from 0.0.0.0/0;

Then, using the HttpGeo module, I set up a match list for addresses that should be rate-limited. This sets the variable $limited to 1 (meaning “rate-limit this address”) or 0 (“don’t rate-limit this address”). The default is 1 and I list addresses I don’t want limiting with a zero value. Address ranges are in CIDR notation:

geo $limited {
  default 1;
  10.0.0.0/24 0;
  192.168.42.42/32 0;
}

Now I set another variable, $limit to the IP real IP address of the connection or to the empty string depending on the value of $limited. If the connection is to be rate-limited then the (binary format) IP address will be used; if not, the empty string is used:

map $limited $limit {
  1        $binary_remote_addr;
  0        "";
}

And here’s the limit zone, keyed using the value of $limit:

limit_req_zone $limit zone=limited:10m rate=5r/m;

When $limit is an empty string, the limit will not be applied. Finally, apply the limit in the location stanza:

location / {
    limit_req zone=limited burst=5;
}

In testing this approach appears to work so far. The site in question goes live next week, so I’ll find out then how it copes in the real world.

This entry was posted in Computing and tagged , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *