How to get the client IP address in PHP
Asked Answered
A

37

1435

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.

Add answered 9/6, 2010 at 4:50 Comment(5)
See RFC6302 on recommendations about what to log, and specifically nowadays remember to log the port and not only the address.Sauna
A word of caution for those tracking users, in several regions of the globe ISPS are using CGNAT which makes it much more complicated to trust a mere IP addressSegal
function getUserIpAddr(){ 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']; } return $ip; }Tit
You should use Abstract IP detection. The value is that it will let you know if the IP is behind a proxy or a VPN which I think is important. They have a PHP snippet you can copy your request from.Clung
Just in case of php Laravel framework a concise test is: * ($request->getClientIp() ?? $request->Ip() ) *Meltage
B
1554

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).

Blackcock answered 9/6, 2010 at 5:15 Comment(7)
Nice answer! I am already using $_SERVER['REMOTE_ADDR'] for my server, and I like that you included another way, plus the benefits and disadvantages.Hanforrd
Note: 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 checking REMOTE_ADDR to see if it matches "localhost", unfortunately the SAPI's config...........................................................................Dropout
..........................................................................had a vulnerability (it takes HTTP_X_FORWARDED_FOR as input) which allows non-admins to gain admin access by altering the HTTP_X_FORWARDED_FOR header. Also see blog.ircmaxell.com/2012/11/anatomy-of-attack-how-i-hacked.htmlDropout
@EmilVikström I Tried echoing everything - $_SERVER["HTTP_CLIENT_IP"]; $_SERVER['REMOTE_ADDR']; $_SERVER['HTTP_X_FORWARDED_FOR']; $_SERVER['HTTP_X_FORWARDED']; $_SERVER['HTTP_FORWARDED_FOR']; $_SERVER['HTTP_FORWARDED']; and $_SERVER['HTTP_X_CLUSTER_CLIENT_IP']; - The ONLY ONE which is returning some IP value from BOTH - Direct access through my browser and through a Proxy server is the REMOTE_ADDR var. Rest all 6 vars are coming as BLANKS. What is being missed here? .....Letters
.....@Dropout - any comments from your side as well on the aboveLetters
Explanation is always a plus, nice answer. I've always trusted $_SERVER['REMOTE_ADDR'] and it gets it done!Andre
key point: DON'T USE IPs FOR PHP SECURITY. NEVER EVER., if you need to restrict IP access, do that on the network level.Insufflate
A
553

$_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

Aaronson answered 11/9, 2008 at 4:1 Comment(11)
Do NOT use the above code unless you know EXACTLY what it does! I've seen MASSIVE security holes due to this. The client can set the 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.Ruebenrueda
With regards to Janoszen's comment, one option is PHP's filter_var($_SERVER['REMOTE_ADDR'], FILTER_VALIDATE_IP).Bionomics
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).Sweet
@Bionomics that is a reasonable thing to do,and it will make it more reliable, but unfortunately it would still allow spoofing.Udelle
Don't use this code above. It can be fake from HTTP_X_FORWARDED_FOR where come from fake address using proxy. Please don't use code above!Myrticemyrtie
@TimSeguine No, remote_addr takes the ip from TCP. One could not spoof an ip over tcp as it needs a handshake .Parallelepiped
@GeoC. You are right. If I remember my frame of mind, I just meant that suggestion doesn't help you decide if it is actually the client. I don't think I was thinking about forged packets, but I honestly don't remember.Udelle
@TimSeguine you are right, too. It doesn't help you decide if it's the client. It could be a 6to4 gateway, or a NAT gateway, or a TOR proxy, or any number of devices or proxies that will not supply a valid X-Forwarded-For: header, and even if they do, there's no way to validate that it's a correct one, since it's unreliable data. That's why this type of data should only be used for analytics, where accurate-ish numbers are OK.Aaronson
For a site of scaled size, there will be load balancers and/or reverse proxies in front of the web application servers. You have to configure these load balancers or proxies to remove any external X-Forwarded-For header, and instead insert their own of what IP address they see for the connecting client.Deena
Additionally, if your application server is fronted by a CDN (Akamai, CloudFlare, or whatever) then the remote IP you see can be from the CDN. You will then have to make sure the CDN inserts a header you can trust, and that cannot be spoofed by malicious clients poking directly at your server.Deena
To lostphilosopher's comment. You do not need to validate the format of $_SERVER['REMOTE_ADDR']. It is taken from the server connection, so not controllable by an attacker. You cant spoof this value either (or you wouldnt be able to complete the TCP handshake). And the security issues Janos Pasztor mentions are not primarily input validation flaws like XSS... a more pressing problem would be testing for '127.0.0.1' to present an admin panel to local users. With the above code, an attacker could put 127.0.0.1 in 'HTTP_CLIENT_IP' header to bypass that restriction and gain access.Preece
S
239
echo $_SERVER['REMOTE_ADDR'];

