42

I'm able to use limit_req to rate-limit all requests to my server.

However I'd like to remove the rate restriction for certain IP addresses (i.e. whitelist) and use a different rate restriction for certain others (i.e. certain IPs I'd like as low as 1r/s).

I tried using conditionals (e.g. if ( $remote_addr = "1.2.3.4" ) {}) but that seems to work only with rewrite rules, not for rate-limit rules.

2 Answers 2

51

It is really better to avoid using the "if" directive. When the key in limit_req_zone (and limit_conn_zone) is empty the limits are not applied. You can use this in conjunction with the map and geo modules to create a whitelist of IPs where the throttle limits are not applied.

This example shows how to configure a limit for both concurrent requests and request rate from a single IP.

http {
    geo $whitelist {
       default 0;
       # CIDR in the list below are not limited
       1.2.3.0/24 1;
       9.10.11.12/32 1;
       127.0.0.1/32 1;
    }

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

    # The directives below limit concurrent connections from a 
    # non-whitelisted IP address to five

    limit_conn_zone      $limit    zone=connlimit:10m;

    limit_conn           connlimit 5;
    limit_conn_log_level warn;   # logging level when threshold exceeded
    limit_conn_status    503;    # the error code to return

    # The code below limits the number requests from a non-whitelisted IP
    # to one every two seconds with up to 3 requests per IP delayed 
    # until the average time between responses reaches the threshold. 
    # Further requests over and above this limit will result 
    # in an immediate 503 error.

    limit_req_zone       $limit   zone=one:10m  rate=30r/m;

    limit_req            zone=one burst=3;
    limit_req_log_level  warn;
    limit_req_status     503;

The zone directives must be placed at the http level, however the other directives can be placed further down, e.g. at the server or the location level to limit their scope or further tailor the limits.

For futher information refer to the Nginx documentation ngx_http_limit_req_module and ngx_http_limit_conn_module

6
  • What's the difference between these 2 modules?
    – mente
    Jan 15, 2016 at 15:09
  • 1
    As per the comments, the first limits concurrent connections, the second limits the rate of connections Jan 17, 2016 at 23:03
  • Can you explain why you do the mapping in two stages, with geo followed by map, rather than just use geo to set $limit directly? Apr 8, 2016 at 12:04
  • 2
    It seems geo cannot map to a variable so if you specify $binary_remote_addr as a mapping value this will translate to the literal string "$binary_remote_addr", not the value of the variable.
    – ColinM
    May 10, 2016 at 23:15
  • 1
    I would like to add that if the IP in question is already in the zone, you must restart nginx; a reload is not enough.
    – Halfgaar
    Oct 24, 2018 at 13:48
6

You can safely use named locations, such as "@location" in an if() block.

See: http://wiki.nginx.org/IfIsEvil

Something like this should work:

http {

   limit_req_zone $binary_remote_addr zone=delay:10m rate=1r/m;

   server {
      ...

      error_page 410 = @slowdown;

      if( $remote_addr != "1.2.3.4" ) {
         return 410;
      }

      location @slowdown {
         limit_req zone=delay burst 5;
         ...
      }

      location / {
         ...
      }
   }

Fill in "location @slowdown { }" with the same information as "location / { }, such as proxy_pass if you're using nginx as a reverse proxy.

3
  • 1
    I'm not sure I understand the 410 part? Do the client actually see a http 410 status code?
    – svrist
    Feb 27, 2012 at 14:35
  • 3
    Wow, this actually works! Very nifty error_page trick, +1! @svrist, see serverfault.com/a/870170/110020 for complete explanation of how something like this would work, and why.
    – cnst
    Aug 24, 2017 at 0:11
  • @svrist I think the 410 is just one of the status codes the Robert chose to use as a flag to jump to the @slowdown block. Jan 20, 2022 at 20:50

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .