NGINX: limit_req – limiting requests per IP

By | 08/09/2019
 

The next task is to set a limit per one IP to our backend’s API:

  1. add requests limiter on the /user/oauth/refresh_token URI
  2. set limit to 5 requests/minute from one IP
  3. if the limit is reached – return 429 Too Many Requests response

Will use NGINX ngx_http_limit_req_module.

limit_req_zone

To make limiting works firs need to create a memory region to store IP addresses and requests from each.

Add it to the nginx.conf, http {} block:

...
limit_req_zone $binary_remote_addr zone=backend_oauth:10m rate=5r/m;
...

Here:

  • $binary_remote_addr – a variable with a visitor’s IP
  • zone=backend_oauth:10m – memory  zone named backend_oauth and 10 MB size ($binary_remote_addr size can be found in the ngx_stream_limit_conn_module documentation)
  • rate=5r/m – 5 requests/minute limit

Also:

limit_req

Next, in a virtual host’s config add a new location where you want to apply limit and add limit_req:

...
    location ~ /v3/user/oauth/refresh_token/ {
        limit_req zone=backend_oauth nodelay;
        limit_req_status 429;

        fastcgi_pass 127.0.0.1:9014;
        include fastcgi_params;
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        fastcgi_param DOCUMENT_ROOT $root_path;
        fastcgi_param  SCRIPT_FILENAME  $root_path$fastcgi_script_name;
        fastcgi_intercept_errors on;
    }
...

Check syntax, re-read configs:

root@bttrm-stage-app-2:/home/admin# nginx -t && systemctl reload nginx
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Apache benchmark

Test it:

ab -n 100 -c 10 "https://stage.example.com/v3/user/oauth/refresh_token/"

Check logs:

root@bttrm-stage-app-2:/home/admin# tail -f /var/log/nginx/stage.example.com-access.log | grep 194.***.***.26
194.***.***.26 - - [08/Aug/2019:14:26:29 +0300] "GET /v3/user/oauth/refresh_token/ HTTP/1.1" 404 27 "-" "ApacheBench/2.3"
194.***.***.26 - - [08/Aug/2019:14:26:29 +0300] "GET /v3/user/oauth/refresh_token/ HTTP/1.1" 429 162 "-" "ApacheBench/2.3"
194.***.***.26 - - [08/Aug/2019:14:26:29 +0300] "GET /v3/user/oauth/refresh_token/ HTTP/1.1" 429 162 "-" "ApacheBench/2.3"
194.***.***.26 - - [08/Aug/2019:14:26:29 +0300] "GET /v3/user/oauth/refresh_token/ HTTP/1.1" 429 162 "-" "ApacheBench/2.3"
194.***.***.26 - - [08/Aug/2019:14:26:29 +0300] "GET /v3/user/oauth/refresh_token/ HTTP/1.1" 429 162 "-" "ApacheBench/2.3"
...

429 was returned, all works.

NGINX, limit_req and AWS ALB

Found a possible issue when using AWS ALB (but this was posted at 2014): with the $binary_remote_addr limits may not works, if so – try to use $http_x_forwarded_for instead.

Although in my current case everything was fine, maybe because of the я set_real_ip_from (see the NGINX: реальный IP в логах при работе за AWS Load Balancer post, Rus).

Links