Question

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 :)

Was it helpful?

Solution

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.

OTHER TIPS

$_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';
}

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.

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.

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>';

        }

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';
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top