We have some PHP servers on EC2 behind ELB. We would like to determine the locale/region by IP address of the clients connecting to our servers. The problem is the PHP servers only see the IP address of the ELB. We would like to see the IP addresses of the clients passed through the ELB.
5 Answers
According to the AWS docs, the ELB should be setting the 'X-Forwarded-For' HTTP header which preserves the original client ip address:
The X-Forwarded-For request header helps you identify the IP address of a client. Because load balancers intercept traffic between clients and servers, your server access logs contain only the IP address of the load balancer. To see the IP address of the client, use the X-Forwarded-For request header.
You can access it using the following PHP code (assuming apache):
$http_headers = apache_request_headers();
$original_ip = $http_headers["X-Forwarded-For"];

- 6,377
- 5
- 40
- 60
-
5If you care about security, you should prevent connections to your web servers from anything but ELB. Otherwise, attackers could pretend to be an ELB, set this header, and lie to you about the true remote IP address. http://docs.aws.amazon.com/ElasticLoadBalancing/latest/DeveloperGuide/using-elb-security-groups.html – Eric Hammond Feb 28 '13 at 01:14
-
3Note that the X-Forwarded-For header can contain a comma-delimited list of IP addresses if there are multiple forwarders. The first one is the original client IP. http://en.wikipedia.org/wiki/X-Forwarded-For#Format – mmindenhall May 19 '14 at 22:27
-
4In case the client provided his own X-Forwarded-For, it will be the first one in the comma-delimited list of IPs provided to your application. The real IP is always the last item, apparently. – Elad Nava Nov 27 '14 at 09:17
-
But what if https is used and the whole request including the header and its X-Forwarded-For are encrypted?.. – vak Jul 01 '16 at 12:48
-
vak ELB uses an http connection with EC2, you shouldn't use encryption at that level – Acyra Jul 16 '16 at 11:37
-
We had an additional problem to the balancer: we use Cloudflare, so the client IP is storen on a header variable called HTTP_CF_CONNECTING_IP. – Mirko Dec 02 '16 at 08:22
If you use Apache, have a look at the mod_remoteip module. It's included in Apache 2.4 and newer, but backports for 2.2 can also be found.
The module overrides the client IP address for the connection with the useragent IP address reported in the request header configured with the RemoteIPHeader directive.
Once replaced as instructed, this overridden useragent IP address is then used for the mod_authz_host Require ip feature, is reported by mod_status, and is recorded by mod_log_config %a and core %a format strings. The underlying client IP of the connection is available in the %{c}a format string.
In other words, you can set which header to use (e.g. X-Forwarded-For) and which IP's are trusted (e.g. your load-balancer). The trusted IPs are removed from the header and the first untrusted IP is used as the originating client. This IP is then used internally by Apache in other modules, such as for logging, host-authentication, etc.
This makes it more useful than only handling the X-F-F header in PHP, since mod_remoteip takes care of the entire stack, makes sure your access-logs are correct, etc.
Note: There's also a mod_rpaf module which is the predecessor to this one. It's much more limited. For example, it cannot handle trusted ip-ranges. You need this since you don't know ELB's IP beforehand. It also can't handle multiple hops, such as ELB before Varnish, before Apache. I'd suggest skipping the rpaf module and using remoteip instead.

- 1
- 1

- 3,529
- 5
- 37
- 38
This is a big problem, so try this :D
<?php
if (!empty($_SERVER["HTTP_X_FORWARDED_FOR"])) {
$real_client_ip = $_SERVER["HTTP_X_FORWARDED_FOR"];
} else {
$real_client_ip = $_SERVER["REMOTE_ADDR"];
}

- 1,374
- 1
- 15
- 23
-
This assumes that you trust the client to provide X-Forwarded-For (i.e., because the client is a load balancer). This code really should check the REMOTE_ADDR against a whitelist of acceptable IPs or it opens your code up to a vulnerability. – ZiggyTheHamster May 11 '16 at 23:44
It seems mod_cloudflare maybe a better option for some - especialy v2.2 users Read more at this chap's blog: http://knowledgevoid.com/blog/2012/01/13/logging-the-correct-ip-address-using-apache-2-2-x-and-amazons-elastic-load-balancer/

- 508
- 1
- 4
- 10
Optimal solution for PHP app behind AWS ELB:
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$commaPos = strrchr($_SERVER['HTTP_X_FORWARDED_FOR'], ',');
if ($commaPos === FALSE) $remote_addr = $_SERVER['HTTP_X_FORWARDED_FOR'];
else $remote_addr = trim(substr($_SERVER['HTTP_X_FORWARDED_FOR'], $commaPos + 1));
} else {
$remote_addr = $_SERVER['REMOTE_ADDR'];
}
Notes: X-Forwarded-For can be a comma-space separated list of proxies, with the last in the list being the one that connected to AWS' ELB, and therefore the only one we can trust as not being spoofed.

- 5,098
- 50
- 48