http://php.net/manual/en/reserved.variables.server.php

Soult answered 9/6, 2010 at 4:51 Comment(7)
Actually i want to know the IP address of the Client who is using my website. Not the server IP addresss where my pages have uploaded or executing.. Please help me.Add
@Anup Prakash This is it – hence the "REMOTE" (from the perspective of the script).Hackworth
Because you're on localhost ;)Spectacled
@SiKni8 ::1 is the IPv6 equivalent of 127.0.0.1Raine
@CamiloMartin you just taught me something. cool!Stormproof
This returns the proxy IP address.Stratiform
For those who get ::1, see #10517871Leslie
T
162

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']);
Traceytrachea answered 8/10, 2014 at 16:20 Comment(16)
Always remember to sanitize any input that could have been modified by the user. This is one of those times.Traceytrachea
I believe the code is missing some expression and the order of priorities is inversed, so it should be like this: $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.Annual
Good catch. I have adjusted the post. Thank you!Traceytrachea
As a function, with filtering: function getUserIP() { $client = @$_SERVER['HTTP_CLIENT_IP']; $forward = @$_SERVER['HTTP_X_FORWARDED_FOR']; return filter_var($client, FILTER_VALIDATE_IP) ? $client : filter_var($forward, FILTER_VALIDATE_IP) ? $forward : $_SERVER['REMOTE_ADDR']; }Radiology
You're testing for one header and return another... It looks like a mistake.Sheepdog
@Radiology Stop editing this if you don't know what your doing.Traceytrachea
I did, i just goofed my first edit. fyi, that doesn't work, but do what you like. better working code is $ip = $_SERVER['HTTP_CLIENT_IP']?$_SERVER['HTTP_CLIENT_IP']:($_SERVER['HTTP_X_FORWARDE‌​D_FOR']?$_SERVER['HTTP_X_FORWARDED_FOR']:$_SERVER['REMOTE_ADDR']);Radiology
@Radiology you realize that is the same code that I have in the post right?Traceytrachea
Not exactly, you have bad characters in your post. i tried to comment that. maybe i missed it, maybe you did. i wasn't trying to hurt anything, but one cannot copy and paste your code as is. or at least i came across this problem.Radiology
works fine in phpstorm what IDE are you using?Traceytrachea
Just added isset() to remove notices: $ip = isset($_SERVER['HTTP_CLIENT_IP'])?$_SERVER['HTTP_CLIENT_IP']:isset($_SERVER['HTTP_X_FORWARDE‌​D_FOR'])?$_SERVER['HTTP_X_FORWARDED_FOR']:$_SERVER['REMOTE_ADDR'];Ellen
dude, your isset version is full of white spaces and syntax errors where there should be none. Just copy the version, and paste it into phptester.netAutohypnosis
As pointed out by someone, all the three examples ARE FULL OF HIDDEN CHARACTERS (U+200C and U+200B). Do not paste them in your php script or you will get PARSE ERRORS. If you want to see all the hidden characters, paste those lines here: soscisurvey.de/tools/view-chars.php . Please clean up that code!Housebroken
I had to replace ?: by ?? and it solve the undefined index problème $_SERVER['HTTP_CLIENT_IP'] ?? ($_SERVER['HTTP_X_FORWARDE‌​D_FOR'] ?? $_SERVER['REMOTE_ADDR']);Bilski
For me HTTP_X_FORWARDED_FOR is the server's IP, and HTTP_CLIENT_IP is not set. So this doesn't work. Only REMOTE_ADDR has the user's IP.Recruitment
with php 7.x, you can use this syntax: $ip = $_SERVER['HTTP_CLIENT_IP'] ?? $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['HTTP_X_REAL_IP'] ?? $_SERVER['REMOTE_ADDR'];Uropygium
D
107

