Nginx restore real IP address when behind a reverse proxy

My Nginx web server is behind a reverse proxy server. How do I restore the original and real IP address of my client/visitors when behind a reverse proxy server such as AWS Cloudfront, Fastly, Cloudflare CDN/WAF?

When our web server is behind a reverse proxy, we see the reverse proxy IP logged into log files. But we need the user’s real ip address. Let us see how to get real user’s IP address in nginx behind a reverse proxy server and restore actual IP address using the ngx_http_realip_module


Nginx restore real IP address with the ngx_http_realip_module

Nginx restore real IP address when nginx server is behind a reverse proxy
First let us see if Nginx compiled and loaded with ngx_http_realip_module using the following command:
nginx -V 2>&1 | egrep --color -o 'http_realip_module'
nginx -V 2>&1 | egrep --color -o 'realip_module'

Sample outputs indicating that my Nginx compiled with required module:


Step 1 – Restoring visitor IPs by setting header name in Nginx

Edit your nginx configuration file such as nginx.conf or virtual domain config. For example:
sudo vi /etc/nginx/vhosts.d/
Set the following in http, server, or location context as follows:
real_ip_header X-Forwarded-For;
Cloudflare users try the following:
real_ip_header CF-Connecting-IP;
Some reverse proxy passes on header named X-Real-IP to backends, so we can use it as follows:
real_ip_header X-Real-IP;

Step 2 – Get user real ip in nginx behind reverse proxy

We need to defines trusted IP addresses that are known to send correct replacement addresses. Typically we add upstream servers IP address. The syntax is:
set_real_ip_from ipv4_addresss;
set_real_ip_from ipv6_address;
set_real_ip_from sub/net;
set_real_ip_from CIDR;

In this instance my trusted upstream proxy is
Here are a few more examples:
set_real_ip_from 2606:4700:10::6816:ad6;
Trust Linode nodebalancer IP:

Step 3 – Restart nginx server

Use the systemctl command sudo systemctl restart nginx
## or ##
sudo systemctl reload nginx

For Unix based system, try:
sudo nginx -s reload

Step 4 – Cloudflare helper scripts to deal with the Forwarded header for Nginx

Revers proxy service providers such as Cloudfront, Fastly, Cloudflare, and others have numerous IPv4 and IPv6 addresses/Classless inter-domain routing (CIDR). Typically they publish a list of all IPv4/IPv6, and we can script it out as per our need. Let us see how to automate it using Cloudflare.

Update nginx config file as follows

Let us edit vdoamin file:
sudo vi /etc/nginx/vhosts.d/
Append the following in server context:
include "/etc/nginx/vhosts.d/cloudflare.conf";
Save and close the file.

Create a shell script to fetch all Cloudflare IP address

sudo vi /root/bin/
Append the following bash code:

#!/usr/bin/env bash
# Purpose: Get user real ip in nginx behind Cloudflare reverse proxy
# Author: Vivek Gite {} under GNU GPL v2.x+
# Call using Cron
# -------------------------------------------------------------------
set -e
# IP List 
# Nginx config file
# Path to nginx binary 
# Get list
wget -q "${IPv4}" -O -> "${file}"
wget -q "${IPv6}" -O ->> "${file}"
# Start building config file
echo 'real_ip_header CF-Connecting-IP;' > "${conf}"
while read i 
	echo "set_real_ip_from ${i};" >> "${conf}"
done < "${file}"
# Check for syntax error and reload the nginx 
${nginx_cmd} -qt && ${nginx_cmd} -s reload
# Clean up
rm -f "${file}"

Set up daily or weekly Linux/Unix cron job

First, set up permission using the chmod command:
chmod +x /root/bin/
Next create soft link with ln command so that everyday file gets updated:
sudo ln -v -s /root/bin/ /etc/cron.d/
Sample outputs:

'/etc/cron.d/' -> '/root/bin/'

To be honest weekly cron job is more than sufficient:
sudo ln -v -s /root/bin/ /etc/cron.weekly/

Test it

We are not going to wait for the cron job. So let us run our script manually:
sudo /etc/cron.d/

Step 5 – Verification

Check your nginx log file and make sure real IP address logged using the tail command/cat command/grep command:
tail -f /var/log/nginx/cyberciti.biz_access.log
Real ip address: - - [07/May/2020:12:16:24 +0000] "GET / HTTP/1.1" 200 3032 "" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36" - - [07/May/2020:12:16:25 +0000] "GET /style.css HTTP/1.1" 200 399 "" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36" - - [07/May/2020:12:16:25 +0000] "GET /images/logo.png HTTP/1.1" 200 34001 "" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36"


In this tutorial, you learned how to retrieve actual client IP addresses and get the real IP address of visitors when your Nginx web server is behind a reverse proxy server. Further, you learned how to automate the process using a simple shell script for Nginx to restore a real IP address. See Nginx docs here for more info.

🐧 If you liked this page, please support my work on Patreon or with a donation.
🐧 Get the latest tutorials on SysAdmin, Linux/Unix, Open Source/DevOps topics:
CategoryList of Unix and Linux commands
File Managementcat
FirewallAlpine Awall CentOS 8 OpenSUSE RHEL 8 Ubuntu 16.04 Ubuntu 18.04 Ubuntu 20.04
Network Utilitiesdig host ip nmap
OpenVPNCentOS 7 CentOS 8 Debian 10 Debian 8/9 Ubuntu 18.04 Ubuntu 20.04
Package Managerapk apt
Processes Managementbg chroot cron disown fg jobs killall kill pidof pstree pwdx time
Searchinggrep whereis which
User Informationgroups id lastcomm last lid/libuser-lid logname members users whoami who w
WireGuard VPNAlpine CentOS 8 Debian 10 Firewall Ubuntu 20.04

2 comments… add one
  • Maurice Jul 16, 2020 @ 0:00

    Very nice tutorial.
    Please note though that your bash script appends the IPs to the conf file each time it runs, instead of erasing the old list and writing the new one. I solved this by adding “rm $conf” just before starting to build the file.

    • 🐧 Vivek Gite Jul 16, 2020 @ 3:54

      The following line avoids that:


      No need to use the rm command. HTH

Leave a Reply

Your email address will not be published.

Use HTML <pre>...</pre>, <code>...</code> and <kbd>...</kbd> for code samples.