# Limit\_req is not working with dynamically extracted user address

**URL:** <https://www.ruby-forum.com/t/limit-req-is-not-working-with-dynamically-extracted-user-address/244302>\
**Category:** NGINX\
**Created:** [March 18, 2016, 2:46pm UTC](https://www.ruby-forum.com/t/limit-req-is-not-working-with-dynamically-extracted-user-address/244302 "2016-03-18T14:46:36Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![Rafa\_F](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/958977/32.png) [@Rafa\_F](https://www.ruby-forum.com/u/Rafa_F)\
**Post date:** [March 18, 2016, 2:46pm UTC](https://www.ruby-forum.com/t/limit-req-is-not-working-with-dynamically-extracted-user-address/244302/1 "2016-03-18T14:46:36Z")

</div>

Hi, if our HTTP block looks like below where we find IP from  
X-Forwarded-For  
using perl module it looks like zone and limit\_req are not using correct  
variable $user\_real\_ip or it is reset right after logging.

The nginx\_access\_log shows the first logged field correctly as  
$user\_real\_ip

- which is first element of comma separated IP addresses in  
X-Forwarded-For  
header from different hopes. User request comes thorough several  
different  
CDN and DDOS services.

But when limit\_req kicks in it would take for some reason last element  
in  
same header X-Forwarded-For which of course not our intention.

Can you help understand why and what we are doing wrong?

Also to note we are using 1.6.2 version. We tried to upgrade it to 1.8  
version but it didn’t help.

Thanks,

Sergey

## Example of nginx\_access\_log and following nginx\_error\_log

555.182.61.171 - - - [www.my.com](http://www.my.com) - [17/Mar/2016:17:44:15 -0400] “GET  
/our/api/here HTTP/1.1” 503 270 0.000 “-” “Java/1.8.0\_51”  
“555.182.61.171,  
333.101.98.188” -

## 2016/03/17 17:44:15 [error] 19382#0: \*8 limiting requests, excess: 5.613 by zone “one”, client: 333.101.98.188, server: [www.my.com](http://www.my.com), request: “GET /our/api/here HTTP/1.1”, host: “[www.my.com](http://www.my.com)”

## HTTP block

http {  
include /etc/nginx/mime.types;  
include /etc/nginx/proxy.conf;  
index index.html index.htm index.php;

…  
perl\_set $user\_real\_ip ’

```
    sub {
        my $r = shift;
        my $str = $r->header_in("X-Forwarded-For");
        my @fields = split /,/, $str;
        my $real_ip = $fields[0];
        return $real_ip;
    }

';

log_format main '$user_real_ip - $remote_addr - $remote_user - 

```

$host -  
[$time\_local] “$request” ’  
'$status $body\_bytes\_sent $request\_time  
“$http\_referer” ’  
‘“$http\_user\_agent” “$http\_x\_forwarded\_for”  
$http\_cf\_ray’;

```
sendfile on;

keepalive_timeout 20;

limit_req_zone $user_real_ip zone=one:50m rate=1r/s;

include /etc/nginx/conf.d/*.conf;

```

}

www.conf location

---

<div class="post-metadata">

**Author:** ![malish8632](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/m/74df32/32.png) [@malish8632](https://www.ruby-forum.com/u/malish8632)\
**Post date:** [March 18, 2016, 3:22pm UTC](https://www.ruby-forum.com/t/limit-req-is-not-working-with-dynamically-extracted-user-address/244302/2 "2016-03-18T15:22:14Z")

</div>

Hello!

On Fri, Mar 18, 2016 at 09:45:58AM -0400, malish8632 wrote:

> same header X-Forwarded-For which of course not our intention.
> 
> Can you help understand why and what we are doing wrong?

How did you found that limit\_req uses a wrong element?

Note:

> 2016/03/17 17:44:15 [error] 19382#0: \*8 limiting requests, excess: 5.613 by  
> zone “one”, client: 333.101.98.188, server: [www.my.com](http://www.my.com), request: “GET  
> /our/api/here HTTP/1.1”, host: “[www.my.com](http://www.my.com)”

The “client: 333.101.98.188” is the client address as automatically  
logged by nginx for all error messages (also known as $remote\_addr).  
It’s not related to the limit string used by limit\_req.

[…]

> ```
> ';
> 
> ```

Note well: this can be easily tricked by clients using a fake  
address in initial X-Forwarded-For header. You may want to use  
the realip module instead, it allows to trust only known proxies,  
see here:

> **[Module ngx\_http\_realip\_module](http://nginx.org/en/docs/http/ngx_http_realip_module.html)**

–  
Maxim D.

> **[nginx](http://nginx.org/)**

---

<div class="post-metadata">

**Author:** ![malish8632](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/m/74df32/32.png) [@malish8632](https://www.ruby-forum.com/u/malish8632)\
**Post date:** [March 18, 2016, 3:49pm UTC](https://www.ruby-forum.com/t/limit-req-is-not-working-with-dynamically-extracted-user-address/244302/3 "2016-03-18T15:49:07Z")

</div>

Hi Maxim,  
thank you for quick response.

> How did you found that limit\_req uses a wrong element?

We don’t know if this is limit\_req - in reality we were just looking  
into  
logs and I guess that’s what confused us. We observed those IPs and  
rolled  
back the changes as we assumed that all requests from CDN or DDOS  
Service  
were blocked.

The only way to I guess to verify that our current schema works is to  
use  
some arbitrary IP and see if our requests are blocked rather then CDN  
service IP is blocked.

We’ve looked into

> **[Module ngx\_http\_realip\_module](http://nginx.org/en/docs/http/ngx_http_realip_module.html)**

and not sure if it is going to work.

As you saw one of the examples we have other services in front of us.  
There are 2 cases:  
User → DDOS Service → Our NGINX - X-Forwarded-For ex:  
555.182.61.171, 333.101.98.188  
User → CDN → DDOS Service → Our NGINX - X-Forwarded-For ex:  
555.182.61.171, 444.1.3.56, 555.12.34.567, 333.101.98.188

Will realip module able to identify real IP of end user?  
Should we set CIDR of both DDOS Service and CDN Service as real ip  
tables:

set\_real\_ip\_from 192.168.1.0/24;  
set\_real\_ip\_from 192.168.2.1;  
set\_real\_ip\_from 2001:0db8::/32;

Thanks again.

Sergey

Posted at Nginx Forum:  
[https://forum.nginx.org/read.php?2,265461,265491#msg-265491](https://forum.nginx.org/read.php?2,265461,265491#msg-265491)

---

<div class="post-metadata">

**Author:** ![malish8632](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/m/74df32/32.png) [@malish8632](https://www.ruby-forum.com/u/malish8632)\
**Post date:** [March 18, 2016, 4:10pm UTC](https://www.ruby-forum.com/t/limit-req-is-not-working-with-dynamically-extracted-user-address/244302/4 "2016-03-18T16:10:43Z")

</div>

Hello!

On Fri, Mar 18, 2016 at 10:48:56AM -0400, malish8632 wrote:

> > How did you found that limit\_req uses a wrong element?
> 
> We don’t know if this is limit\_req - in reality we were just looking into  
> logs and I guess that’s what confused us. We observed those IPs and rolled  
> back the changes as we assumed that all requests from CDN or DDOS Service  
> were blocked.
> 
> The only way to I guess to verify that our current schema works is to use  
> some arbitrary IP and see if our requests are blocked rather then CDN  
> service IP is blocked.

Ok, so no problem here.

> Will realip module able to identify real IP of end user?  
> Should we set CIDR of both DDOS Service and CDN Service as real ip tables:
> 
> set\_real\_ip\_from 192.168.1.0/24;  
> set\_real\_ip\_from 192.168.2.1;  
> set\_real\_ip\_from 2001:0db8::/32;

The realip module uses last non-trusted address from the header  
(assuming real\_ip\_recursive is set). So you have to instruct it  
to trust addresses of your DDoS mitigation service and CDN, e.g.:

set\_real\_ip\_from ;  
set\_real\_ip\_from ;  
real\_ip\_header X-Forwarded-For;  
real\_ip\_recursive on;

–  
Maxim D.

> **[nginx](http://nginx.org/)**
