15

Background: Website (example.com), dns setup through cloudflare pro plan, this offers "flexible ssl" (read here), which means that ssl only exists between client and cloudflare and not between cloudflare and server, thus not needing dedicated ip and not needing special setups on the server. The server is setup to not use ssl (just a generic website), however cloudflare's flexible ssl is is taking care of the ssl aspect.

Language: PHP (codeignighter, but that doesnt really matter)

Goal: when browsing to domain "exmple.com/" or "http:// exmple.com/", to generate a variable "http:// example.com", and when browsing to "https:// example.com/*" to generate a variable "https:// example.com".

What should work (but doesnt):

$root = '';
if( isset($_SERVER['HTTPS'] )  && $_SERVER['HTTPS'] != 'off' )
{
    //it doesnt reach here...
    $root .= 'https://';
}
else
{
    $root .= 'http://';
}
$root  .= "".$_SERVER['HTTP_HOST'];
$root .= str_replace(basename($_SERVER['SCRIPT_NAME']),"",$_SERVER['SCRIPT_NAME']);

I can always make it do: "//example.com", but that doesnt really solve the problem for me. Thoughts? Should i be doing some string comparison to determine the https-ness?

Im sure the reason for this is when a request reaches the server (https or http), it comes through port 80 and it doesnt get recognized as ssl, so $_SERVER['HTTPS'] is not defined. I could setup a custom ssl between the server and cloudflare, but would be nicer (less effort) if i could just use some regexp and compare the url somehow.

I would also like to know possible issues and vulnerabilities.

Thanks :)

decay
  • 612
  • 6
  • 19
  • You should not use Cloudflare's "Flexible SSL" because it tells users they are connected to your site securely when the connection is not secure. "The Flexible SSL option allows a secure HTTPS connection between your visitor and Cloudflare, but forces Cloudflare to connect to your origin web server over unencrypted HTTP. An SSL certificate is not required on your origin web server and your visitors will still see the site as being HTTPS enabled." https://support.cloudflare.com/hc/en-us/articles/200170416-End-to-end-HTTPS-with-Cloudflare-Part-3-SSL-options#h_4e0d1a7c-eb71-4204-9e22-9d3ef9ef7fef – PHP Guru Nov 08 '20 at 05:40

6 Answers6

32

Ok, i will answer my own question for future people who has the same issue:

if(!empty($_SERVER['HTTP_X_FORWARDED_PROTO'])){
    $root .= $_SERVER['HTTP_X_FORWARDED_PROTO'].'://';
}
else{
    $root .= !empty($_SERVER['HTTPS']) ? "https://" : "http://";
}

Came accross it when i was looking closely at $_SERVER, and googling around for HTTP_X_FORWARDED_PROTO and got me to few pages that confirmed this.

Hossein
  • 3,755
  • 2
  • 29
  • 39
decay
  • 612
  • 6
  • 19
  • I suspect this works for Full SSL but not Flexible SSL. My answer can handle both of them but if you use Full SSL only, it's ok to apply this answer. – Mygod Aug 14 '16 at 13:31
8

$_SERVER['HTTP_X_FORWARDED_PROTO'] doesn't work for me. I don't know why. Anyway here's my solution:

function is_https_buttflare() {
    return isset($_SERVER['HTTPS']) ||
        ($visitor = json_decode($_SERVER['HTTP_CF_VISITOR'])) &&
            $visitor->scheme == 'https';
}
Mygod
  • 2,077
  • 1
  • 19
  • 43
8

It's working on me without problem. You can use in anywhere:

function is_ssl_active() {
  if (isset($_SERVER['HTTP_CF_VISITOR'])) {
    $cf_visitor = json_decode($_SERVER['HTTP_CF_VISITOR']);
    if (isset($cf_visitor->scheme) && $cf_visitor->scheme == 'https') {
      return true;
    }
  } else if (isset($_SERVER['HTTPS'])) {
    return true;
  }
  return false;
}

Cloudflare puts some HTTP headers like 'CF-Visitor'. Click here to more information.

iamdual
  • 1,251
  • 14
  • 11
0

I was facing the same issue where I could not get the $_SERVER["HTTPS"] variable to show up when doing a var_dump($_SERVER). In CloudFlare, there are three SSL settings, (Flexible SSL, Full SSL and Full SSL(Strict). For servers with self signed certificates, it's recommended to use Full SSL. After I changed my settings to Full SSL, the $_SERVER["HTTPS"] variable appeared and the $_SERVER["SERVER_PORT"] changed from 80 to 443. Now I can validate if my page is HTTPS or not much more easily, like this:

public function get_https_status() {
    return isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == 'on' ?
        true : false;
}

Prior to discovering these changes, I was relying on $_SERVER["HTTP_CF_VISITOR"] and validating the scheme, it all worked fine when accessing the page via public IP address, but when accessing the page locally, in other words, "localhost" as the URL, $_SERVER["HTTP_CF_VISITOR"] didn't existed, but $_SERVER["HTTPS"] did... So my issue was, accessing the page via public IP address, $_SERVER["HTTPS"] did not show and $_SERVER["HTTP_CF_VISITOR"] did. Vice versa.

El Sordo
  • 165
  • 2
  • 14
0

I did this to check for all outcomes:

        // check if ssl used on server
        if ( (!empty( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] !== 'off' ) || $_SERVER['SERVER_PORT'] == 443 ){

            echo '<script>console.log("https")</script>';

        //check if ssl used on load balancers such as cloudflare
        }elseif ( !empty( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' ) {

            echo '<script>console.log("https")</script>';

        }else{

            echo '<script>console.log("http")</script>';

        }
Uriahs Victor
  • 1,049
  • 14
  • 32
-1

Have you tried checking the $_SERVER['SERVER_PORT'] variable? That should return '80' if its just a HTTP connection, or '443' if you're using a HTTPS connection.

So a function like this should hopefully work:

function get_http()
{
    if ($_SERVER['SERVER_PORT'] == '443')
    {
        return 'https';
    }

    return 'http';
}
Johneh
  • 84
  • 5
  • 1
    As mentioned before, the request comes to the server through the normal port 80. So your solution does not work, and if it works the traditional solution (mentioned on my post) would also work. tl;dr; in both http and https traffic, port comes up as 80, not 443 (because of the nature of the flexible ssl by cloudflare) – decay May 08 '14 at 22:39
  • Ah, I thought it was just that the $_SERVER['HTTPS'] parameter wasn't appearing. Appears you've already sorted it yourself above, next suggestion was going to be to check the rest of the server variable as there's usually something useful in there at least. – Johneh May 09 '14 at 07:47