4

I'm using cPanel from someone else who resold it to me. This will probably mean I cannot use mod_cloudflare

I would like to get the visitor's IP and not CloudFlare IP. The part of code I'm using:

$_SERVER['REMOTE_ADDR']

That line will get the IP of cloud flare and not the original user's IP.

Is there any way I can get the original IP address from the visitor?

Jeroen
  • 65
  • 1
  • 8

4 Answers4

2

Akam's answer is too complicated and does not work. Here's a simpler way:

if (isset($_SERVER["HTTP_CF_CONNECTING_IP"])) {
  $_SERVER['REMOTE_ADDR'] = $_SERVER["HTTP_CF_CONNECTING_IP"];
}
Hudson Taylor
  • 1,142
  • 2
  • 10
  • 30
  • @hudson-taylor Can you elaborate on this answer more? Unsure how/where to implement this. – Jay Jun 07 '17 at 19:35
  • @Jay Unfortunately, not. I just helped fix some grammar and/or formatting on this answer; I didn't actually help write the code :( Denis Ragusoz would probably be a better person to ask. – Hudson Taylor Jun 09 '17 at 03:46
1

I used following Functions in my application: http://ipaddress.standingtech.com/ to exclude CloudFlare IP address

<?php

/*
 * @param $ips = array of IP address
 */
function clear_flare_ips($ips){
  foreach($ips as $index => $ip){
    if(is_in_flareips($ip)){
      unset($ips[$index]);
    }
  }
  ksort($ips);
  return $ips;
}

function is_in_flareips($ip){
 $flareips = getflareips();
 foreach($flareips as $range){
   if(ip_in_range( $ip, $range )){
    return true;
   }
 }
 return false;
}

function getallips(){
      if (isset($_SERVER['HTTP_CLIENT_IP']))
          $ipaddress[] = $_SERVER['HTTP_CLIENT_IP'];
      if(isset($_SERVER['HTTP_X_FORWARDED_FOR']))
          $ipaddress[] = $_SERVER['HTTP_X_FORWARDED_FOR'];
      if(isset($_SERVER['HTTP_X_FORWARDED']))
          $ipaddress[] = $_SERVER['HTTP_X_FORWARDED'];
      if(isset($_SERVER['HTTP_FORWARDED_FOR']))
          $ipaddress[] = $_SERVER['HTTP_FORWARDED_FOR'];
      if(isset($_SERVER['HTTP_FORWARDED']))
          $ipaddress[] = $_SERVER['HTTP_FORWARDED'];
      if(isset($_SERVER['REMOTE_ADDR']))
          $ipaddress[] = $_SERVER['REMOTE_ADDR'];
      if(count($ipaddress) == 0)
          $ipaddress[] = 'UNKNOWN';
      return $ipaddress;
}

function getflareips(){
  /*
  https://www.cloudflare.com/ips-v4
  */
  return array(
  '103.21.244.0/22',
  '103.22.200.0/22',
  '103.31.4.0/22',
  '104.16.0.0/12',
  '108.162.192.0/18',
  '131.0.72.0/22',
  '141.101.64.0/18',
  '162.158.0.0/15',
  '172.64.0.0/13',
  '173.245.48.0/20',
  '188.114.96.0/20',
  '190.93.240.0/20',
  '197.234.240.0/22',
  '198.41.128.0/17',
  '199.27.128.0/21'
  );
}

function ip_in_range( $ip, $range ) {
/**
 * Check if a given ip is in a network
 * @param  string $ip    IP to check in IPV4 format eg. 127.0.0.1
 * @param  string $range IP/CIDR netmask eg. 127.0.0.0/24, also 127.0.0.1 is accepted and /32 assumed
 * @return boolean true if the ip is in this range / false if not.
 */
    if ( strpos( $range, '/' ) == false ) {
        $range .= '/32';
    }
    // $range is in IP/CIDR format eg 127.0.0.1/24
    list( $range, $netmask ) = explode( '/', $range, 2 );
    $range_decimal = ip2long( $range );
    $ip_decimal = ip2long( $ip );
    $wildcard_decimal = pow( 2, ( 32 - $netmask ) ) - 1;
    $netmask_decimal = ~ $wildcard_decimal;
    return ( ( $ip_decimal & $netmask_decimal ) == ( $range_decimal & $netmask_decimal ) );
}
Akam
  • 1,089
  • 16
  • 24
0

Yes, but without access to the server configuration, you will be unable to utilize $_SERVER['REMOTE_ADDR'].

However, you can still use X-Forwarded-For and CF-Connecting-IP. Both would be available in the $_SERVER super-global.