It should be contained in the $_SERVER['REMOTE_ADDR'] variable.

Distributee answered 11/9, 2008 at 3:38 Comment(0)
P
65

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

Pitchfork answered 7/7, 2014 at 9:2 Comment(4)
Great answer! Using production tested code, developed and used in such a big framework is one of the best things you can do :)Switcheroo
So wait zend doesnt filter anything? I should see something like: filter_var( $_SERVER['REMOTE_ADDR'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 );Soleure
@Hanoncs why would you do that ? it's very difficult to spoof the remote addressGrunberg
@Hanoncs I think you have to check the value after getting it with this class. Its not part of it's logic. It just gets the value from $_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.Pitchfork
G
56

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;
}
Guarantor answered 29/12, 2016 at 15:18 Comment(1)
Really easy to fake. I just tried on my own website with Requestly Chrome extension by setting Client-ip header to "111.111.111.111".Monocoque
V
39

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;
}
Vierno answered 29/1, 2015 at 14:37 Comment(6)
This is wrong. HTTP_CLIENT_IP is more unreliable than REMOTE_ADDR and the ip validation function is nonsense.Pontiff
@Pontiff . funny you say this but this is the only function set that actually worked for me behind a varnish box on a redirect page load. I give it big thumbs up .Canvas
The link is (effectively) broken.Flyer
link removed, it seems that the page disappeared. thank youVierno
Is it the best way to get the ip in 2019?Imprinting
I had no much time to develop in the last year but as far as I remember it was working in HA systems behind proxies and LbsVierno
C
30

The answer is to use $_SERVER variable. For example, $_SERVER["REMOTE_ADDR"] would return the client's IP address.

Cilka answered 9/6, 2010 at 4:56 Comment(0)
E
27

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";
}
Empanel answered 23/6, 2019 at 8:4 Comment(3)
warning, hackers can easily spoof the ip by sending fake X-FORWARDED-FOR: fakeip HTTP headersLaryngotomy
Ofcourse, but if you use NGINX, clientIP is usually on "HTTP_X_FORWARDED_FOR"Empanel
This may not work as you expect: 1. all headers here allow the use of commas and/or semicolons, so you have to tokenize the string (ie, 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).Greenery
B
15

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;
}
Belen answered 2/6, 2019 at 7:12 Comment(0)
S
14
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;
                }
            }
        }
    }
}

Steelyard answered 20/5, 2019 at 13:58 Comment(0)
S
12

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.

Shapiro answered 26/5, 2016 at 10:49 Comment(0)
P
11

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');
}
Phillida answered 10/5, 2011 at 12:0 Comment(4)
i mean what's the point.. doesn't getenv give you the same thing as $_SERVER ?Dropout
@Paceriermy guess would be older versions of PHP where $_SERVER was not yet available ;)Quirinal
@Johan Why not just return an array with all three?Chelsiechelsy
@nueverest Because a user doesn't access your site from 3 different IPs (usually). You want to return the one that applies to him.Stannum
S
11
$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;
Spoken answered 28/12, 2015 at 10:50 Comment(7)
How does the first snippet return the client's IP address? Seems to me it will echo the server's address.Achieve
Thanks Robin. Yes ,sometimes you won't get correct result. Please use second solution.Spoken
@MahfuzAhmed, can you tell, what does file_get_contents() do? and how do you get IP via file_get_contents()Manvel
file_get_contents is completely useless in here :)Love
Daksh B. It was a third party API. I have deleted that. as it's not a secure way.... If you need try the code above now.Spoken
What was the need to initialize $ip in line 1. If all conditions fail, then also the $ip = $_SERVER['REMOTE_ADDR'] would run.Manicotti
Yes Friend Yash Verma, you are right. And you know that to use a variable as output we must have to initialize for this here i have initial it as " " (blank String).Spoken
T
10

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;
Tiphane answered 13/2, 2014 at 14:47 Comment(1)
Thank you for allowing me to spoof my IP address by simply setting an HTTP header!Garage
M
10

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']);.

