I want to use nginx to rate limit access to my API end-points only when the origin server response code is 401(Unauthorized). I have searched everywhere and couldn’t find anything related to my issue so please let me know if this is doable with nginx. Thanks in advance.

These answers are provided by our Community. If you find them useful, show some love by clicking the heart. If you run into issues leave a comment, or add your own answer to help others.

×
2 answers

This isn’t something I’ve attempted and doing some searching shows that you’ve been looking around for an answer for this quite a bit lately :)

You can have nginx intercept upstream error messages, so with that turned on you could rate limit based on a 401 response in the same way as if that error was local to nginx.

This shows an example of the option to intercept upstream error codes.

  • Hello Ryan,

    I tried this and it doesn’t work. It seems rate limiting happens early in request pipeline so intercepting errors and having a limit_req inside that location block does nothing. Here’s my source:

    limit_req_zone $limit zone=noauth:10m rate=1r/m;
    
    location /api {
        proxy_intercept_errors on;
        ...
        error_page 401=@noauth;
    }
    
    location @noauth {
        limit_req zone=noauth;
    
        return 401;
    }
    
    

    Let me know if I am doing anything wrong and thank you for your reply.

    • Hello,

      The order is important. If you work with HTTP status codes from an upstream service, you should put the rate limiting directives after the “proxy_<something>” directives.

      This should work for example:

      map $status $bad_guy {
          ~^[45]  $binary_remote_addr;
          default "";
      }
      ...
      ...
      ...
      limit_req_zone $bad_guy zone=by_status:10m rate=30r/m;
      ...
      ...
      ...
      server {
          location /by-uri/myburst_401 {
              proxy_pass http://example.com/;
              limit_req zone=by_status burst=5 nodelay;
          }
      }
      ...
      
      

      Please note how the limitreq zone is after the proxypass directive. This is very important otherwise the mapping won’t work. That is because the $status variable is still empty before calling the upstream.

      • Hello,

        Unfortunately, my previous answer doesn’t work, sorry about that.

        Configuring rate limiting by http status is quite tricky because the $status variable is empty when declaring the limitreqzone. The $status is only known after nginx has processed the request. For example after a proxy_pass directive.

        The closest I could get to achieve rate limiting by status is doing the following:

        ...
        ...
        ...
        limit_req_zone $binary_remote_addr zone=api:10m rate=5r/s;
        ...
        ...
        ...
        server {
            location /mylocation {
                proxy_intercept_errors on;
                proxy_pass http://example.org;
                error_page 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 421 422 423 424 426 428 429 431 451 500 501 502 503 504 505 506 507 508 510 511 @custom_error;
            }
        
            location @custom_error {
                limit_req zone=api burst=5 nodelay;
                return <some_error_code>;
            }
        
        }
        ...
        
        edited by MattIPv4
Submit an Answer