Rate Limiting with Nginx Behind AWS ELB

By | 2014/11/20

To prevent abuse or limit the number of requests to a site or url, nginx works great. However behind an AWS ELB, one small change is needed to make this work. Check it out!


The nginx docs for reference are here:

http://nginx.org/en/docs/http/ngx_http_limit_req_module.html

Instead of using $binary_remote_addr as in those docs, use $http_x_forwarded_for when behind an ELB.

limit_req_zone $http_x_forwarded_for zone=search:10m rate=5r/s;


Below is an example snipit to limit requests on the url /search.

For the url I want to rate limit (/search in this example) I copied my entire location block and added the limit_req line. This way persons hitting /search will be limited and temporarily see an HTTP 503 until their attempts slow down or stop.


limit_req_zone $http_x_forwarded_for zone=search:10m rate=5r/s;


server {

...

    location /search {
    
        limit_req zone=search burst=10;

        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $my_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Protocol https;
        proxy_read_timeout 90;
    }

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $my_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Protocol https;
        proxy_read_timeout 90;
    }

...

}


When rate limited, the nginx error logs will produce output similar to the following:

2014/11/20 17:28:46 [error] 30347#0: *55 limiting requests, excess: 5.658 by zone "search", client: 10.170.2.23, server: www.example.com, request: "GET /search/results/?keyword= HTTP/1.1", host: "test-site-www-2014112001.example.com", referrer: "https://test-site-www-2014112001.example.com/search/results/?keyword=" 
2014/11/20 17:28:46 [error] 30347#0: *55 limiting requests, excess: 5.273 by zone "search", client: 10.170.2.23, server: www.example.com, request: "GET /search/results/?keyword= HTTP/1.1", host: "test-site-www-2014112001.example.com", referrer: "https://test-site-www-2014112001.example.com/search/results/?keyword=" 
2014/11/20 17:28:47 [error] 30347#0: *55 limiting requests, excess: 5.508 by zone "search", client: 10.170.2.23, server: www.example.com, request: "GET /search/results/?keyword= HTTP/1.1", host: "test-site-www-2014112001.example.com", referrer: "https://test-site-www-2014112001.example.com/search/results/?keyword=" 
2014/11/20 17:28:47 [error] 30347#0: *55 limiting requests, excess: 5.200 by zone "search", client: 10.170.2.23, server: www.example.com, request: "GET /search/results/?keyword= HTTP/1.1", host: "test-site-www-2014112001.example.com", referrer: "https://test-site-www-2014112001.example.com/search/results/?keyword=" 
2014/11/20 17:28:48 [error] 30347#0: *55 limiting requests, excess: 5.567 by zone "search", client: 10.170.2.23, server: www.example.com, request: "GET /search/results/?keyword= HTTP/1.1", host: "test-site-www-2014112001.example.com", referrer: "https://test-site-www-2014112001.example.com/search/results/?keyword=" 


The default nginx 503 page as seen by the user when rate limited appears as below. You can of course style a custom 503 page like many sites do.

nginx-rate-limit