Manicotti answered 24/2, 2016 at 9:21 Comment(0)
K
10

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'];
Katmandu answered 11/6, 2019 at 10:14 Comment(1)
Documentation about server variables: php.net/manual/en/reserved.variables.server.phpSiberia
C
7

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();

Consol answered 19/6, 2016 at 0:9 Comment(0)
D
6

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;
  }
}
Dragonhead answered 27/4, 2016 at 1:28 Comment(0)
L
6

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
Lethia answered 5/10, 2018 at 8:14 Comment(0)
S
5

<?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'] ?? '';
}
Smutty answered 1/4, 2019 at 7:42 Comment(0)
J
4

Try this one:

 $_SERVER['REMOTE_ADDR'];
Jarret answered 26/5, 2015 at 10:49 Comment(3)
This was already mentioned few times and you answer doesn't add-up actually anything useful.Leontine
'you answer doesn't add-up actually anything useful' - not sure what you mean, it answers the question that was asked. how is that not useful?Mechanistic
because he is answering to 5 years old question and a lot of same and much better answers are already answered.Minute
U
3

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://mcmap.net/q/46243/-cloudflare-and-logging-visitor-ip-addresses-via-in-php

Undershot answered 29/4, 2019 at 4:51 Comment(0)
L
3

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;
}
Lheureux answered 29/6, 2022 at 1:59 Comment(0)
C
3

I used one of the other answers and added in some additional stuff like CloudFlare proxy and NGINX proxy detection.

/**
 * Gets, validates and returns the connecting client's IP
 */
function getClientIP(){

    // Get real visitor IP behind CloudFlare network
    if (!empty($_SERVER["HTTP_CF_CONNECTING_IP"]) && validateIP($_SERVER['HTTP_CF_CONNECTING_IP'])) {
        return $_SERVER["HTTP_CF_CONNECTING_IP"];
    }

    // Get real visitor IP behind NGINX proxy - https://easyengine.io/tutorials/nginx/forwarding-visitors-real-ip/
    if (!empty($_SERVER["HTTP_X_REAL_IP"]) && validateIP($_SERVER['HTTP_X_REAL_IP'])) {
        return $_SERVER["HTTP_X_REAL_IP"];
    }

    // Check for shared Internet/ISP IP
    if (!empty($_SERVER['HTTP_CLIENT_IP']) && validateIP($_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 (validateIP($ip))
                    return $ip;
            }
        }
        else {
            if (validateIP($_SERVER['HTTP_X_FORWARDED_FOR']))
                return $_SERVER['HTTP_X_FORWARDED_FOR'];
        }
    }

    if (!empty($_SERVER['HTTP_X_FORWARDED']) && validateIP($_SERVER['HTTP_X_FORWARDED']))
        return $_SERVER['HTTP_X_FORWARDED'];

    if (!empty($_SERVER['HTTP_X_CLUSTER_CLIENT_IP']) && validateIP($_SERVER['HTTP_X_CLUSTER_CLIENT_IP']))
        return $_SERVER['HTTP_X_CLUSTER_CLIENT_IP'];

    if (!empty($_SERVER['HTTP_FORWARDED_FOR']) && validateIP($_SERVER['HTTP_FORWARDED_FOR']))
        return $_SERVER['HTTP_FORWARDED_FOR'];

    if (!empty($_SERVER['HTTP_FORWARDED']) && validateIP($_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 validateIP($ip) {

    if (strtolower($ip) === 'unknown')
        return false;

    // Generate IPv4 network address
    $ip = ip2long($ip);

    // Do additional filtering on IP
    if(!filter_var($ip, FILTER_VALIDATE_IP))
        return false;

    // 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;
}

I am using this in a production node and it works well. As most of the code came from here I have released a node under GNU @ https://github.com/d3vdigital/whatsmyip-node

Chimaera answered 15/10, 2022 at 13:27 Comment(2)
Does this have any security implications? Would I able to save the value to the db after all the validating above?Correggio
@Correggio yes, this can go directly to a dbChimaera
P
2

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.

Postbox answered 3/12, 2021 at 17:59 Comment(0)
A
2

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;
}
Assess answered 5/7, 2022 at 9:15 Comment(0)
H
2
<?php
/**
 * Get the real IP address of the client
 * 
 * @param array $trusted_proxies list of IP addresses of reverse proxy servers that you trust
 * @return mixed client IP or null
 */
function get_client_ip($trusted_proxies=[]) {
    // In cli mode, there is no remote address
    if (empty($_SERVER['REMOTE_ADDR'])) {
        return null;
    }

    $client_ip = $_SERVER['REMOTE_ADDR'];
    // If the remote address is not a trusted proxy, we shouldn't trust
    // any headers that malicious clients may send
    if (!in_array($client_ip, $trusted_proxies)) {
        return $client_ip;
    }

    // The request is coming from a trusted proxy, so we can trust the 
    // "forwarded for" headers
    if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
        return $_SERVER['HTTP_X_FORWARDED_FOR'];
    }

    if (isset($_SERVER['HTTP_CLIENT_IP'])) {
        return $_SERVER['HTTP_CLIENT_IP'];
    }

    // No forwarded client IP header provided; this might be some kind 
    // of health check request. Just return the trusted proxy IP.
    return $client_ip;
}

