1416

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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Anup Prakash
  • 14,406
  • 4
  • 17
  • 12
  • 3
    See RFC6302 on recommendations about what to log, and specifically nowadays remember to log the port and not only the address. – Patrick Mevzek Jan 03 '18 at 20:05
  • 3
    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 address – Jonathan DS Aug 08 '18 at 15:13
  • 2
    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; } –  Apr 05 '20 at 12:30
  • 1
    You should use [Abstract IP detection](https://www.abstractapi.com/ip-geolocation-api?ref=stackoverflow). 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. – johnson23 Feb 12 '21 at 10:56

36 Answers36

1530

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

Emil Vikström
  • 90,431
  • 16
  • 141
  • 175
  • 4
    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. – Blue Dec 22 '14 at 21:46
  • 61
    **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........................................................................... – Pacerier Jun 29 '15 at 04:26
  • 47
    ..........................................................................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 http://blog.ircmaxell.com/2012/11/anatomy-of-attack-how-i-hacked.html – Pacerier Jun 29 '15 at 04:26
  • @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? ..... – Aquaholic Nov 21 '21 at 14:39
  • .....@Pacerier - any comments from your side as well on the above – Aquaholic Nov 21 '21 at 14:41
  • Explanation is always a plus, nice answer. I've always trusted $_SERVER['REMOTE_ADDR'] and it gets it done! – Dexter Jun 29 '23 at 04:23
550

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

Samvel Aleqsanyan
  • 2,812
  • 4
  • 20
  • 28
Tim Kennedy
  • 5,980
  • 1
  • 21
  • 16
  • 241
    **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. – Janos Pasztor Jun 25 '13 at 14:37
  • 30
    With regards to Janoszen's comment, one option is PHP's filter_var($_SERVER['REMOTE_ADDR'], FILTER_VALIDATE_IP). – lostphilosopher Jan 27 '14 at 21:28
  • 9
    `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). – Martin Tournoij Feb 13 '14 at 14:51
  • 3
    @lostphilosopher that is a reasonable thing to do,and it will make it more reliable, but unfortunately it would still allow spoofing. – Tim Seguine Apr 16 '14 at 08:14
  • 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! – Marin Sagovac Apr 16 '14 at 15:06
  • @TimSeguine No, remote_addr takes the ip from TCP. One could not spoof an ip over tcp as it needs a handshake . – Geo C. May 21 '14 at 21:13
  • @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. – Tim Seguine May 22 '14 at 10:51
  • @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. – Tim Kennedy May 22 '14 at 12:49
  • 8
    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. – Jon Watte Dec 01 '14 at 06:31
  • 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. – Jon Watte Dec 01 '14 at 06:32
  • 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.** – hiburn8 Sep 26 '19 at 09:46
237
echo $_SERVER['REMOTE_ADDR'];

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

lemon
  • 9,155
  • 7
  • 39
  • 47
159

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']);
Nikola Kirincic
  • 3,651
  • 1
  • 24
  • 28
josh123a123
  • 2,070
  • 1
  • 13
  • 21
  • 13
    Always remember to sanitize any input that could have been modified by the user. This is one of those times. – josh123a123 Nov 25 '14 at 03:50
  • 8
    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. – DavidTaubmann Jun 23 '16 at 23:11
  • 2
    Good catch. I have adjusted the post. Thank you! – josh123a123 Jun 27 '16 at 20:51
  • 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']; }` – SpYk3HH Sep 07 '16 at 21:44
  • You're testing for one header and return another... It looks like a mistake. – oriadam Nov 16 '16 at 18:07
  • @SpYk3HH Stop editing this if you don't know what your doing. – josh123a123 Nov 16 '16 at 20:02
  • 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']);` – SpYk3HH Nov 16 '16 at 20:40
  • @SpYk3HH you realize that is the same code that I have in the post right? – josh123a123 Nov 17 '16 at 14:54
  • 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. – SpYk3HH Nov 18 '16 at 15:43
  • works fine in phpstorm what IDE are you using? – josh123a123 Nov 21 '16 at 15:11
  • 8
    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'];** – shasi kanth Nov 29 '17 at 12:18
  • 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.net – Toskan Feb 15 '18 at 22:34
  • 3
    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: https://www.soscisurvey.de/tools/view-chars.php . Please clean up that code! – Frank Feb 19 '18 at 11:46
  • 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']); – bormat Feb 25 '18 at 21:58
  • 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. – Curtis Mar 28 '19 at 05:16
  • 2
    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']; – aFeijo Oct 11 '19 at 15:47
105

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

JJJ
  • 32,902
  • 20
  • 89
  • 102
Kyle Cronin
  • 77,653
  • 43
  • 148
  • 164
64

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

algorhythm
  • 8,530
  • 3
  • 35
  • 47
  • 3
    Great answer! Using production tested code, developed and used in such a big framework is one of the best things you can do :) – jnhghy - Alexandru Jantea Jan 19 '15 at 14:19
  • 3
    So wait zend doesnt filter anything? I should see something like: filter_var( $_SERVER['REMOTE_ADDR'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ); – M H Jul 19 '15 at 05:57
  • @Hanoncs why would you do that ? it's very difficult to spoof the remote address – Marius.C Aug 10 '15 at 10:37
  • 1
    @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. – algorhythm Aug 10 '15 at 16:33
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;
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
K.Suthagar
  • 2,226
  • 1
  • 16
  • 28
  • 11
    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". –  Aug 10 '17 at 12:50
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;
}
manuelbcd
  • 3,106
  • 1
  • 26
  • 39
30

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

tjhorner
  • 351
  • 2
  • 16
kainosnous
  • 558
  • 3
  • 5
26

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";
}
hanshenrik
  • 19,904
  • 4
  • 43
  • 89
Stergios Zg.
  • 652
  • 6
  • 9
  • 5
    warning, hackers can easily spoof the ip by sending fake `X-FORWARDED-FOR: fakeip` HTTP headers – hanshenrik Nov 02 '19 at 15:06
  • Ofcourse, but if you use NGINX, clientIP is usually on "HTTP_X_FORWARDED_FOR" – Stergios Zg. Dec 14 '20 at 12:17
  • 4
    **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`). – Jonathan Rosa Jan 18 '21 at 19:50
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;
}
ws-ono
  • 323
  • 2
  • 7
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;
                }
            }
        }
    }
}

Keaser
  • 351
  • 3
  • 6
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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Siamak
  • 1,689
  • 1
  • 17
  • 26
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;
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Mahfuz Ahmed
  • 721
  • 9
  • 23
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');
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Johan Wikström
  • 921
  • 8
  • 18
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;
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
carbontwelve
  • 1,090
  • 1
  • 13
  • 24
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']);.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Yash Kumar Verma
  • 9,427
  • 2
  • 17
  • 28
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'];
SIE
  • 155
  • 1
  • 3
  • Documentation about server variables: https://www.php.net/manual/en/reserved.variables.server.php – Jsowa May 10 '20 at 09:24
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();

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Ivan Barayev
  • 2,035
  • 5
  • 24
  • 30
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;
  }
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Akam
  • 1,089
  • 16
  • 24
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
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Alexander Yancharuk
  • 13,817
  • 5
  • 55
  • 55
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'] ?? '';
}
William Desportes
  • 1,412
  • 1
  • 22
  • 31
4

Try this one:

 $_SERVER['REMOTE_ADDR'];
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
  • 33
    This was already mentioned few times and you answer doesn't add-up actually anything useful. – kenorb May 26 '15 at 11:23
  • '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? – Chris Sep 10 '15 at 13:40
  • 11
    because he is answering to 5 years old question and a lot of same and much better answers are already answered. – Mg Thar Oct 12 '15 at 14:29
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://stackoverflow.com/a/14985633/1190051

vr_driver
  • 1,867
  • 28
  • 39
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;
}
Faraz Ahmad
  • 515
  • 1
  • 6
  • 13
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

Eric Aya
  • 69,473
  • 35
  • 181
  • 253
zac
  • 2,223
  • 2
  • 15
  • 12
  • Does this have any security implications? Would I able to save the value to the db after all the validating above? – sij_a Feb 22 '23 at 16:42
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.

jerryurenaa
  • 3,863
  • 1
  • 27
  • 17
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();
oriadam
  • 7,747
  • 2
  • 50
  • 48
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;
}
Mohamad Hamouday
  • 2,070
  • 23
  • 20
1

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;
}
Shriyank
  • 47
  • 6
0

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

Helper123
  • 15
  • 6
0

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;
}
0
<?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 (!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']);

Emil M
  • 1,082
  • 12
  • 18
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 <pgregg@pgregg.com>
 * 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 <james@cloudflare.com> 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

HGStyle
  • 13
  • 5
-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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
B.F.
  • 477
  • 6
  • 9
-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.

HRM
  • 73
  • 1
  • 5