How can I get the client IP address using PHP?
I want to keep record of the user who logged into my website through his/her IP address.
Whatever you do, make sure not to trust data sent from the client. $_SERVER['REMOTE_ADDR']
contains the real IP address of the connecting party. That is the most reliable value you can find.
However, they can be behind a proxy server in which case the proxy may have set the $_SERVER['HTTP_X_FORWARDED_FOR']
, but this value is easily spoofed. For example, it can be set by someone without a proxy, or the IP can be an internal IP from the LAN behind the proxy.
This means that if you are going to save the $_SERVER['HTTP_X_FORWARDED_FOR']
, make sure you also save the $_SERVER['REMOTE_ADDR']
value. E.g. by saving both values in different fields in your database.
If you are going to save the IP to a database as a string, make sure you have space for at least 45 characters. IPv6 is here to stay and those addresses are larger than the older IPv4 addresses.
(Note that IPv6 usually uses 39 characters at most but there is also a special IPv6 notation for IPv4 addresses which in its full form can be up to 45 characters. So if you know what you are doing you can use 39 characters, but if you just want to set and forget it, use 45).
$_SERVER['REMOTE_ADDR']
may not actually contain real client IP addresses, as it will give you a proxy address for clients connected through a proxy, for example. That may well be what you really want, though, depending what your doing with the IPs. Someone's private RFC1918 address may not do you any good if you're say, trying to see where your traffic is originating from, or remembering what IP the user last connected from, where the public IP of the proxy or NAT gateway might be the more appropriate to store.
There are several HTTP headers like X-Forwarded-For
which may or may not be set by various proxies. The problem is that those are merely HTTP headers which can be set by anyone. There's no guarantee about their content. $_SERVER['REMOTE_ADDR']
is the actual physical IP address that the web server received the connection from and that the response will be sent to. Anything else is just arbitrary and voluntary information. There's only one scenario in which you can trust this information: you are controlling the proxy that sets this header. Meaning only if you know 100% where and how the header was set should you heed it for anything of importance.
Having said that, here's some sample code:
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
$ip = $_SERVER['HTTP_CLIENT_IP'];
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
} else {
$ip = $_SERVER['REMOTE_ADDR'];
}
Editor's note: Using the above code has security implications. The client can set all HTTP header information (ie. $_SERVER['HTTP_...
) to any arbitrary value it wants. As such it's far more reliable to use $_SERVER['REMOTE_ADDR']
, as this cannot be set by the user.
From: http://roshanbh.com.np/2007/12/getting-real-ip-address-in-php.html
X-Forwarded-For
or the Client-IP
header to any arbitrary value it wants. Unless you have a trusted reverse proxy, you shouldn't use any of those values.
X-Forwarded-For
may contain multiple IP addresses, separated by a comma; and should really be `parsed' rather than taken at face value (AFAIK, it almost never contains a single IP).
echo $_SERVER['REMOTE_ADDR'];
http://php.net/manual/en/reserved.variables.server.php
::1
is the IPv6 equivalent of 127.0.0.1
Here is a cleaner code sample of a good way to get the IP address of the user.
$ip = $_SERVER['HTTP_CLIENT_IP']
? $_SERVER['HTTP_CLIENT_IP']
: ($_SERVER['HTTP_X_FORWARDED_FOR']
? $_SERVER['HTTP_X_FORWARDED_FOR']
: $_SERVER['REMOTE_ADDR']);
Here is a shorter version that uses the elvis operator:
$_SERVER['HTTP_CLIENT_IP']
? : ($_SERVER['HTTP_X_FORWARDED_FOR']
? : $_SERVER['REMOTE_ADDR']);
Here is a version that uses isset to remove notices (thank you, @shasi kanth):
$ip = isset($_SERVER['HTTP_CLIENT_IP'])
? $_SERVER['HTTP_CLIENT_IP']
: (isset($_SERVER['HTTP_X_FORWARDED_FOR'])
? $_SERVER['HTTP_X_FORWARDED_FOR']
: $_SERVER['REMOTE_ADDR']);
$ip = $_SERVER['HTTP_CLIENT_IP']?$_SERVER['HTTP_CLIENT_IP']:($_SERVER['HTTP_X_FORWARDED_FOR']?$_SERVER['HTTP_X_FORWARDED_FOR']:$_SERVER['REMOTE_ADDR']);
Nevertheless, very good one.
It should be contained in the $_SERVER['REMOTE_ADDR']
variable.
My favourite solution is the way Zend Framework 2 uses. It also considers the $_SERVER
properties HTTP_X_FORWARDED_FOR
, HTTP_CLIENT_IP
, REMOTE_ADDR
but it declares a class for it to set some trusted proxies and it returns one IP address not an array. I think this is the solution that comes closest to it:
class RemoteAddress
{
/**
* Whether to use proxy addresses or not.
*
* As default this setting is disabled - IP address is mostly needed to increase
* security. HTTP_* are not reliable since can easily be spoofed. It can be enabled
* just for more flexibility, but if user uses proxy to connect to trusted services
* it's his/her own risk, only reliable field for IP address is $_SERVER['REMOTE_ADDR'].
*
* @var bool
*/
protected $useProxy = false;
/**
* List of trusted proxy IP addresses
*
* @var array
*/
protected $trustedProxies = array();
/**
* HTTP header to introspect for proxies
*
* @var string
*/
protected $proxyHeader = 'HTTP_X_FORWARDED_FOR';
// [...]
/**
* Returns client IP address.
*
* @return string IP address.
*/
public function getIpAddress()
{
$ip = $this->getIpAddressFromProxy();
if ($ip) {
return $ip;
}
// direct IP address
if (isset($_SERVER['REMOTE_ADDR'])) {
return $_SERVER['REMOTE_ADDR'];
}
return '';
}
/**
* Attempt to get the IP address for a proxied client
*
* @see http://tools.ietf.org/html/draft-ietf-appsawg-http-forwarded-10#section-5.2
* @return false|string
*/
protected function getIpAddressFromProxy()
{
if (!$this->useProxy
|| (isset($_SERVER['REMOTE_ADDR']) && !in_array($_SERVER['REMOTE_ADDR'], $this->trustedProxies))
) {
return false;
}
$header = $this->proxyHeader;
if (!isset($_SERVER[$header]) || empty($_SERVER[$header])) {
return false;
}
// Extract IPs
$ips = explode(',', $_SERVER[$header]);
// trim, so we can compare against trusted proxies properly
$ips = array_map('trim', $ips);
// remove trusted proxy IPs
$ips = array_diff($ips, $this->trustedProxies);
// Any left?
if (empty($ips)) {
return false;
}
// Since we've removed any known, trusted proxy servers, the right-most
// address represents the first IP we do not know about -- i.e., we do
// not know if it is a proxy server, or a client. As such, we treat it
// as the originating IP.
// @see http://en.wikipedia.org/wiki/X-Forwarded-For
$ip = array_pop($ips);
return $ip;
}
// [...]
}
See the full code here: https://raw.githubusercontent.com/zendframework/zend-http/master/src/PhpEnvironment/RemoteAddress.php
$_SERVER
variable as it is and jumps over some defined and well-known proxy servers. That's all. If you think that the returning value is not safe, then check it or report a bug to the PHP developers.
There are different types of users behind the Internet, so we want to catch the IP address from different portions. Those are:
1. $_SERVER['REMOTE_ADDR']
- This contains the real IP address of the client. That is the most reliable value you can find from the user.
2. $_SERVER['REMOTE_HOST']
- This will fetch the host name from which the user is viewing the current page. But for this script to work, hostname lookups on inside httpd.conf must be configured.
3. $_SERVER['HTTP_CLIENT_IP']
- This will fetch the IP address when the user is from shared Internet services.
4. $_SERVER['HTTP_X_FORWARDED_FOR']
- This will fetch the IP address from the user when he/she is behind the proxy.
So we can use this following combined function to get the real IP address from users who are viewing in diffrent positions,
// Function to get the user IP address
function getUserIP() {
$ipaddress = '';
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 if(isset($_SERVER['REMOTE_ADDR']))
$ipaddress = $_SERVER['REMOTE_ADDR'];
else
$ipaddress = 'UNKNOWN';
return $ipaddress;
}
The following is the most advanced method I have found, and I have already tried some others in the past. It is valid to ensure to get the IP address of a visitor (but please note that any hacker could falsify the IP address easily).
function get_ip_address() {
// Check for shared Internet/ISP IP
if (!empty($_SERVER['HTTP_CLIENT_IP']) && validate_ip($_SERVER['HTTP_CLIENT_IP'])) {
return $_SERVER['HTTP_CLIENT_IP'];
}
// Check for IP addresses passing through proxies
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
// Check if multiple IP addresses exist in var
if (strpos($_SERVER['HTTP_X_FORWARDED_FOR'], ',') !== false) {
$iplist = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
foreach ($iplist as $ip) {
if (validate_ip($ip))
return $ip;
}
}
else {
if (validate_ip($_SERVER['HTTP_X_FORWARDED_FOR']))
return $_SERVER['HTTP_X_FORWARDED_FOR'];
}
}
if (!empty($_SERVER['HTTP_X_FORWARDED']) && validate_ip($_SERVER['HTTP_X_FORWARDED']))
return $_SERVER['HTTP_X_FORWARDED'];
if (!empty($_SERVER['HTTP_X_CLUSTER_CLIENT_IP']) && validate_ip($_SERVER['HTTP_X_CLUSTER_CLIENT_IP']))
return $_SERVER['HTTP_X_CLUSTER_CLIENT_IP'];
if (!empty($_SERVER['HTTP_FORWARDED_FOR']) && validate_ip($_SERVER['HTTP_FORWARDED_FOR']))
return $_SERVER['HTTP_FORWARDED_FOR'];
if (!empty($_SERVER['HTTP_FORWARDED']) && validate_ip($_SERVER['HTTP_FORWARDED']))
return $_SERVER['HTTP_FORWARDED'];
// Return unreliable IP address since all else failed
return $_SERVER['REMOTE_ADDR'];
}
/**
* Ensures an IP address is both a valid IP address and does not fall within
* a private network range.
*/
function validate_ip($ip) {
if (strtolower($ip) === 'unknown')
return false;
// Generate IPv4 network address
$ip = ip2long($ip);
// If the IP address is set and not equivalent to 255.255.255.255
if ($ip !== false && $ip !== -1) {
// Make sure to get unsigned long representation of IP address
// due to discrepancies between 32 and 64 bit OSes and
// signed numbers (ints default to signed in PHP)
$ip = sprintf('%u', $ip);
// Do private network range checking
if ($ip >= 0 && $ip <= 50331647)
return false;
if ($ip >= 167772160 && $ip <= 184549375)
return false;
if ($ip >= 2130706432 && $ip <= 2147483647)
return false;
if ($ip >= 2851995648 && $ip <= 2852061183)
return false;
if ($ip >= 2886729728 && $ip <= 2887778303)
return false;
if ($ip >= 3221225984 && $ip <= 3221226239)
return false;
if ($ip >= 3232235520 && $ip <= 3232301055)
return false;
if ($ip >= 4294967040)
return false;
}
return true;
}
The answer is to use $_SERVER
variable. For example, $_SERVER["REMOTE_ADDR"]
would return the client's IP address.
A quick solution (error free)
function getClientIP():string
{
$keys=array('HTTP_CLIENT_IP','HTTP_X_FORWARDED_FOR','HTTP_X_FORWARDED','HTTP_FORWARDED_FOR','HTTP_FORWARDED','REMOTE_ADDR');
foreach($keys as $k)
{
if (!empty($_SERVER[$k]) && filter_var($_SERVER[$k], FILTER_VALIDATE_IP))
{
return $_SERVER[$k];
}
}
return "UNKNOWN";
}
X-FORWARDED-FOR: fakeip
HTTP headers
strtok($k, ';,')
); 2. HTTP_X_FORWARDED
does not exist; 3. The usage of HTTP_FORWARDED
here (standardized) will always fail the filter_var test because it uses it's own syntax (ie, for=1.1.1.1;by=1.1.1.0
).
function get_client_ip()
{
foreach (array(
'HTTP_CLIENT_IP',
'HTTP_X_FORWARDED_FOR',
'HTTP_X_FORWARDED',
'HTTP_X_CLUSTER_CLIENT_IP',
'HTTP_FORWARDED_FOR',
'HTTP_FORWARDED',
'REMOTE_ADDR') as $key) {
if (array_key_exists($key, $_SERVER)) {
foreach (explode(',', $_SERVER[$key]) as $ip) {
$ip = trim($ip);
if ((bool) filter_var($ip, FILTER_VALIDATE_IP,
FILTER_FLAG_IPV4 |
FILTER_FLAG_NO_PRIV_RANGE |
FILTER_FLAG_NO_RES_RANGE)) {
return $ip;
}
}
}
}
return null;
}
Or the compressed version:
function get_ip() {
foreach (array('HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR') as $key) {
if (array_key_exists($key, $_SERVER) === true) {
foreach (array_map('trim', explode(',', $_SERVER[$key])) as $ip) {
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false) {
return $ip;
}
}
}
}
}
Here's a bit of code that should pick a valid IP by checking through various sources.
First, it checks if 'REMOTE_ADDR' is a public IP or not (and not one of your trusted reverse proxies), then goes through one of the HTTP headers until it finds a public IP and returns it. (PHP 5.2+)
It should be reliable as long as the reverse proxy is trusted or the server is directly connected with the client.
//Get client's IP or null if nothing looks valid
function ip_get($allow_private = false)
{
//Place your trusted proxy server IPs here.
$proxy_ip = ['127.0.0.1'];
//The header to look for (Make sure to pick the one that your trusted reverse proxy is sending or else you can get spoofed)
$header = 'HTTP_X_FORWARDED_FOR'; //HTTP_CLIENT_IP, HTTP_X_FORWARDED, HTTP_FORWARDED_FOR, HTTP_FORWARDED
//If 'REMOTE_ADDR' seems to be a valid client IP, use it.
if(ip_check($_SERVER['REMOTE_ADDR'], $allow_private, $proxy_ip)) return $_SERVER['REMOTE_ADDR'];
if(isset($_SERVER[$header]))
{
//Split comma separated values [1] in the header and traverse the proxy chain backwards.
//[1] https://en.wikipedia.org/wiki/X-Forwarded-For#Format
$chain = array_reverse(preg_split('/\s*,\s*/', $_SERVER[$header]));
foreach($chain as $ip) if(ip_check($ip, $allow_private, $proxy_ip)) return $ip;
}
return null;
}
//Check for valid IP. If 'allow_private' flag is set to truthy, it allows private IP ranges as valid client IP as well. (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16)
//Pass your trusted reverse proxy IPs as $proxy_ip to exclude them from being valid.
function ip_check($ip, $allow_private = false, $proxy_ip = [])
{
if(!is_string($ip) || is_array($proxy_ip) && in_array($ip, $proxy_ip)) return false;
$filter_flag = FILTER_FLAG_NO_RES_RANGE;
if(!$allow_private)
{
//Disallow loopback IP range which doesn't get filtered via 'FILTER_FLAG_NO_PRIV_RANGE' [1]
//[1] https://www.php.net/manual/en/filter.filters.validate.php
if(preg_match('/^127\.$/', $ip)) return false;
$filter_flag |= FILTER_FLAG_NO_PRIV_RANGE;
}
return filter_var($ip, FILTER_VALIDATE_IP, $filter_flag) !== false;
}
As all others said before you can use $_SERVER['REMOTE_ADDR'];
to get the client IP address.
Also, if you need more information about a user, you can use this:
<?php
$ip = '0.0.0.0';
$ip = $_SERVER['REMOTE_ADDR'];
$clientDetails = json_decode(file_get_contents("http://ipinfo.io/$ip/json"));
echo "You're logged in from: <b>" . $clientDetails->country . "</b>";
?>
Client's more specific information goes in $clientDetails. You can fetch JSON items stored in $clientDetails variable this way: $clientDetails->PostalCode/hostname/region/loc...
I'm using ipinfo.io to get extra information.
I like this codesnippet:
function getClientIP() {
if (isset($_SERVER)) {
if (isset($_SERVER["HTTP_X_FORWARDED_FOR"]))
return $_SERVER["HTTP_X_FORWARDED_FOR"];
if (isset($_SERVER["HTTP_CLIENT_IP"]))
return $_SERVER["HTTP_CLIENT_IP"];
return $_SERVER["REMOTE_ADDR"];
}
if (getenv('HTTP_X_FORWARDED_FOR'))
return getenv('HTTP_X_FORWARDED_FOR');
if (getenv('HTTP_CLIENT_IP'))
return getenv('HTTP_CLIENT_IP');
return getenv('REMOTE_ADDR');
}
getenv
give you the same thing as $_SERVER
?
$ip = "";
if (!empty($_SERVER["HTTP_CLIENT_IP"]))
{
// Check for IP address from shared Internet
$ip = $_SERVER["HTTP_CLIENT_IP"];
}
elseif (!empty($_SERVER["HTTP_X_FORWARDED_FOR"]))
{
// Check for the proxy user
$ip = $_SERVER["HTTP_X_FORWARDED_FOR"];
}
else
{
$ip = $_SERVER["REMOTE_ADDR"];
}
echo $ip;
$ip
in line 1. If all conditions fail, then also the $ip = $_SERVER['REMOTE_ADDR']
would run.
This is the method that I use, and it validates an IPv4 input:
// Get user IP address
if ( isset($_SERVER['HTTP_CLIENT_IP']) && ! empty($_SERVER['HTTP_CLIENT_IP'])) {
$ip = $_SERVER['HTTP_CLIENT_IP'];
} elseif ( isset($_SERVER['HTTP_X_FORWARDED_FOR']) && ! empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
} else {
$ip = (isset($_SERVER['REMOTE_ADDR'])) ? $_SERVER['REMOTE_ADDR'] : '0.0.0.0';
}
$ip = filter_var($ip, FILTER_VALIDATE_IP);
$ip = ($ip === false) ? '0.0.0.0' : $ip;
Well, this can be simply done by using the GLOBAL
variable named as $_SERVER
.
The $_SERVER
is an array which has the attribute name REMOTE_ADDR
.
Just assign it like this:
$userIp = $_SERVER['REMOTE_ADDR'];
Or use it directly like echo $_SERVER['REMOTE_ADDR'];
or echo ($_SERVER['REMOTE_ADDR']);
.
One of these :
$ip = $_SERVER['REMOTE_ADDR'];
$ip = $_SERVER['HTTP_CLIENT_IP'];
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
$ip = $_SERVER['HTTP_X_FORWARDED'];
$ip = $_SERVER['HTTP_FORWARDED_FOR'];
$ip = $_SERVER['HTTP_FORWARDED'];
This function is compact and you can use it everywhere. But!
Don't forget this! In this type of functions or code blocks there is not a guarantee for recording the user's real IP address because some users can use a proxy or another secure gateway for be invisible or cannot tracking
PHP function:
function GetIP()
{
if ( getenv("HTTP_CLIENT_IP") ) {
$ip = getenv("HTTP_CLIENT_IP");
} elseif ( getenv("HTTP_X_FORWARDED_FOR") ) {
$ip = getenv("HTTP_X_FORWARDED_FOR");
if ( strstr($ip, ',') ) {
$tmp = explode(',', $ip);
$ip = trim($tmp[0]);
}
} else {
$ip = getenv("REMOTE_ADDR");
}
return $ip;
}
Usage:
$IP = GetIP();
or directly GetIP();
Safe and warnings-aware snippet for getting the IP address:
$ip = filter_input(INPUT_SERVER, 'HTTP_CLIENT_IP', FILTER_VALIDATE_IP)
?: filter_input(INPUT_SERVER, 'HTTP_X_FORWARDED_FOR', FILTER_VALIDATE_IP)
?: $_SERVER['REMOTE_ADDR']
?? '0.0.0.0'; // Or other value fits "not defined" in your logic
The following function determine all possibilities and return the values in a comma separated format (ip, ip, etc.).
It has also an optional validation function as (first parameter that disabled by default) to validate the IP address against (private range and reserved range).
<?php
echo GetClientIP(true);
function GetClientIP($validate = False) {
$ipkeys = array(
'REMOTE_ADDR',
'HTTP_CLIENT_IP',
'HTTP_X_FORWARDED_FOR',
'HTTP_X_FORWARDED',
'HTTP_FORWARDED_FOR',
'HTTP_FORWARDED',
'HTTP_X_CLUSTER_CLIENT_IP'
);
/*
Now we check each key against $_SERVER if containing such value
*/
$ip = array();
foreach ($ipkeys as $keyword) {
if (isset($_SERVER[$keyword])) {
if ($validate) {
if (ValidatePublicIP($_SERVER[$keyword])) {
$ip[] = $_SERVER[$keyword];
}
}
else{
$ip[] = $_SERVER[$keyword];
}
}
}
$ip = ( empty($ip) ? 'Unknown' : implode(", ", $ip) );
return $ip;
}
function ValidatePublicIP($ip){
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
return true;
}
else {
return false;
}
}
Try this one:
$_SERVER['REMOTE_ADDR'];
<?php
/**
* Function to get the client ip address
*
* @return string The Ip address
*/
function getIp(): string {
if (! empty($_SERVER['HTTP_CLIENT_IP'])) {
return $_SERVER['HTTP_CLIENT_IP'];
}
if (! empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
return $_SERVER['HTTP_X_FORWARDED_FOR'];
}
return $_SERVER['REMOTE_ADDR'] ?? '?';
}
Even smaller
/**
* Function to get the client ip address
*
* @return string The Ip address
*/
function getIp(): string {
return $_SERVER['HTTP_CLIENT_IP'] ?? $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR'] ?? '';
}
This function should work as expected
function Get_User_Ip()
{
$IP = false;
if (getenv('HTTP_CLIENT_IP'))
{
$IP = getenv('HTTP_CLIENT_IP');
}
else if(getenv('HTTP_X_FORWARDED_FOR'))
{
$IP = getenv('HTTP_X_FORWARDED_FOR');
}
else if(getenv('HTTP_X_FORWARDED'))
{
$IP = getenv('HTTP_X_FORWARDED');
}
else if(getenv('HTTP_FORWARDED_FOR'))
{
$IP = getenv('HTTP_FORWARDED_FOR');
}
else if(getenv('HTTP_FORWARDED'))
{
$IP = getenv('HTTP_FORWARDED');
}
else if(getenv('REMOTE_ADDR'))
{
$IP = getenv('REMOTE_ADDR');
}
//If HTTP_X_FORWARDED_FOR == server ip
if((($IP) && ($IP == getenv('SERVER_ADDR')) && (getenv('REMOTE_ADDR')) || (!filter_var($IP, FILTER_VALIDATE_IP))))
{
$IP = getenv('REMOTE_ADDR');
}
if($IP)
{
if(!filter_var($IP, FILTER_VALIDATE_IP))
{
$IP = false;
}
}
else
{
$IP = false;
}
return $IP;
}
Just on this, and I'm surprised it hasn't been mentioned yet, is to get the correct IP addresses of those sites that are nestled behind the likes of CloudFlare infrastructure. It will break your IP addresses, and give them all the same value. Fortunately they have some server headers available too. Instead of me rewriting what's already been written, have a look here for a more concise answer, and yes, I went through this process a long while ago too. https://stackoverflow.com/a/14985633/1190051
Here's a simple one liner
$ip = $_SERVER['HTTP_X_FORWARDED_FOR']?: $_SERVER['HTTP_CLIENT_IP']?: $_SERVER['REMOTE_ADDR'];
EDIT:
Above code may return reserved addresses (like 10.0.0.1), a list of addresses of all proxy servers on the way, etc. To handle these cases use the following code:
function valid_ip($ip) {
// for list of reserved IP addresses, see https://en.wikipedia.org/wiki/Reserved_IP_addresses
return $ip && substr($ip, 0, 4) != '127.' && substr($ip, 0, 4) != '127.' && substr($ip, 0, 3) != '10.' && substr($ip, 0, 2) != '0.' ? $ip : false;
}
function get_client_ip() {
// using explode to get only client ip from list of forwarders. see https://en.wikipedia.org/wiki/X-Forwarded-For
return
@$_SERVER['HTTP_X_FORWARDED_FOR'] ? explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'], 2)[0] :
@$_SERVER['HTTP_CLIENT_IP'] ? explode(',', $_SERVER['HTTP_CLIENT_IP'], 2)[0] :
valid_ip(@$_SERVER['REMOTE_ADDR']) ?:
'UNKNOWN';
}
echo get_client_ip();
In PHP the last option to get the public IP should always be $_SERVER["REMOTE_ADDR"]
for way too many security reasons.
Here is a workaround to get the validated IP address of the client.
public static function getPublicIP() : string
{
$realIP = "Invalid IP Address";
$activeHeaders = [];
$headers = [
"HTTP_CLIENT_IP",
"HTTP_PRAGMA",
"HTTP_XONNECTION",
"HTTP_CACHE_INFO",
"HTTP_XPROXY",
"HTTP_PROXY",
"HTTP_PROXY_CONNECTION",
"HTTP_VIA",
"HTTP_X_COMING_FROM",
"HTTP_COMING_FROM",
"HTTP_X_FORWARDED_FOR",
"HTTP_X_FORWARDED",
"HTTP_X_CLUSTER_CLIENT_IP",
"HTTP_FORWARDED_FOR",
"HTTP_FORWARDED",
"ZHTTP_CACHE_CONTROL",
"REMOTE_ADDR" #this should be the last option
];
#Find active headers
foreach ($headers as $key)
{
if (array_key_exists($key, $_SERVER))
{
$activeHeaders[$key] = $_SERVER[$key];
}
}
#Reemove remote address since we got more options to choose from
if(count($activeHeaders) > 1)
{
unset($activeHeaders["REMOTE_ADDR"]);
}
#Pick a random item now that we have a secure way.
$realIP = $activeHeaders[array_rand($activeHeaders)];
#Validate the public IP
if (filter_var($realIP, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4))
{
return $realIP;
}
return $realIP;
}
As you can see here $_SERVER["REMOTE_ADDR"] is our last option to the IP. After receiving the IP we also validate the IP to ensure quality and security.
If you don't like to use if-else/switch statements, then the following solution is for you.
function get_client_ip()
{
$fields = array(
'HTTP_CF_CONNECTING_IP',
'HTTP_X_SUCURI_CLIENTIP',
'HTTP_CLIENT_IP',
'HTTP_X_FORWARDED_FOR',
'HTTP_X_FORWARDED',
'HTTP_FORWARDED_FOR',
'HTTP_FORWARDED',
'REMOTE_ADDR',
// more custom fields
);
foreach ($fields as $ip_field) {
if (!empty($_SERVER[$ip_field])) {
return $_SERVER[$ip_field];
}
}
return null;
}
You can use below function
function get_client_ip() {
$ipaddress = '';
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_FORWARDED_FOR']))
$ipaddress = $_SERVER['HTTP_FORWARDED_FOR'];
else if(isset($_SERVER['HTTP_FORWARDED']))
$ipaddress = $_SERVER['HTTP_FORWARDED'];
else if(isset($_SERVER['REMOTE_ADDR']))
$ipaddress = $_SERVER['REMOTE_ADDR'];
else
$ipaddress = 'UNKNOWN';
return $ipaddress;
}
Like the following?
if (($ip=filter_input(INPUT_SERVER, 'REMOTE_ADDR', validate_ip)) === false or empty($ip)) {
exit;
}
echo $ip;
PS
if (($ip=filter_input(INPUT_SERVER, 'REMOTE_ADDR', FILTER_VALIDATE_IP|FILTER_FLAG_NO_PRIV_RANGE|FILTER_FLAG_NO_RES_RANGE)) === false) {
header('HTTP/1.0 400 Bad Request');
exit;
}
All headers beginning with 'HTTP_' or 'X-' may be spoofed, respectively is user defined. If you want to keep track, use cookies, etc.
Success story sharing
REMOTE_ADDR
might not contain the real IP of the TCP connection. This entirely depends on your SAPI. Ensure that your SAPI is properly configured such that$_SERVER['REMOTE_ADDR']
actually returns the IP of the TCP connection. Failing that might give rise to some serious vulnerabilities, for example, StackExchange used to grant admin access by checkingREMOTE_ADDR
to see if it matches "localhost", unfortunately the SAPI's config...........................................................................HTTP_X_FORWARDED_FOR
as input) which allows non-admins to gain admin access by altering theHTTP_X_FORWARDED_FOR
header. Also see blog.ircmaxell.com/2012/11/anatomy-of-attack-how-i-hacked.html