// Example usage

// if the app doesn't work behind proxy server:
$ip = get_client_ip();

// if you're behind reverse proxy, pass the IP address like this:
$ip = get_client_ip(trusted_proxies: ['10.10.10.10']);

// or like this, for older PHP versions:
$ip = get_client_ip(['10.10.10.10']);

Hatshepsut answered 21/6, 2023 at 6:54 Comment(0)
S
1

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();
Sheepdog answered 16/11, 2016 at 18:15 Comment(0)
D
1

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;
}
Dunois answered 1/3, 2019 at 6:38 Comment(0)
C
1

We can use this for both localhost and website

function get_ip() {
    $ip = '';
    if (isset($_SERVER['HTTP_CLIENT_IP'])){
        $ip = $_SERVER['HTTP_CLIENT_IP'];
    }else if(isset($_SERVER['HTTP_X_FORWARDED_FOR'])){
        $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
    }else if(isset($_SERVER['HTTP_X_FORWARDED'])){
        $ip = $_SERVER['HTTP_X_FORWARDED'];
    }else if(isset($_SERVER['HTTP_FORWARDED_FOR'])){
        $ip = $_SERVER['HTTP_FORWARDED_FOR'];
    }else if(isset($_SERVER['HTTP_FORWARDED'])){
        $ip = $_SERVER['HTTP_FORWARDED'];
    }else if(isset($_SERVER['REMOTE_ADDR'])){
        $ip = $_SERVER['REMOTE_ADDR'];
    }
    if( empty($ip) || $ip == '0.0.0.0' || substr( $ip, 0, 2 ) == '::' ){
        $ip = file_get_contents('https://api.ipify.org/');
        $ip = ($ip===false?$ip:'');
    }
    return $ip;
}
Centonze answered 1/5, 2023 at 21:59 Comment(0)
C
0

Use $ip = $_SERVER["REMOTE_ADDR"] this will save the ip in the ip variable.

Consumerism answered 11/3, 2023 at 20:31 Comment(0)
J
0

As many people said, "anti-proxies" to find real people IP is a little hard to create because you need IP list of proxies that you trust. Cvolton, a Geometry Dash player, has recreated the whole game server in PHP and MySQL and has a function to get user IP that bypasses Cloudflare and 7mPl "localhost bug". Here is the code (since original code was fragmented in 2 files here is the fusioned file) (also the get IP function is at the end of the programm):

