43

I have a webapp under NGinx and another frontal load balancer, something like below (x.x.x.x = IP address):

Client(a.a.a.a) -> LB (b.b.b.b) -> NGX (c.c.c.c) -> WEBAPP (d.d.d.d)

Here is a snippet of my NGinx configuration:

location / {
    proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header  X-Real-IP       $remote_addr;
    real_ip_header    X-Forwarded-For;
    set_real_ip_from  b.b.b.b;
    real_ip_recursive on;
}
  1. The load balancer add X-Forwarded-For field with client IP
    X-Forwarded-For = a.a.a.a
  2. NGinx search for client real IP in X-Forwarded-For header by omiting LB IP (b.b.b.b) and change $remote_addr from b.b.b.b to a.a.a.a so proxy_set_header X-Real-IP $remote_addr become true (OK that's what I want !)
    BUT, NGinx also complete X-Forwarded-For header with a.a.a.a IP instead of b.b.b.b
  3. WEBAPP receive the following headers:
    X-Forwarded-For = a.a.a.a, a.a.a.a
    X-Real-IP = a.a.a.a
    -> X-Forwarded-For should be a.a.a.a, b.b.b.b

What I need is the ability to set first proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for and then search for real IP and replace $remote_addr value.

Any one can help me to solve this problem ?

pierrefevrier
  • 1,570
  • 4
  • 22
  • 33
  • seems like your LB is not adding itself into the header. – codewandler Apr 01 '15 at 13:07
  • 3
    I don't think so, NGinx is adding itself as `a.a.a.a` (see step 3, with twice `a.a.a.a` in `X-Forwarded-For` header) instead of `b.b.b.b` because of `real_ip_header` execution before `proxy_set_header` – pierrefevrier Apr 01 '15 at 14:11
  • [Related NGinx ticket](https://trac.nginx.org/nginx/ticket/2127) – William Rust Nov 01 '22 at 15:55
  • Isn't the fact that `$proxy_add_x_forwarded_for` doesn't work for you is a hint that you're doing it wrong? Consider the [following setup](https://gist.github.com/x-yuri/30691c2170d4c226a0b9a05dfe587e7b). Yeah, this way X-Forwarded-For is nothing but duplicates, but every node should trust or not trust only its immediate client (should not know about the whole chain). And every node has the real (to the extent it can be trusted) client IP in `$remote_addr`. Why would you want it the other way? – x-yuri May 10 '23 at 06:36

3 Answers3

29

The $proxy_add_x_forwarded_for is equal to $http_x_forwarded_for,$remote_addr, and the $remote_addr variable will be changed when http_realip_module is used. So you will not get the last proxy addr in that header. Changing the order of directives won't have an effect because nginx configuration is declarative.

When http_realip_module is used, the $realip_remote_addr variable (nginx >= 1.9.7) can be used as the original $remote_addr. So you can set your X-Forwarded-For header like this:

proxy_set_header X-Forwarded-For "$http_x_forwarded_for, $realip_remote_addr";
x-yuri
  • 16,722
  • 15
  • 114
  • 161
Y. King
  • 301
  • 3
  • 4
  • 3
    Please give more context for your answer, why might your answer work? An answer should be clear, complete, and answer the question – jhhoff02 Jun 29 '17 at 12:54
  • Amazing, this should probably be the correct answer. – basicdays Mar 08 '18 at 07:47
  • Solution workd, but isnt this a bad implementation from nginx? They should atleast tell it explicitly with the module description or better provide another variable which should have stored the client info of the nginx. – coderelliot Jan 25 '21 at 07:05
4

Same problem here. It's annoying, and I'm not actually sure if this is feature or bug:)

I know it's not a solution, but I've removed real_ip_header, and simply use X-Forwarded-For first ipaddress to get client's ip address wherever I need it (eg. logs).

faja
  • 121
  • 2
  • 3
  • It's a workaround :) You have to parse `X-Forwarded-For` to get the real IP, but it works. I don't know if NGinx team want the feature or if it is a bug... – pierrefevrier Feb 18 '16 at 09:11
  • If real_ip_header was removed, the $remote_addr does not get the actual client IP from X-Forwarded-For. – Roc King Mar 05 '20 at 02:59
4

I recently came across the same "problem" and came to the conclusion that this behaviour is caused by the real_ip_recursive on; directive.

From the nginx realip docs:

If recursive search is enabled, an original client address that matches one of the trusted addresses is replaced by the last non-trusted address sent in the request header field.

You have specified to trust b.b.b.b (because of your set_real_ip_from b.b.b.b;

So what you would expect, i.e. a.a.a.a, b.b.b.b will get replaced by a.a.a.a, a.a.a.a.

The source that made this clear for me is: https://serverfault.com/questions/314574/nginx-real-ip-header-and-x-forwarded-for-seems-wrong

Tom
  • 14,041
  • 16
  • 64
  • 80