I have the following nginx configuration:

worker_processes auto;
daemon off;
error_log /dev/stdout warn;
include /etc/nginx/modules/*.conf;

events {


http {
  real_ip_header X-Forwarded-For;
  real_ip_recursive on;

  server_tokens off;
  default_type application/octet-stream;
  log_format json_combined escape=json
  '"real_ip": "$realip_remote_addr",'
  '"http_x_real_ip": "$http_x_real_ip"'
  access_log /dev/stdout json_combined;

  server {
    location / {
      root /content/;
include /etc/nginx/conf.d/*.conf;

My expectation is that the realip_remote_addr variable should have the last non-trusted address in the X-Forwarded-For header. However, when I make a request such as this:

$ curl -v --header "X-Forwarded-For:," localhost

The logs show for both the remote_addr and realip_remote_addr variables.

Here's my nginx version:

nginx version: nginx/1.16.0
built by gcc 9.1.1 20190503 (Red Hat 9.1.1-1) (GCC) 
built with OpenSSL 1.1.1b FIPS  26 Feb 2019 (running with OpenSSL 1.1.1c FIPS  28 May 2019)
TLS SNI support enabled
configure arguments: --prefix=/usr/share/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --http-client-body-temp-path=/var/lib/nginx/tmp/client_body --http-proxy-temp-path=/var/lib/nginx/tmp/proxy --http-fastcgi-temp-path=/var/lib/nginx/tmp/fastcgi --http-uwsgi-temp-path=/var/lib/nginx/tmp/uwsgi --http-scgi-temp-path=/var/lib/nginx/tmp/scgi --pid-path=/run/nginx.pid --lock-path=/run/lock/subsys/nginx --user=nginx --group=nginx --with-file-aio --with-ipv6 --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-stream_ssl_preread_module --with-http_addition_module --with-http_xslt_module=dynamic --with-http_image_filter_module=dynamic --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_random_index_module --with-http_secure_link_module --with-http_degradation_module --with-http_slice_module --with-http_stub_status_module --with-http_perl_module=dynamic --with-http_auth_request_module --with-mail=dynamic --with-mail_ssl_module --with-pcre --with-pcre-jit --with-stream=dynamic --with-stream_ssl_module --with-google_perftools_module --with-debug --with-cc-opt='-O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -fexceptions -fstack-protector-strong -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection' --with-ld-opt='-Wl,-z,relro -Wl,--as-needed -Wl,-z,now -specs=/usr/lib/rpm/redhat/redhat-hardened-ld -Wl,-E'

Any ideas what I am doing wrong?

1 Answer 1


The setting set_real_ip_from means that nginx will only trust X-Forwarded-For headers sent from that IP address. In your test the header comes from and hence nginx ignores that header.

  • Thank you. How does the logic apply when you have a chain of proxies adding their own IP address in the X-Forwarded-For header? We can use CIDRs if they are in known subnets but if they are across different subnets, would be the only solution?
    – Amit
    Jul 22, 2019 at 7:42
  • I believe that you can pass mutliple set_real_ip_from settings to cover multiple proxies. However if there is a chain of IP addresses in the X-Forwarded-For header, really only the last one is relevant (the rest could be faked), and I don't know what the realip module will do in such a situation.
    – wurtel
    Jul 22, 2019 at 8:55
  • Thanks for your suggestion.
    – Amit
    Jul 22, 2019 at 22:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.