/*
 * ip_in_range.php - Function to determine if an IP is located in a
 *                   specific range as specified via several alternative
 *                   formats.
 *
 * Network ranges can be specified as:
 * 1. Wildcard format:     1.2.3.*
 * 2. CIDR format:         1.2.3/24  OR  1.2.3.4/255.255.255.0
 * 3. Start-End IP format: 1.2.3.0-1.2.3.255
 *
 * Return value BOOLEAN : ip_in_range($ip, $range);
 *
 * Copyright 2008: Paul Gregg <[email protected]>
 * 10 January 2008
 * Version: 1.2
 *
 * Source website: http://www.pgregg.com/projects/php/ip_in_range/
 * Version 1.2
 *
 * This software is Donationware - if you feel you have benefited from
 * the use of this tool then please consider a donation. The value of
 * which is entirely left up to your discretion.
 * http://www.pgregg.com/donate/
 *
 * Please do not remove this header, or source attibution from this file.
 */

/*
* Modified by James Greene <[email protected]> to include IPV6 support
* (original version only supported IPV4).
* 21 May 2012
*/

class ipInRange {


    // decbin32
    // In order to simplify working with IP addresses (in binary) and their
    // netmasks, it is easier to ensure that the binary strings are padded
    // with zeros out to 32 characters - IP addresses are 32 bit numbers
    public static function decbin32 ($dec) {
        return str_pad(decbin($dec), 32, '0', STR_PAD_LEFT);
    }

    // ipv4_in_range
    // This function takes 2 arguments, an IP address and a "range" in several
    // different formats.
    // Network ranges can be specified as:
    // 1. Wildcard format:     1.2.3.*
    // 2. CIDR format:         1.2.3/24  OR  1.2.3.4/255.255.255.0
    // 3. Start-End IP format: 1.2.3.0-1.2.3.255
    // The function will return true if the supplied IP is within the range.
    // Note little validation is done on the range inputs - it expects you to
    // use one of the above 3 formats.
    public static function ipv4_in_range($ip, $range) {
        if (strpos($range, '/') !== false) {
            // $range is in IP/NETMASK format
            list($range, $netmask) = explode('/', $range, 2);
            if (strpos($netmask, '.') !== false) {
                // $netmask is a 255.255.0.0 format
                $netmask = str_replace('*', '0', $netmask);
                $netmask_dec = ip2long($netmask);
                return ( (ip2long($ip) & $netmask_dec) == (ip2long($range) & $netmask_dec) );
            } else {
                // $netmask is a CIDR size block
                // fix the range argument
                $x = explode('.', $range);
                while(count($x)<4) $x[] = '0';
                list($a,$b,$c,$d) = $x;
                $range = sprintf("%u.%u.%u.%u", empty($a)?'0':$a, empty($b)?'0':$b,empty($c)?'0':$c,empty($d)?'0':$d);
                $range_dec = ip2long($range);
                $ip_dec = ip2long($ip);

                # Strategy 1 - Create the netmask with 'netmask' 1s and then fill it to 32 with 0s
                #$netmask_dec = bindec(str_pad('', $netmask, '1') . str_pad('', 32-$netmask, '0'));

                # Strategy 2 - Use math to create it
                $wildcard_dec = pow(2, (32-$netmask)) - 1;
                $netmask_dec = ~ $wildcard_dec;

                return (($ip_dec & $netmask_dec) == ($range_dec & $netmask_dec));
            }
        } else {
            // range might be 255.255.*.* or 1.2.3.0-1.2.3.255
            if (strpos($range, '*') !==false) { // a.b.*.* format
                // Just convert to A-B format by setting * to 0 for A and 255 for B
                $lower = str_replace('*', '0', $range);
                $upper = str_replace('*', '255', $range);
                $range = "$lower-$upper";
            }

            if (strpos($range, '-')!==false) { // A-B format
                list($lower, $upper) = explode('-', $range, 2);
                $lower_dec = (float)sprintf("%u",ip2long($lower));
                $upper_dec = (float)sprintf("%u",ip2long($upper));
                $ip_dec = (float)sprintf("%u",ip2long($ip));
                return ( ($ip_dec>=$lower_dec) && ($ip_dec<=$upper_dec) );
            }
            return false;
        }
    }

