If statement with $limit_req_status under location block with proxy_pass not working

Maxim Dounin mdounin at mdounin.ru
Fri Jun 3 21:05:42 UTC 2022


Hello!

On Thu, Jun 02, 2022 at 05:44:26PM -0400, acidiclight wrote:

> I'm trying to customize my response to rate-limited requests by keeping
> limit_req_dry_run on, and using an if statement depending on the value of
> $limit_req_status:
> 
> This works as expected:
> 
> limit_req_zone $binary_remote_addr zone=one:1m rate=2r/m;
> 
> server {
>     
>     listen       80;
>     server_name  localhost;
> 
>     location / {
>         # set rate limiting for this location
>         limit_req zone=one burst=10 nodelay;
>         limit_req_dry_run on;
> 
>         add_header X-my-var "$myvar" always;
> 
>         if ($limit_req_status = "REJECTED_DRY_RUN") {
>             add_header X-custom-header "rejected" always;
>             return 400 'rejected';
>         }
> 
>         root   /usr/share/nginx/html;
>         index index.html;
>     }
> 
> }
> 
> But once I replace root and index with a proxy_pass, the whole thing stops
> working:
> 
> limit_req_zone $binary_remote_addr zone=one:1m rate=2r/m;
> 
> server {
>     resolver 8.8.8.8;
>     listen       80;
>     server_name  localhost;
> 
>     location / {
>         set $myupstream "myurl.com";
>         
>         # set rate limiting for this location
>         limit_req zone=one burst=10 nodelay;
>         limit_req_dry_run on;
> 
>         add_header X-limit-req-status "$limit_req_status" always;
> 
>         if ($limit_req_status = "REJECTED_DRY_RUN") {
>             add_header X-custom-header "rejected" always;
>             return 400 'rejected';
>         }
> 
>         proxy_pass http://$myupstream;
>     }
> 
> }
> 
> I added $limit_req_status to my log_format and can confirm that the value of
> $limit_req_status does get set to "REJECTED_DRY_RUN". I also see the header
> "X-limit-req-status" from the request set to "REJECTED_DRY_RUN".
> 
> I'm assuming the issue is the way Nginx evaluates if statements that have
> unset variables at the beginning of the request? If so, any pointers on how
> to get this working? Thank you!

The rewrite module directives, notably "if", "set", and "return" 
in your configuration, are evaluated while looking for a 
configuration to process a request[1].  In contrast, limit_req limits 
are evaluated before processing a request in a given 
configuration.  As such, both the above configurations are not 
expected to work.

First one likely appear to work for you because you are testing it 
with requests such as "/", involving an internal redirect to the 
index file[2], so "if" acts on the limit_req results before the 
internal redirect.  Testing with direct link to a file will reveal 
the it doesn't work too.

Proper solution to get things working would be to actually switch 
off limit_req_dry_run.  If you need to redefine response code 
and/or add custom headers, consider using limit_req_status and/or 
error_page instead.  For example:

    location / {
        limit_req zone=one burst=10 nodelay;
        error_page 503 = /rejected;
        proxy_pass http://...;
    }

    location = /rejected {
        add_header X-custom-header "rejected" always;
        return 400 rejected;
    }

Hope this helps.

[1] http://nginx.org/en/docs/http/ngx_http_rewrite_module.html
[2] http://nginx.org/en/docs/http/request_processing.html#simple_php_site_configuration

-- 
Maxim Dounin
http://mdounin.ru/



More information about the nginx mailing list