sjagr
  • 15,983
  • 5
  • 40
  • 67
  • $_SERVER['HTTP_X_FORWARDED_FOR'] This will give me Parse error: syntax error, unexpected – Jeroen May 25 '15 at 13:58
  • @Jeroen That's not a problem with `$_SERVER[..]`, you have a *syntax error* in the code you wrote. – deceze May 25 '15 at 14:01
  • http://pastebin.com/LtFKChxD Thats the part of the code, if i change the third line to: $fname2 = $_SERVER['HTTP_X_FORWARDED_FOR'] // or: $fname2 = $ip=$_SERVER['HTTP_X_FORWARDED_FOR']; // It will both give error – Jeroen May 25 '15 at 14:05
  • That goes way above my knowledge haha, the thing is that it works with $_SERVER['REMOTE_ADDR'] but if i just replace that part it will give the error. – Jeroen May 25 '15 at 14:16
  • I am not able to fix it, im using $_SERVER['REMOTE_ADDR'] without a problem but when using $_SERVER['HTTP_X_FORWARDED_FOR'] its giving the syntax error which cannot be explained – Jeroen Jun 14 '15 at 09:51
  • @Jeroen I can't reproduce your problem, so you're going to have to show complete code that causes the same issue you're describing. – sjagr Jun 21 '15 at 15:36
  • @Jeroen [See this sandbox](http://sandbox.onlinephpfunctions.com/code/556b92636f27495ac90b95bc14848fe19e21c5e2). Notice how the error message (for the `HTTP_X_FORWARDED_FOR`) is not the same as your syntax error message (it complains about an undefined index - which makes sense since that environment isn't running behind CloudFlare). Please show your code with the usage of `HTTP_X_FORWARDED_FOR` instead of `REMOTE_ADDR` and double-check the error message you're getting for it. – sjagr Jun 22 '15 at 14:17
0

How to read the below:

  • Skim to get an overview
  • Check the right way
  • Check details in the answer under Details explaining answer with examples title (Last one)
  • There are a lot of details. As the topic can be large. Skim or read all depending on your interest.

If you don't care about the right way. And want something fast.

It's advised to go the right way. You do it once. And you get all the benefits.

If you want just something fast and works. For your need.

Check if the Cloudflare headers are forwarded to your server and available in your app. If available then you can access the value. If not available. Then you need to do server config.

The Cloudflare header is CF-Connecting-IP

To access headers in PHP you can use the below method:

// Replace XXXXXX_XXXX with the name of the header you need in UPPERCASE (and with '-' replaced by '_')
$headerStringValue = $_SERVER['HTTP_XXXXXX_XXXX'];
  • Replace XXXXXX_XXXX with the name of the header you need in UPPERCASE (and with '-' replaced by '_')

In our case, we should check

$_SERVER['HTTP_CF_CONNECTING_IP']

You can check more ways of accessing headers here
How do I read any request header in PHP

Note: Why the right way? If there are any modules that rely on REMOTE_ADDR variable automatically. You'll find yourself needing to go for the resolution. As they won't work. Same thing with logs. Like Apache logs. U start to see Cloudflare IPs (surely you can modify the Apache formatting to include the Cloudflare header value). However, you can get the idea. Of how many places you may need to do things if you don't go for the resolution.

Refs

Right way and Cpanel|Plesk

This will probably mean I cannot use mod_cloudflare

  • (or ip-resolution modules)

No you can without any issue if you are using Apache alone as server. Or Nginx alone as a server.

If You are using Apache behind Nginx as a proxy.

Three cases:

Case 1: CPanel or Plesk already does set the headers (Which normally is the case)

proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

If those two lines are there. In that case. You only need to add one newly created config file in Nginx. No issue at all. No conflict with Plesk or CPanel. And In Apache the same (one file). In Apache you use X-Real-IP.

Case 2: CPanel doesn't include X-Real-IP But does for X-Forwarded-For

You can use X-Forwarded-For for resolution in Apache.

Case 3: If none of those lines are included by default
(Which normally is never the case. At least X-Forwarded-For)

In that case, you can use a hack. Where through shell scripting you check and add an include toward a config file. That injects lines inside the location {} block in question. You can use chatgpt for creating such a script fast. Make sure to include a check. And include the script in systemd using ExecStartPre= (have to run before start). So on service restart the check happens. If the include is not there it would add it. If Plesk|Cpanel ever regenerates the config after. Plesk|Cpanel will restart the service and our script will make sure to put back the include if not there.

Real ip resolution as the right way

The right way is to set a real-ip resolution at server levels

  • Nginx (with http_realip_module)
    • You just add a config. As shown in the link to the answer below.
    • No issue with CPanel or Plesk. only. Clone the Plesk|Cpanel config and link to the clone directly. And take it from there.
  • Apache (with mod_remoteip module)
    • You just add a config. As shown in the link to the answer below.
    • No issue with CPanel or Plesk.
  • Or both Nginx and Apache, in a multi-proxy setup (Using both of the above)
    • Nginx as a reverse proxy - The part about passing headers to Apache (location {} block for proxy_pass). Would require u to modify cpanel or plesk-generated configs. You can script a check and inject an included line within the proxy passing location block. (check the answer in the last title) - Otherwise, u opt out of Plesk or Cpanel for Nginx config

The benefit of that. Is having the whole stack works naturally without modifications and the whole stack. Also if you make modifications to your stack. Things will remain to work without needing to re-setup.

  • Apache, and all its modules
  • App and framework and all libraries behind Apache
  • Same thing for nginx
  • The logs will work with clients' IPs. Not Cloudflare. In debugging is helfpul
  • Modules relying on logs and the ip's will not get broken
  • In short the whole stack. Variables are defined at every stack or layer level. And depending on the layers above them.

Also, The usage of such real-ip modules adds trust security.

  • Only the defined trusted ips will be used to set the real-ip.

Note: Why the right way? If there are any modules that rely on REMOTE_ADDR variable automatically. You'll find yourself needing to go for the resolution. As they won't work. Same thing with logs. Like Apache logs. U start to see Cloudflare IPs (surely you can modify the Apache formatting to include the Cloudflare header value) (Then what about modules that may rely on the log format). In all cases, however, you can get the idea. Of how many places you may need to do things if you don't go for the resolution.

Cloudflare module deprecated

As by Cloudflare doc. The Old Cloudflare module is deprecated. And you have to use Apache and Nginx-specific specialized modules.
https://developers.cloudflare.com/support/troubleshooting/restoring-visitor-ips/restoring-original-visitor-ips/ a good read.

Details explaining answer with examples (all info are in the SO answer link)

My answer here shows a full example and goes into explaining the details, It's long and detailed
https://stackoverflow.com/a/76845593/7668448

Info index (points of the answer above)

  • Two sides deal
    • One side set headers (Proxy)
    • One side resolves using the headers and set real-ip (overriding internal variables)
      • Right way. Because it allows all stacks defaults to work naturally
  • What does setting real-ip mean (Nginx, Apache, ....)
  • Cloudflare => Nginx => Apache situation example
  • Proxy side (Nginx example)
  • Why we should use real-ip module
    • Trust security
  • Details about all elements
    • real-ip module
    • X-Forward-for header and how it works
    • $remote_addr variable
    • $proxy_add_x_forwarded_for variable
    • X-Forwarded-For vs X-Real-IP
  • Usual used Headers
  • Setting headers
  • Apache Side .
    • mod_remoteip module
    • whole details about setting up the configuration
  • Setting real-ip, vs just Logging headers
  • Some header debugging tips (Apache and nginx)
  • Bonus: Automating trust IP lists update
Mohamed Allal
  • 17,920
  • 5
  • 94
  • 97
  • How does this answer the question? – TylerH Aug 09 '23 at 12:51
  • @TylerH did you even read and follow the link ?? You setup Nginx, or Apache so it resolve the ips. And you set it up with the specific modules for each. In Nginx it's `http_realip_module`. And in Apache it's `mod_remoteip module`. I updated the answer to include them. Otherwise, you have to check the link. I didn't include the full answer here to not duplicate. All is explained in details. You setup the modules. You understand why it's the right way. The resolution assure that the variable REMOTE_ADDR will hold the correct value (client IP, instead of cloudflare). You should read – Mohamed Allal Aug 09 '23 at 13:18
  • Basically after setting the resolution in Apache or Nginx Or both if u are using nginx as reverse proxy ahead of Apache. The PHP value `$_SERVER['REMOTE_ADDR']` in your app, will hold the client IP value. The way it works. Nginx or Apache with there FCGI module or whatever module that deal with Apache. They will set the PHP variable `REMOTE_ADDR`. Both nginx and apache hold an equiv variable and the resolution set them. Apache var is the same as PHP `REMOTE_ADDR`. In Nginx its `$remote_addr` mostly the same. – Mohamed Allal Aug 09 '23 at 13:25
  • The fact that you are using CPanel. Doesn't change a bit. You can configure Apache easily. Without having any conflict with CPanel. I do that with Plesk all the time. – Mohamed Allal Aug 09 '23 at 13:53
  • Why the right way? If there are any modules that rely on `REMOTE_ADDR` variable automatically. You'll find yourself needing to go for the resolution. As they won't work. Same thing with logs. Like Apache logs. U start to see Cloudflare IPs (surely you can modify the Apache formatting to include the Cloudflare header value). However, you can get the idea. Of how many places you may need to do things if you don't go for the resolution. – Mohamed Allal Aug 09 '23 at 13:58
  • @TylerH If you still think that doesn't answer the question let me know. And I did update the above to clarify even further – Mohamed Allal Aug 09 '23 at 14:06
  • I added a full section about `This will probably mean I cannot use mod_cloudflare` – Mohamed Allal Aug 09 '23 at 14:39