    public static function ip2long6($ip) {
        if (substr_count($ip, '::')) {
            $ip = str_replace('::', str_repeat(':0000', 8 - substr_count($ip, ':')) . ':', $ip);
        }

        $ip = explode(':', $ip);
        $r_ip = '';
        foreach ($ip as $v) {
            $r_ip .= str_pad(base_convert($v, 16, 2), 16, 0, STR_PAD_LEFT);
        }

        return base_convert($r_ip, 2, 10);
    }

    // Get the ipv6 full format and return it as a decimal value.
    public static function get_ipv6_full($ip)
    {
        $pieces = explode ("/", $ip, 2);
        $left_piece = $pieces[0];
        $right_piece = $pieces[1];

        // Extract out the main IP pieces
        $ip_pieces = explode("::", $left_piece, 2);
        $main_ip_piece = $ip_pieces[0];
        $last_ip_piece = $ip_pieces[1];

        // Pad out the shorthand entries.
        $main_ip_pieces = explode(":", $main_ip_piece);
        foreach($main_ip_pieces as $key=>$val) {
            $main_ip_pieces[$key] = str_pad($main_ip_pieces[$key], 4, "0", STR_PAD_LEFT);
        }

        // Check to see if the last IP block (part after ::) is set
        $last_piece = "";
        $size = count($main_ip_pieces);
        if (trim($last_ip_piece) != "") {
            $last_piece = str_pad($last_ip_piece, 4, "0", STR_PAD_LEFT);

            // Build the full form of the IPV6 address considering the last IP block set
            for ($i = $size; $i < 7; $i++) {
                $main_ip_pieces[$i] = "0000";
            }
            $main_ip_pieces[7] = $last_piece;
        }
        else {
            // Build the full form of the IPV6 address
            for ($i = $size; $i < 8; $i++) {
                $main_ip_pieces[$i] = "0000";
            }
        }

        // Rebuild the final long form IPV6 address
        $final_ip = implode(":", $main_ip_pieces);

        return ip2long6($final_ip);
    }


    // Determine whether the IPV6 address is within range.
    // $ip is the IPV6 address in decimal format to check if its within the IP range created by the cloudflare IPV6 address, $range_ip.
    // $ip and $range_ip are converted to full IPV6 format.
    // Returns true if the IPV6 address, $ip,  is within the range from $range_ip.  False otherwise.
    public static function ipv6_in_range($ip, $range_ip)
    {
        $pieces = explode ("/", $range_ip, 2);
        $left_piece = $pieces[0];
        $right_piece = $pieces[1];

        // Extract out the main IP pieces
        $ip_pieces = explode("::", $left_piece, 2);
        $main_ip_piece = $ip_pieces[0];
        $last_ip_piece = $ip_pieces[1];

        // Pad out the shorthand entries.
        $main_ip_pieces = explode(":", $main_ip_piece);
        foreach($main_ip_pieces as $key=>$val) {
            $main_ip_pieces[$key] = str_pad($main_ip_pieces[$key], 4, "0", STR_PAD_LEFT);
        }

        // Create the first and last pieces that will denote the IPV6 range.
        $first = $main_ip_pieces;
        $last = $main_ip_pieces;

        // Check to see if the last IP block (part after ::) is set
        $last_piece = "";
        $size = count($main_ip_pieces);
        if (trim($last_ip_piece) != "") {
            $last_piece = str_pad($last_ip_piece, 4, "0", STR_PAD_LEFT);

            // Build the full form of the IPV6 address considering the last IP block set
            for ($i = $size; $i < 7; $i++) {
                $first[$i] = "0000";
                $last[$i] = "ffff";
            }
            $main_ip_pieces[7] = $last_piece;
        }
        else {
            // Build the full form of the IPV6 address
            for ($i = $size; $i < 8; $i++) {
                $first[$i] = "0000";
                $last[$i] = "ffff";
            }
        }

        // Rebuild the final long form IPV6 address
        $first = ip2long6(implode(":", $first));
        $last = ip2long6(implode(":", $last));
        $in_range = ($ip >= $first && $ip <= $last);

        return $in_range;
    }
}

