Is there a way to collect the IP address of a client connected to your website through a proxy server?
The entire setup is an internal LAN and through the sysadmin, I have control over the proxy machine as well. I am using PHP5 for the website server side.
I tried $_SERVER['REMOTE_ADDR'] in PHP but this variable just stores the IP address of the proxy.
Any ideas?
The standard solution (in php) is:
if ($_SERVER['HTTP_X_FORWARDED_FOR']){
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
}
else{
$ip = $_SERVER['REMOTE_ADDR'];
}
But as the first answer says this all depends on the header actually being set.
It depends on the proxy. Some proxies add a header which gives the original IP address, the X-Forwarded-For header, but given that most companies uses proxies to hide the internal network structure that's rare. If this is the case then you're not going to be able to do it easily.
If you have control over the proxy then it's time to read the proxy documentation to see how to add that header.
X-Forwarded-For is the only way to get client's IP address. Check if there is a way to enable that in your proxy.
On some proxy, it gives you option how to handle existing XFF header (when request passes through multiple proxies). Here is what you need to consider,
If the client address is for security/trust purposes (like ACL or rate-limiting), existing XFF header should be dropped by proxy.
If the address is for information only (logging, debugging), you should append peer address to existing XFF, separated by comma. The first IP on the list would be the client's IP.
This code can be used to get the client's IP address who's connecting through a proxy.
public static String getClientIpAddr(HttpServletRequest request) {
String ip = request.getHeader("X-Forwarded-For");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
But it detects only when proxy is transparent.
Below is the information on HTTP proxy:
Not using any proxy server:
request.getRemoteAddr() = IP address of client
request.getHeader("HTTP_X_FORWARDED_FOR") = No value or No display
Use Transparent Proxies:
HTTP_X_FORWARDED_FOR = Real IP address of client
Use Normal Anonymous Proxies:
request.getRemoteAddr() = IP address of proxy server
HTTP_X_FORWARDED_FOR = IP address of proxy server
Related
I am trying to code a PHP script for getting the client's IP address using this function:
public function getIpAddress() {
$ipAddress = '';
if (! empty($_SERVER['HTTP_CLIENT_IP']) && $this->isValidIpAddress($_SERVER['HTTP_CLIENT_IP'])) {
// check for shared ISP IP
$ipAddress = $_SERVER['HTTP_CLIENT_IP'];
} else if (! empty($_SERVER['HTTP_X_FORWARDED_FOR']) && $this->isValidIpAddress($_SERVER['HTTP_CLIENT_IP'])) {
// check for IPs passing through proxy servers
// check if multiple IP addresses are set and take the first one
$ipAddressList = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
foreach ($ipAddressList as $ip) {
if ($this->isValidIpAddress($ip)) {
$ipAddress = $ip;
break;
}
}
} else if (! empty($_SERVER['HTTP_X_FORWARDED']) && $this->isValidIpAddress($_SERVER['HTTP_X_FORWARDED'])) {
$ipAddress = $_SERVER['HTTP_X_FORWARDED'];
} else if (! empty($_SERVER['HTTP_X_CLUSTER_CLIENT_IP']) && $this->isValidIpAddress($_SERVER['HTTP_X_CLUSTER_CLIENT_IP'])) {
$ipAddress = $_SERVER['HTTP_X_CLUSTER_CLIENT_IP'];
} else if (! empty($_SERVER['HTTP_FORWARDED_FOR']) && $this->isValidIpAddress($_SERVER['HTTP_FORWARDED_FOR'])) {
$ipAddress = $_SERVER['HTTP_FORWARDED_FOR'];
} else if (! empty($_SERVER['HTTP_FORWARDED']) && $this->isValidIpAddress($_SERVER['HTTP_FORWARDED'])) {
$ipAddress = $_SERVER['HTTP_FORWARDED'];
} else if (! empty($_SERVER['REMOTE_ADDR']) && $this->isValidIpAddress($_SERVER['REMOTE_ADDR'])) {
$ipAddress = $_SERVER['REMOTE_ADDR'];
}else{
$ipAddress = 'error';
}
return $ipAddress;
}
I tried it many times and it's working. But I tried to connect to a VPN and it retrieved IPv6 instead of IPv4!
This could be a normal situation but I need to get the IPv4 of the visitor of that PHP script.
I googled a lot about converting IPv6 to IPv4 and I know that it cannot be converted.
But I notices some IP geolocation services are retrieving IPv4 for the same VPN (on the same device)! For example: canyouseeme.org and api.ipify.org.
That's mean that even if the IPv6 cannot be converted into IPv4, there's a method can be implemented to get IPv4 even if the visitor is using IPv6!
My questions:
If the conversion is not possible between these two versions, how did these IP geolocation services retrieved the IPv4 of the visitor?
If they are not converting the IPv6 into IPv4. So, they are retrieving the IPv4 directly without touching the IPv6. But, how did they do that if the $_SERVER does not contain an IPv4?
In additional to that, I noticed that when I visited whatismyipaddress.com, they retrieved the IPv6 first then it start loading beside the IPv4 field then they retrieved it!
Note: All these sites has retrieved the same IPv4.
Thanks.
Just drop the AAAA record from your DNS name (or disable ipv6 in your webservrr - this might introduce delays as browsers are supposed to prefer ipv6), then, however, people w/o ipv4 cannot access your service any more. As #MagnusEriksson said: "Instead of fighting the future, wouldn't it be better to fix your application so it works with IPv6 as well?"
Also, please be aware, if the ip address is so important for you, that http headers can be easily spoofed by clients (i.e., don't trust user provided data) or proxy servers might provide private ips as defined in RFC1918. You should only consider/use the headers you know a trusted proxy infront of your server uses - in all other cases you cannot trust the user provided headers.
if you are using apache, you may want to check if there are any IPv6 Bindings or VirtualHosts. there will be similar settings for other web servers
if you don't have access to the web server config, you can see if there's an option to disable IPv6 in your webhost panel
also, this could be client-controlled, i.e. when you connect using a VPN then IPv6 is enabled for that network interface (or tunnel). in which case you cannot force the visitor to disable IPv6 on their device / router, but you could try to disable it on your end. unlike HTTPS Everywhere which only accepts port 443 or nothing, I don't think IP addresses are at the stage where it is only possible to accept IPv6 with no fallback to IPv4, so this recommendation should be safe
I'm trying to track and log users/visitors that are accessing my website using PHP's $_SERVER['REMOTE_ADDR'] to do so. A typical method for IP address tracking in PHP.
However, I am using CloudFlare for caching and such and receiving their IP addresses as CloudFlare's:
108.162.212.* - 108.162.239.*
What would be a correct method of retrieving the actual users/visitors IP address while still using CloudFlare?
Extra server variables that are available to cloud flare are:
$_SERVER["HTTP_CF_CONNECTING_IP"] real visitor ip address, this is what you want
$_SERVER["HTTP_CF_IPCOUNTRY"] country of visitor
$_SERVER["HTTP_CF_RAY"]
$_SERVER["HTTP_CF_VISITOR"] this can help you know if its http or https
you can use it like this:
if (isset($_SERVER["HTTP_CF_CONNECTING_IP"])) {
$_SERVER['REMOTE_ADDR'] = $_SERVER["HTTP_CF_CONNECTING_IP"];
}
If you do this, and the validity of the visiting IP address is important, you might need to verify that the $_SERVER["REMOTE_ADDR"] contains an actual valid cloudflare IP address, because anyone can fake the header if he was able to connect directly to the server IP.
Update: CloudFlare has released a module mod_cloudflare for apache, the module will log and display the actual visitor IP Addresses rather than those accessed by cloudflare! https://www.cloudflare.com/resources-downloads#mod_cloudflare (Answer by: olimortimer)
If you dont have access to the apache runtime you can use the script below, this will allow you to check if the connection was through cloudflare and get the users ip.
I am rewriting my answer i used for another question "CloudFlare DNS / direct IP identifier"
Cloudflare's ips are stored in public so you can go view them here then check if the ip is from cloudflare (this will allow us to get the real ip from the http header HTTP_CF_CONNECTING_IP).
If you are using this to disable all non cf connections or vice versa, i recommend you to have a single php script file that gets called before every other script such as a common.php or pagestart.php etc.
function ip_in_range($ip, $range) {
if (strpos($range, '/') == false)
$range .= '/32';
// $range is in IP/CIDR format eg 127.0.0.1/24
list($range, $netmask) = explode('/', $range, 2);
$range_decimal = ip2long($range);
$ip_decimal = ip2long($ip);
$wildcard_decimal = pow(2, (32 - $netmask)) - 1;
$netmask_decimal = ~ $wildcard_decimal;
return (($ip_decimal & $netmask_decimal) == ($range_decimal & $netmask_decimal));
}
function _cloudflare_CheckIP($ip) {
$cf_ips = array(
'199.27.128.0/21',
'173.245.48.0/20',
'103.21.244.0/22',
'103.22.200.0/22',
'103.31.4.0/22',
'141.101.64.0/18',
'108.162.192.0/18',
'190.93.240.0/20',
'188.114.96.0/20',
'197.234.240.0/22',
'198.41.128.0/17',
'162.158.0.0/15',
'104.16.0.0/12',
);
$is_cf_ip = false;
foreach ($cf_ips as $cf_ip) {
if (ip_in_range($ip, $cf_ip)) {
$is_cf_ip = true;
break;
}
} return $is_cf_ip;
}
function _cloudflare_Requests_Check() {
$flag = true;
if(!isset($_SERVER['HTTP_CF_CONNECTING_IP'])) $flag = false;
if(!isset($_SERVER['HTTP_CF_IPCOUNTRY'])) $flag = false;
if(!isset($_SERVER['HTTP_CF_RAY'])) $flag = false;
if(!isset($_SERVER['HTTP_CF_VISITOR'])) $flag = false;
return $flag;
}
function isCloudflare() {
$ipCheck = _cloudflare_CheckIP($_SERVER['REMOTE_ADDR']);
$requestCheck = _cloudflare_Requests_Check();
return ($ipCheck && $requestCheck);
}
// Use when handling ip's
function getRequestIP() {
$check = isCloudflare();
if($check) {
return $_SERVER['HTTP_CF_CONNECTING_IP'];
} else {
return $_SERVER['REMOTE_ADDR'];
}
}
To use the script it's quite simple:
$ip = getRequestIP();
$cf = isCloudflare();
if($cf) echo "Connection is through cloudflare: <br>";
else echo "Connection is not through cloudflare: <br>";
echo "Your actual ip address is: ". $ip;
echo "The server remote address header is: ". $_SERVER['REMOTE_ADDR'];
This script will show you the real ip address and if the request is CF or not!
Cloudflare sends some additional request headers to your server including CF-Connecting-IP which we can store into $user_ip, if defined, using this simple one-liner:
$user_ip = $_SERVER["HTTP_CF_CONNECTING_IP"] ?? $_SERVER['REMOTE_ADDR'];
Since this question was asked and answered, CloudFlare has released mod_cloudflare for Apache, which logs & displays the actual visitor IP address rather than the CloudFlare address:
https://www.cloudflare.com/resources-downloads#mod_cloudflare
It would be hard to convert HTTP_CF_CONNECTING_IP to REMOTE_ADDR. So you can use apache (.htaccess) auto prepending to do that. So that you do not need to think about whether the $_SERVER['REMOTE_ADDR'] has the correct value in all the PHP scripts.
.htaccess code
php_value auto_prepend_file "/path/to/file.php"
php code (file.php)
<?php
define('CLIENT_IP', isset($_SERVER['HTTP_CF_CONNECTING_IP']) ? $_SERVER['HTTP_CF_CONNECTING_IP'] : $_SERVER['REMOTE_ADDR']);
Learn More here
HTTP_CF_CONNECTING_IP is only working if you are using cloudflare maybe you transfer your site or remove cloudflare you will forget the value so use this code .
$ip=$_SERVER["HTTP_CF_CONNECTING_IP"];
if (!isset($ip)) {
$ip = $_SERVER['REMOTE_ADDR'];
}
When you are using CloudFlare all your requests between your server and users are routed through CloudFlare servers.
In this case there are two methods to get User's Real IP Address:
Through Extra Server Headers added by CloudFlare Servers
Adding a CloudFlare Apache/NGINX Module on your server.
Method 1: Get IP though extra Server Headers
You can use the following code to get user's IP Address:
$user_ip = (isset($_SERVER["HTTP_CF_CONNECTING_IP"]) $_SERVER["HTTP_CF_CONNECTING_IP"]:$_SERVER['REMOTE_ADDR']);
How it is working?
CloudFlare adds some extra server variables in the request as follows:
$_SERVER["HTTP_CF_CONNECTING_IP"] - Real IP Address of user
$_SERVER["HTTP_CF_IPCOUNTRY"] - ISO2 Country of the User
$_SERVER["HTTP_CF_RAY"] A Special string for loggin purpose
In the above code, we are checking if $_SERVER["HTTP_CF_CONNECTING_IP"] is set or not. If it is there we will consider that as user's IP Address else we will use the default code as $_SERVER['REMOTE_ADDR']
Method 2: Installing Cloudflare Module on your server
Installing CloudFlare Module on Apache Server
Installing CloudFlare Module on NGINX Server
Installing CloudFlare Module on EasyApache+CPanel
In Laravel add the following line to AppServiceProvider:
...
use Symfony\Component\HttpFoundation\Request;
...
public function boot()
{
Request::setTrustedProxies(['REMOTE_ADDR'], Request::HEADER_X_FORWARDED_FOR);
}
Now you can get real client IP using request()->ip().
Read more here.
Cloudflare has an option to use a "Pseudo IPv4" address in the headers on the Network Management page for the domain. You have the option of adding or overwriting the headers that are sent to your server.
From the documentation:
What is Pseudo IPv4?
As a stopgap to accelerate the adoption of IPv6, Cloudflare offers Pseudo IPv4 which supports IPv6 addresses in legacy applications expecting IPv4 addresses. The goal is to provide a nearly unique IPv4 address for each IPv6 address, using Class E IPv4 address space, which is designated as experimental and would not normally see traffic. To learn more see here.
Options
Add header: Add additional Cf-Pseudo-IPv4 header only
Overwrite headers: Overwrite the existing Cf-Connecting-IP and X-Forwarded-For headers with a pseudo IPv4 address.
Note: We recommend leaving this set to “Off” unless you have a specific need.
You can learn more about this feature from this article from Cloudflare, where it goes into a little more detail about how they try to accommodate the 128-bit address space of IPv6 in the 32-bit space found in IPv4.
In the event that you would like to know the IPv6 address along with the pseudo IPv4, Cloudflare adds a Cf-Connecting-IPv6 header when Pseudo IPv4 is enabled. This can be used to measure how much of your traffic is originating from devices that are on IPv6 networks, which can be useful if you need a solid number to show management before investments are made in updating systems that are still bound to the IPv4 limitations.
It's better to check cloudflare ip ranges online (because it may change anytime) then check it's from cloudflare or not.
Cloudflare IP txt
If the source is Cloudflare you can use $_SERVER['HTTP_CF_CONNECTING_IP'] to get your client request ip-address but it's not safe to use it for all requests because it can send by any user in request header to trick you.
You can use following code to get real ip-address of your client request:
function _getUserRealIP() {
$ipaddress = '';
if(isset($_SERVER['REMOTE_ADDR']))
$ipaddress = $_SERVER['REMOTE_ADDR'];
else if (isset($_SERVER['HTTP_CLIENT_IP']))
$ipaddress = $_SERVER['HTTP_CLIENT_IP'];
else if(isset($_SERVER['HTTP_X_FORWARDED_FOR']))
$ipaddress = $_SERVER['HTTP_X_FORWARDED_FOR'];
else if(isset($_SERVER['HTTP_X_FORWARDED']))
$ipaddress = $_SERVER['HTTP_X_FORWARDED'];
else if(isset($_SERVER['HTTP_X_CLUSTER_CLIENT_IP']))
$ipaddress = $_SERVER['HTTP_X_CLUSTER_CLIENT_IP'];
else if(isset($_SERVER['HTTP_FORWARDED_FOR']))
$ipaddress = $_SERVER['HTTP_FORWARDED_FOR'];
else if(isset($_SERVER['HTTP_FORWARDED']))
$ipaddress = $_SERVER['HTTP_FORWARDED'];
else
$ipaddress = 'UNKNOWN';
return $ipaddress;
}
function _readCloudflareIps()
{
$file = file("https://www.cloudflare.com/ips-v4",FILE_IGNORE_NEW_LINES);
return $file;
}
function _checkIpInRange($ip, $range) {
if (strpos($range, '/') == false)
$range .= '/32';
// $range is in IP/CIDR format eg 127.0.0.1/24
list($range, $netmask) = explode('/', $range, 2);
$range_decimal = ip2long($range);
$ip_decimal = ip2long($ip);
$wildcard_decimal = pow(2, (32 - $netmask)) - 1;
$netmask_decimal = ~ $wildcard_decimal;
return (($ip_decimal & $netmask_decimal) == ($range_decimal & $netmask_decimal));
}
function _checkIsCloudflare($ip) {
$cf_ips = _readCloudflareIps();
$is_cf_ip = false;
foreach ($cf_ips as $cf_ip) {
if (_checkIpInRange($ip, $cf_ip)) {
$is_cf_ip = true;
break;
}
}
return $is_cf_ip;
}
function getRealIp()
{
$httpIp = _getUserRealIP();
$check = _checkIsCloudflare($httpIp);
if ($check) {
return $_SERVER['HTTP_CF_CONNECTING_IP'];
}else{
return $httpIp;
}
}
After importing this functions to your code you just need to call getRealIp() function like:
$userIp = getRealIp();
echo $userIp();
For magento 1.x users (I haven't try magento 2.0 yet), check https://tall-paul.co.uk/2012/03/13/magento-show-remote-ip-in-cloudflare-the-right-way/ which needs to change app/etc/local.xml and add:
HTTP_CF_CONNECTING_IP
another way to get it in Laravel is simply by reading HTTP_CF_CONNECTING_IP header
$request->server('HTTP_CF_CONNECTING_IP')
you can read more about it here:
How to get real client IP behind Cloudflare in Laravel / PHP
I'm trying to track and log users/visitors that are accessing my website using PHP's $_SERVER['REMOTE_ADDR'] to do so. A typical method for IP address tracking in PHP.
However, I am using CloudFlare for caching and such and receiving their IP addresses as CloudFlare's:
108.162.212.* - 108.162.239.*
What would be a correct method of retrieving the actual users/visitors IP address while still using CloudFlare?
Extra server variables that are available to cloud flare are:
$_SERVER["HTTP_CF_CONNECTING_IP"] real visitor ip address, this is what you want
$_SERVER["HTTP_CF_IPCOUNTRY"] country of visitor
$_SERVER["HTTP_CF_RAY"]
$_SERVER["HTTP_CF_VISITOR"] this can help you know if its http or https
you can use it like this:
if (isset($_SERVER["HTTP_CF_CONNECTING_IP"])) {
$_SERVER['REMOTE_ADDR'] = $_SERVER["HTTP_CF_CONNECTING_IP"];
}
If you do this, and the validity of the visiting IP address is important, you might need to verify that the $_SERVER["REMOTE_ADDR"] contains an actual valid cloudflare IP address, because anyone can fake the header if he was able to connect directly to the server IP.
Update: CloudFlare has released a module mod_cloudflare for apache, the module will log and display the actual visitor IP Addresses rather than those accessed by cloudflare! https://www.cloudflare.com/resources-downloads#mod_cloudflare (Answer by: olimortimer)
If you dont have access to the apache runtime you can use the script below, this will allow you to check if the connection was through cloudflare and get the users ip.
I am rewriting my answer i used for another question "CloudFlare DNS / direct IP identifier"
Cloudflare's ips are stored in public so you can go view them here then check if the ip is from cloudflare (this will allow us to get the real ip from the http header HTTP_CF_CONNECTING_IP).
If you are using this to disable all non cf connections or vice versa, i recommend you to have a single php script file that gets called before every other script such as a common.php or pagestart.php etc.
function ip_in_range($ip, $range) {
if (strpos($range, '/') == false)
$range .= '/32';
// $range is in IP/CIDR format eg 127.0.0.1/24
list($range, $netmask) = explode('/', $range, 2);
$range_decimal = ip2long($range);
$ip_decimal = ip2long($ip);
$wildcard_decimal = pow(2, (32 - $netmask)) - 1;
$netmask_decimal = ~ $wildcard_decimal;
return (($ip_decimal & $netmask_decimal) == ($range_decimal & $netmask_decimal));
}
function _cloudflare_CheckIP($ip) {
$cf_ips = array(
'199.27.128.0/21',
'173.245.48.0/20',
'103.21.244.0/22',
'103.22.200.0/22',
'103.31.4.0/22',
'141.101.64.0/18',
'108.162.192.0/18',
'190.93.240.0/20',
'188.114.96.0/20',
'197.234.240.0/22',
'198.41.128.0/17',
'162.158.0.0/15',
'104.16.0.0/12',
);
$is_cf_ip = false;
foreach ($cf_ips as $cf_ip) {
if (ip_in_range($ip, $cf_ip)) {
$is_cf_ip = true;
break;
}
} return $is_cf_ip;
}
function _cloudflare_Requests_Check() {
$flag = true;
if(!isset($_SERVER['HTTP_CF_CONNECTING_IP'])) $flag = false;
if(!isset($_SERVER['HTTP_CF_IPCOUNTRY'])) $flag = false;
if(!isset($_SERVER['HTTP_CF_RAY'])) $flag = false;
if(!isset($_SERVER['HTTP_CF_VISITOR'])) $flag = false;
return $flag;
}
function isCloudflare() {
$ipCheck = _cloudflare_CheckIP($_SERVER['REMOTE_ADDR']);
$requestCheck = _cloudflare_Requests_Check();
return ($ipCheck && $requestCheck);
}
// Use when handling ip's
function getRequestIP() {
$check = isCloudflare();
if($check) {
return $_SERVER['HTTP_CF_CONNECTING_IP'];
} else {
return $_SERVER['REMOTE_ADDR'];
}
}
To use the script it's quite simple:
$ip = getRequestIP();
$cf = isCloudflare();
if($cf) echo "Connection is through cloudflare: <br>";
else echo "Connection is not through cloudflare: <br>";
echo "Your actual ip address is: ". $ip;
echo "The server remote address header is: ". $_SERVER['REMOTE_ADDR'];
This script will show you the real ip address and if the request is CF or not!
Cloudflare sends some additional request headers to your server including CF-Connecting-IP which we can store into $user_ip, if defined, using this simple one-liner:
$user_ip = $_SERVER["HTTP_CF_CONNECTING_IP"] ?? $_SERVER['REMOTE_ADDR'];
Since this question was asked and answered, CloudFlare has released mod_cloudflare for Apache, which logs & displays the actual visitor IP address rather than the CloudFlare address:
https://www.cloudflare.com/resources-downloads#mod_cloudflare
It would be hard to convert HTTP_CF_CONNECTING_IP to REMOTE_ADDR. So you can use apache (.htaccess) auto prepending to do that. So that you do not need to think about whether the $_SERVER['REMOTE_ADDR'] has the correct value in all the PHP scripts.
.htaccess code
php_value auto_prepend_file "/path/to/file.php"
php code (file.php)
<?php
define('CLIENT_IP', isset($_SERVER['HTTP_CF_CONNECTING_IP']) ? $_SERVER['HTTP_CF_CONNECTING_IP'] : $_SERVER['REMOTE_ADDR']);
Learn More here
HTTP_CF_CONNECTING_IP is only working if you are using cloudflare maybe you transfer your site or remove cloudflare you will forget the value so use this code .
$ip=$_SERVER["HTTP_CF_CONNECTING_IP"];
if (!isset($ip)) {
$ip = $_SERVER['REMOTE_ADDR'];
}
When you are using CloudFlare all your requests between your server and users are routed through CloudFlare servers.
In this case there are two methods to get User's Real IP Address:
Through Extra Server Headers added by CloudFlare Servers
Adding a CloudFlare Apache/NGINX Module on your server.
Method 1: Get IP though extra Server Headers
You can use the following code to get user's IP Address:
$user_ip = (isset($_SERVER["HTTP_CF_CONNECTING_IP"]) $_SERVER["HTTP_CF_CONNECTING_IP"]:$_SERVER['REMOTE_ADDR']);
How it is working?
CloudFlare adds some extra server variables in the request as follows:
$_SERVER["HTTP_CF_CONNECTING_IP"] - Real IP Address of user
$_SERVER["HTTP_CF_IPCOUNTRY"] - ISO2 Country of the User
$_SERVER["HTTP_CF_RAY"] A Special string for loggin purpose
In the above code, we are checking if $_SERVER["HTTP_CF_CONNECTING_IP"] is set or not. If it is there we will consider that as user's IP Address else we will use the default code as $_SERVER['REMOTE_ADDR']
Method 2: Installing Cloudflare Module on your server
Installing CloudFlare Module on Apache Server
Installing CloudFlare Module on NGINX Server
Installing CloudFlare Module on EasyApache+CPanel
In Laravel add the following line to AppServiceProvider:
...
use Symfony\Component\HttpFoundation\Request;
...
public function boot()
{
Request::setTrustedProxies(['REMOTE_ADDR'], Request::HEADER_X_FORWARDED_FOR);
}
Now you can get real client IP using request()->ip().
Read more here.
Cloudflare has an option to use a "Pseudo IPv4" address in the headers on the Network Management page for the domain. You have the option of adding or overwriting the headers that are sent to your server.
From the documentation:
What is Pseudo IPv4?
As a stopgap to accelerate the adoption of IPv6, Cloudflare offers Pseudo IPv4 which supports IPv6 addresses in legacy applications expecting IPv4 addresses. The goal is to provide a nearly unique IPv4 address for each IPv6 address, using Class E IPv4 address space, which is designated as experimental and would not normally see traffic. To learn more see here.
Options
Add header: Add additional Cf-Pseudo-IPv4 header only
Overwrite headers: Overwrite the existing Cf-Connecting-IP and X-Forwarded-For headers with a pseudo IPv4 address.
Note: We recommend leaving this set to “Off” unless you have a specific need.
You can learn more about this feature from this article from Cloudflare, where it goes into a little more detail about how they try to accommodate the 128-bit address space of IPv6 in the 32-bit space found in IPv4.
In the event that you would like to know the IPv6 address along with the pseudo IPv4, Cloudflare adds a Cf-Connecting-IPv6 header when Pseudo IPv4 is enabled. This can be used to measure how much of your traffic is originating from devices that are on IPv6 networks, which can be useful if you need a solid number to show management before investments are made in updating systems that are still bound to the IPv4 limitations.
It's better to check cloudflare ip ranges online (because it may change anytime) then check it's from cloudflare or not.
Cloudflare IP txt
If the source is Cloudflare you can use $_SERVER['HTTP_CF_CONNECTING_IP'] to get your client request ip-address but it's not safe to use it for all requests because it can send by any user in request header to trick you.
You can use following code to get real ip-address of your client request:
function _getUserRealIP() {
$ipaddress = '';
if(isset($_SERVER['REMOTE_ADDR']))
$ipaddress = $_SERVER['REMOTE_ADDR'];
else if (isset($_SERVER['HTTP_CLIENT_IP']))
$ipaddress = $_SERVER['HTTP_CLIENT_IP'];
else if(isset($_SERVER['HTTP_X_FORWARDED_FOR']))
$ipaddress = $_SERVER['HTTP_X_FORWARDED_FOR'];
else if(isset($_SERVER['HTTP_X_FORWARDED']))
$ipaddress = $_SERVER['HTTP_X_FORWARDED'];
else if(isset($_SERVER['HTTP_X_CLUSTER_CLIENT_IP']))
$ipaddress = $_SERVER['HTTP_X_CLUSTER_CLIENT_IP'];
else if(isset($_SERVER['HTTP_FORWARDED_FOR']))
$ipaddress = $_SERVER['HTTP_FORWARDED_FOR'];
else if(isset($_SERVER['HTTP_FORWARDED']))
$ipaddress = $_SERVER['HTTP_FORWARDED'];
else
$ipaddress = 'UNKNOWN';
return $ipaddress;
}
function _readCloudflareIps()
{
$file = file("https://www.cloudflare.com/ips-v4",FILE_IGNORE_NEW_LINES);
return $file;
}
function _checkIpInRange($ip, $range) {
if (strpos($range, '/') == false)
$range .= '/32';
// $range is in IP/CIDR format eg 127.0.0.1/24
list($range, $netmask) = explode('/', $range, 2);
$range_decimal = ip2long($range);
$ip_decimal = ip2long($ip);
$wildcard_decimal = pow(2, (32 - $netmask)) - 1;
$netmask_decimal = ~ $wildcard_decimal;
return (($ip_decimal & $netmask_decimal) == ($range_decimal & $netmask_decimal));
}
function _checkIsCloudflare($ip) {
$cf_ips = _readCloudflareIps();
$is_cf_ip = false;
foreach ($cf_ips as $cf_ip) {
if (_checkIpInRange($ip, $cf_ip)) {
$is_cf_ip = true;
break;
}
}
return $is_cf_ip;
}
function getRealIp()
{
$httpIp = _getUserRealIP();
$check = _checkIsCloudflare($httpIp);
if ($check) {
return $_SERVER['HTTP_CF_CONNECTING_IP'];
}else{
return $httpIp;
}
}
After importing this functions to your code you just need to call getRealIp() function like:
$userIp = getRealIp();
echo $userIp();
For magento 1.x users (I haven't try magento 2.0 yet), check https://tall-paul.co.uk/2012/03/13/magento-show-remote-ip-in-cloudflare-the-right-way/ which needs to change app/etc/local.xml and add:
HTTP_CF_CONNECTING_IP
another way to get it in Laravel is simply by reading HTTP_CF_CONNECTING_IP header
$request->server('HTTP_CF_CONNECTING_IP')
you can read more about it here:
How to get real client IP behind Cloudflare in Laravel / PHP
I have PHP code that is supposed to detect a user's IP address. The code below returns an IP address, but the IP address is sometimes a local network IP (e.g. 10.0.0.1) and not a public IP. How can I ensure that I always get the public IP? Thanks. BTW, this code is from another StackOverflow post. Also, this code is used in a website that is being accessed over the internet from a completely separate network than that of my Apache web server.
if (isset($_SERVER["HTTP_CLIENT_IP"])){
$ip = $_SERVER["HTTP_CLIENT_IP"];
} elseif (isset($_SERVER["HTTP_X_FORWARDED_FOR"])){
$ip = $_SERVER["HTTP_X_FORWARDED_FOR"];
} elseif (isset($_SERVER["HTTP_X_FORWARDED"])){
$ip = $_SERVER["HTTP_X_FORWARDED"];
} elseif (isset($_SERVER["HTTP_FORWARDED_FOR"])){
$ip = $_SERVER["HTTP_FORWARDED_FOR"];
} elseif (isset($_SERVER["HTTP_FORWARDED"])){
$ip = $_SERVER["HTTP_FORWARDED"];
} else {
$ip = $_SERVER["REMOTE_ADDR"];
}
Eliminate all the if(){} else{} code, and just request the REMOTE_ADDR:
$ip = $_SERVER["REMOTE_ADDR"];
It's the only reliable source of a user's remote IP address as all the other _SERVER keys can be masked by the client.
If the IP address is still local (and youre SURE that you're dealing with clients not in the networks or on a local VPN) the you may be dealing with either a server caching system (Squid Proxy eg). Have a look at http://www.nineteenlabs.com/2007/08/24/high-anonymous-proxy-squid-25/
I've tried to get the IP address of client browsing the site using
$_SERVER['REMOTE_ADDR'], but I'm not getting the exact IP of the client
please help me... Thanks
$_SERVER['REMOTE_ADDR']
is the best you will get really.
There are various other headers that can be sent by the client (HTTP_FORWARDED_FOR et al.) - see this question for a complete overview - but these can be freely manipulated by the client and are not to be deemed reliable.
// Get the real client IP
function getIP() {
if (getenv("HTTP_CLIENT_IP") && strcasecmp(getenv("HTTP_CLIENT_IP"), "unknown")) {
$ip = getenv("HTTP_CLIENT_IP");
} else if (getenv("HTTP_X_FORWARDED_FOR") && strcasecmp(getenv("HTTP_X_FORWARDED_FOR"), "unknown")) {
$ip = getenv("HTTP_X_FORWARDED_FOR");
} else if (getenv("REMOTE_ADDR") && strcasecmp(getenv("REMOTE_ADDR"), "unknown")) {
$ip = getenv("REMOTE_ADDR");
} else if (isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], "unknown")) {
$ip = $_SERVER['REMOTE_ADDR'];
} else {
$ip = "unknown";
}
return($ip);
}
The IP address that the server sees is the client's public-facing IP address. If they're behind a NAT router, they will have a different address inside their network.
If you run ipconfig (Windows) or ifconfig (Unix-y systems) on the client machine, you'll get their local IP address. If it's in the 192.168.x.x or 10.x.x.x ranges, they're behind a NAT router and the internet will see them coming from a different address.
If user is behind a proxy you will be getting the IP of the proxy. The user IP would be then either one of these (you'd need to check both):
$_SERVER['HTTP_CLIENT_IP']
$_SERVER['HTTP_X_FORWARDED_FOR']
If any of them is set, then user is behind a proxy (unless he is faking those headers) and you should use them as the source IP.
Else use $_SERVER['REMOTE_ADDR'].