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