/* These two functions are from Cvolton (github.com/Cvolton) */
/* https://github.com/Cvolton/GMDprivateServer/blob/master/incl/lib/mainLib.php#L511 */

function isCloudFlareIP($ip) {
        $cf_ips = array(
            '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/13',
            '104.24.0.0/14',
            '172.64.0.0/13',
            '131.0.72.0/22'
        );
        foreach ($cf_ips as $cf_ip) {
            if (ipInRange::ipv4_in_range($ip, $cf_ip)) {
                return true;
            }
        }
        return false;
}

function getIP(){
        if (isset($_SERVER['HTTP_CF_CONNECTING_IP']) && $this->isCloudFlareIP($_SERVER['REMOTE_ADDR'])) //CLOUDFLARE REVERSE PROXY SUPPORT
            return $_SERVER['HTTP_CF_CONNECTING_IP'];
        if(isset($_SERVER['HTTP_X_FORWARDED_FOR']) && ipInRange::ipv4_in_range($_SERVER['REMOTE_ADDR'], '127.0.0.0/8')) //LOCALHOST REVERSE PROXY SUPPORT (7m.pl)
            return $_SERVER['HTTP_X_FORWARDED_FOR'];
        return $_SERVER['REMOTE_ADDR'];
}

I know that code is really long (and maybe there is some useless things in it, I did not wrote the programm, I just fusionned them) so you could try to optimize it (like fusionnate the 3 functions into only one and keep only what you need for the functions) but this (normally) works. Code is from https://github.com/Cvolton/GMDprivateServer/ The Cvolton's functions: https://github.com/Cvolton/GMDprivateServer/blob/master/incl/lib/mainLib.php#L511 The ip_in_range class: https://github.com/Cvolton/GMDprivateServer/blob/master/incl/lib/ip_in_range.php

Janson answered 29/6, 2023 at 8:7 Comment(0)
C
0

I have gone thorugh many answer and I decided to give improved code to get Visitor IP address.

  • Compatible with CloudFlare
  • Get real visitor IP behind CloudFlare network
  • Handle the case where there are multiple proxies involved (HTTP_X_FORWARDED_FOR)
  • HTTP_X_FORWARDED_FOR can have multiple ip like '1.1.1.1,2.2.2.2'
  • Work if $_SERVER was not available.
  • Filter private and/or reserved IPs
  • Process all forwarded IPs in X_FORWARDED_FOR
  • Check for shared Internet/ISP IP (HTTP_CLIENT_IP)

function GetIP()   {
// Compatible with CloudFlare
// Get real visitor IP behind CloudFlare network
if (isset($_SERVER["HTTP_CF_CONNECTING_IP"])) {
    $_SERVER['REMOTE_ADDR'] = $_SERVER["HTTP_CF_CONNECTING_IP"];
    $_SERVER['HTTP_CLIENT_IP'] = $_SERVER["HTTP_CF_CONNECTING_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)
    {
       // Handle the case where there are multiple proxies involved
       // HTTP_X_FORWARDED_FOR can have multiple ip like '1.1.1.1,2.2.2.2'
        foreach (array_map('trim', explode(',', $_SERVER[$key])) as $ip) 
        {
            // Filter private and/or reserved IPs;
            if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false)
            {
                return $ip;
            }
        }
    }  else {
        // Work if $_SERVER was not available.
        foreach (array_map('trim', explode(',', getenv($key))) as $ip)
        {
            // Filter private and/or reserved IPs;
            if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false)
            {
                return $ip;
            }
        }
    }
}
}
Christiachristian answered 25/10, 2023 at 7:59 Comment(0)
H
-1

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.

Handstand answered 6/12, 2014 at 10:41 Comment(0)
T
-3

To get client IP Address, please use getenv("REMOTE_ADDR").

For example,

$ip_address = getenv("REMOTE_ADDR");
echo $ip_address;

If you call your server using localhost, it will print out ::1. So, please call your server using direct server ip address or domain.

Territorialize answered 22/7, 2021 at 14:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.