Behaviour of map changes when using it's result in limit_req/limit_req_zone

Felix Gläske soupdiver at
Fri Oct 11 08:30:14 UTC 2019

I try to achieve the following:
I want to throttle requests based on upstream response codes.
If a user (repeatedly) fails to login, and therefor the upstream server responds with 403, the requests should be throttled. Not for 2xx or other response codes. Only 403.

Online I found a solution using a `map` and `limit_req_zone`/`limit_req`.

So far so good, I tried out the things separately and the mapping as well as the throttling works.
But when putting them together things get weird, at least as far as I understand so far.

My config:
  error_log  /dev/stdout info;

  daemon off;

  events {
    worker_connections  4096;

  http {
    map $status $bad_guy {
      ~^[2] 1;
      ~^[4] $remote_addr;
      default "default";

    map $status $wup {
      ~^[2] 1;
      ~^[4] $remote_addr;
      default "default";

    log_format   main '$remote_addr - $status ->$wup<- ->$bad_guy<- ';
    access_log   /dev/stdout  main;

    limit_req_zone $bad_guy zone=by_status:10m rate=60r/m;

    server {
      listen       8000;

      location / {
        limit_req zone=by_status burst=2;

I have two identical maps. The only difference is that $bad_guy is used with "limit_req_zone".
I would assume that in the log statement the values for $wup and $bad_guy are the same since they are based on maps with the same rules.
However, this is _not_ the case:

Scenario 1: Upstream reports 403
Log: - 403 -><- ->default<-

As we can see, only $wup has the expected value and $bad_guy is default for some reason.

Scenario 2: Upstream reports 226
Log: - 226 ->1<- ->default<-

The same behaviour as before.

Let's change the configuration a little bit and comment out the line "limit_req zone=by_status burst=2;"

Scenario 3: Upstream reports 403
Log: - 403 -><- -><-

Ok, now the values seem all right.

Scenario 4: Upstream reports 226
Log: - 226 ->1<- ->1<-
Ok, good values again.

Final question: Why does using "limit_req" change the behaviour/outcome of the "map"? Maybe I'm not aware enough of the internals but this seems weird to me and also blocks me from reaching my goal to throttle failed login attempts.

I hope someone can shine some light on that.

More information about the nginx mailing list