PHP: Remote IP + Load Balancer
PHP + Code Igniter: How to Get Remote IP Behind Load Balancer
Because most PHP opensource software assumes that your web server is directly exposed the to the Internet, I find that I have to alter the code often to get the actual remote ip address. Why? Because my company’s servers are behind a load balancer. In our configuration, X-HTTP-CLIENT-IP is the header that the load balancer passes to the actual web server. I’ve been using codeigniter a lot lately and luckily, code igniter is easily extended. The following code needs to reside in your system/applications/library folder. It needs to be named MY_Input.php (unless you changed the MY_ prefix.)
UPDATE: Alternately, you can also use my new version below that is more update-proof.
This code simply goes through the list of possible matches for remote ip (even from proxy servers.) I really don’t depend on it for anything other than logging, so I’m not worried by the security implications.
<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
/*** Brett: Fri Sep 19 23:18:46 GMT 2008
* Extension to Input class to correctly get netscaler remote ip address ***/
class MY_Input extends CI_Input
{
function ip_address()
{
if ($this->ip_address !== FALSE)
{
return $this->ip_address;
}
if ($this->server('HTTP_X_CLIENTIP'))
{
$this->ip_address = $_SERVER['HTTP_X_CLIENTIP'];
}
elseif ($this->server('REMOTE_ADDR') AND $this->server('HTTP_CLIENT_IP'))
{
$this->ip_address = $_SERVER['HTTP_CLIENT_IP'];
}
elseif ($this->server('REMOTE_ADDR'))
{
$this->ip_address = $_SERVER['REMOTE_ADDR'];
}
elseif ($this->server('HTTP_CLIENT_IP'))
{
$this->ip_address = $_SERVER['HTTP_CLIENT_IP'];
}
elseif ($this->server('HTTP_X_FORWARDED_FOR'))
{
$this->ip_address = $_SERVER['HTTP_X_FORWARDED_FOR'];
}
if ($this->ip_address === FALSE)
{
$this->ip_address = '0.0.0.0';
return $this->ip_address;
}
if (strstr($this->ip_address, ','))
{
$x = explode(',', $this->ip_address);
$this->ip_address = end($x);
}
if ( ! $this->valid_ip($this->ip_address))
{
$this->ip_address = '0.0.0.0';
}
return $this->ip_address;
}
}
?>Update (Aug 19th, 2010)
Because of security concerns with my original MY_input class, and because I see updates to 1.7.2 code for the ip_address() method since this page was was originally posted, you should probably use this new version instead . It incorporates post-processing which has 2 benefits:
- Any updates or improvements to the CodeIgniter core code are maintained because the parent method is called first.
- This version requires specific configuration for the proxy load balancer and the header value that is expected to have the real IP. Failure to setup config options simply bypasses any additional processing and the original value from the parent class (from the core code) is returned instead.
<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
/*** Extension to Input class to correctly get load balancer http proxy remote ip addresses ***/
class MY_Input extends CI_Input {
function ip_address() {
// call the core version of the class to get any updates
// (note this may also break the code below...
// ...When updating be sure to check the core ip_address() code!)
$this->ip_address = parent::ip_address();
// let's get a CI instance so we can search config, etc.
$CI =& get_instance();
$CI->benchmark->mark('custom_ip_address_start');
// let's get the load balancer value of config array, if it exists.
// Make sure you autoload or call config->load() in your constructor
// if you use a separate config file.
$lb_ip = $CI->config->item('load_balancer_ip');
// check to see if we have the load balancer.
if ( $lb_ip !== FALSE && $this->ip_address == '0.0.0.0' ) {
// check to see if the original value was the load balancer.
if ($this->server('REMOTE_ADDR') == $lb_ip) {
$is_load_balancer = TRUE;
}
// check to see if this ip matches the load balancer ip.
} elseif ( $lb_ip !== FALSE && $this->ip_address == $lb_ip ) {
$is_load_balancer = TRUE;
} else {
$is_load_balancer = FALSE;
}
// let's attmept to find the real IP. If load balancer not detected, do nothing.
if ( $is_load_balancer ) {
// okay get the result from the header that is sent by the load balancer.
$lb_header_key = $CI->config->item('load_balancer_header');
if ( $lb_header_key !== FALSE ) {
$real_ip = $this->server($lb_header_key);
if ( $this->valid_ip($real_ip)) {
$this->ip_address = $real_ip;
} else {
// IP from load balancer is not valid.
$this->ip_address = '0.0.0.0';
}
}
}
$CI->benchmark->mark('custom_ip_address_end');
return $this->ip_address;
}
}
?>My concern is that a malicious user could possibly mask his IP using HTTP_X_CLIENTIP in situations where, perhaps, you do not have an http-proxy load balancer in front of your Web server(s.) This new override method only looks for specifically configured values and, through this, removes this potential vulnerability. However, IP spoofing is still possible from the underlying code. This is never a guaranteed operation. You should never rely on any data that originates remotely.
Because the URL to this page was black-listed in the CodeIgniter forums, I thought that, perhaps, I had given advise that was very unpalatable to the crew at CodeIgniter. Turns out that the forum just didn’t like the /free/ segment in the old URL. I have since changed that segment to /code/. However, I’m glad that I updated this class for all the points previously mentioned.



We have an intranet on LAMP, and planning for a Load Balancing architecture.
We donot have Codeigniter here and we too log the client IP address as of now by using: “$_SERVER['REMOTE_ADDR']“. It will be useful if you may throw some light on how we can get still get the Client IP in the LoabBalancer scenario.
Thanks
Joy Deepak
It depends on the load balancer. Netscaler does more than balance the load, it acts as an http proxy (or any web-specific protocol) for your client connections. It optimizes TCP traffic as to keep the TCP traffic (syn/ack handshakes etc) from taking resources on your Web servers. As such, it makes the connection to your web servers behind it and, thus, $_SERVER['REMOTE_ADDR'] always returns the ip address of the Netscaler. The Netscaler sends an additional HTTP header (I believe) to indicate where the request initiated. The method above uses this information to get the remote IP. I will note that it will also work when the Web server is not behind a Netscaler because it falls through various optional headers and uses more typical header values as a fallback.
What we have now is a “standard” load balancer that simply routes the inbound packets (NAT.) The incoming packet source IP is maintained and no special method for getting the remote address is required. Most likely, your load balanced solution will fall into this category. If so, you will not need to change anything.
Finally, this method could be modified to be a “standard” PHP function in a non-codeigniter implementation. Here is a link to another blog to demonstrate.
check out this link, I did and it works great:
Spanish (original)
English Translated
How can we get this value “load_balancer_header” ?
‘load_balancer_header’ is a configuration file associative key. It’s value ( at least for netscaler. not certain about other load balancers. ) would be X-HTTP-CLIENT-IP.
[...] I came across a fix but I’m not sure it meets my needs. http://brettic.us/code/php-codeigniter-remote-ip-from-load-balancer/#codesyntax_2 [...]
I was curious about how to extend this to support Amazon and their ever changing IPs.