PHP: How to detect / get the real client IP address of website visitors?

by Yang Yang on March 4, 2010

It may seem simple at first because most of us should be relying on the server side environmental variable REMOTE_ADDR solely for client IP addresses:

echo $_SERVER['REMOTE_ADDR'];

Yet it’s barely enough to get the real IP for a variety of circumstances such as when the user is visiting your website from a proxy server. To everyone’s surprise, there are a lot more environmental variables regarding client IP address than just the most straightforward one, REMOTE_ADDR. Consider this snippet in the attempt to detect the real source IP address of the request:

function get_ip_address() {
    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 (explode(',', $_SERVER[$key]) as $ip) {
                if (filter_var($ip, FILTER_VALIDATE_IP) !== false) {
                    return $ip;
                }
            }
        }
    }
}

It first searches through a series of possible environmental variables that may contain the client IP address and uses whichever that is set and then extract the potential IP value to be validated. After successful validation by the PHP5 filter_var() function, the value is returned. You better not change the order these variable names are placed in the literal array.

This approach is much more sophisticated than just looking at REMOTE_ADDR but it’s far from mess-proof because it relies on the HTTP header information which can be easily manipulated anywhere along the way the request is routed to your server / website.

Subscribe to Kavoir: blog feed

You should also read:

Omoba March 27, 2010 at 1:10 am

I got you stuff, really looks nice and tactical. But I tried to call the function this way:
echo get_ip_address(); but generated this error: Fatal error: Call to undefined function filter_var() in C:\wamp\www\php-bank\create-directory\index.php on line 10, That is the error that it generated, please help! I would love to get the reply in my mail box please! Thanks.

Yang Yang March 28, 2010 at 8:21 am

You may want to upgrade to the latest PHP version. :) filter_var() is a recent addition to the native functions.

Alphonse September 15, 2010 at 11:30 pm

$client_addr = filter_var((!empty($_SERVER['HTTP_CLIENT_IP']))? $_SERVER['HTTP_CLIENT_IP']:
(!empty($_SERVER['HTTP_X_FORWARDED_FOR']))? $_SERVER['HTTP_X_FORWARDED_FOR']:
(!empty($_SERVER['REMOTE_ADDR']))? $_SERVER['REMOTE_ADDR']:’UNKNOWN’, FILTER_SANITIZE_STRING);

Yang Yang September 18, 2010 at 2:25 pm

This is not preferable because it jeopardizes the readability of the code.

Human efficiency comes way higher priority than that of machines or storage.

Ifreelancer.asia November 20, 2012 at 11:37 pm

The Above code given by ‘Alphonse’ is an awesome simple code, and is more better than yours, while taking on reference to Server.

urorbit February 27, 2013 at 9:38 am

i like your code :)
thanx

Marcel March 7, 2011 at 8:02 pm

Thanks for script, I’ll try, it seems very useful.

niko March 22, 2011 at 1:50 pm

This is the most elegant code I’ve seen for the job.
Nice work!

Robin Jakobsson June 9, 2011 at 4:44 pm

Very nice snippet, thanks a lot!

Steve August 30, 2011 at 11:07 pm

Hello, nice job here! I totally agree with Yang Yang on readability being the most important thing. Actually, for the sake of it, 2 extra things I’d do on your function are:
- use a regex and avoid one extra level of indentation (on the filter_var line)
- declare the array before the foreach (might have some speed benefits also, if an array is otherwise build at every cycle iteration – not sure on this; main point is nevertheless to be able to break long constant line in multiple ones, though, so o improve readability);
But hey, again, cool solution of yours ;-)

en4ce November 17, 2011 at 10:19 pm

thanks! that works great

Rutger December 20, 2011 at 5:58 pm

ip-spoofing get’s realy easy with this, just adding an extra header in enough.

anshu February 7, 2012 at 3:07 pm

[ $client_addr = filter_var((!empty($_SERVER['HTTP_CLIENT_IP']))? $_SERVER['HTTP_CLIENT_IP']:
(!empty($_SERVER['HTTP_X_FORWARDED_FOR']))? $_SERVER['HTTP_X_FORWARDED_FOR']:
(!empty($_SERVER['REMOTE_ADDR']))? $_SERVER['REMOTE_ADDR']:’UNKNOWN’, FILTER_SANITIZE_STRING) ]

Ianne May 18, 2012 at 3:23 pm
phphunger July 5, 2012 at 6:31 pm

wow simply superb post…

errorare August 29, 2012 at 3:56 pm

thanks for great works.

sanaan Barzinji September 13, 2012 at 2:39 pm

well done,

Sanaan Barzinji
Erbil, Iraq

Comments on this entry are closed.

{ 2 trackbacks }

Previous post:

Next post: