BlitZ
BlitZ

Reputation: 12168

Is $_SERVER['REQUEST_SCHEME'] reliable?

I recently was seeking a way to properly determine protocol, under which url request was supplied to the server.

I watched through parse_url() and though $_SERVER superglobal variable, and found this:

<?php

header('Content-Type: text/plain');

print_r($_SERVER);

Output:

[REQUEST_SCHEME] => http

However, I was unable to find it on php.net or Google. Though, I was able to find this question. Q#1: If $_SERVER['REQUEST_SCHEME'] wasn't documented, then it is probably unreliable, or it can be trusted?

I'am using VC9 PHP 5.4.14 TS under windows for development. But my production is under ubuntu. Q#2: Is this property also availible under ubuntu linux too?

Upvotes: 52

Views: 66627

Answers (9)

aldemarcalazans
aldemarcalazans

Reputation: 1534

As this variable is not available in all web server versions, it is not reliable testing only it. Instead, you can change your PHP code to test two more server environment variables, which can also indicate that https is being used, as below:

if ( (! empty($_SERVER['REQUEST_SCHEME']) && $_SERVER['REQUEST_SCHEME'] == 'https') ||
     (! empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ||
     (! empty($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == '443') ) {
    $server_request_scheme = 'https';
} else {
    $server_request_scheme = 'http';
}

As remarked by toxalot, REQUEST_SCHEME is a native variable of Apache web server since its version 2.4. Apache 2.2 does not have it (see Apache 2.2 server variables) and Microsoft IIs 8.5 does not have it either (see IIS 8.5 Server Variables). Naturally, if a variable is not set by the server, PHP will not include it in its global array $_SERVER.

Fortunately, for compatibility with codes based exclusively on REQUEST_SCHEME checking, you can create this variable in Apache 2.2 editing all your host configuration files (httpd.conf, ssl.conf, 000-default.conf, vhosts.conf), adding the following lines:

# FOR HOSTS LISTENING AT PORT 80
SetEnvIf Request_Protocol ^HTTP/ REQUEST_SCHEME=http

# FOR HOSTS LISTENING AT PORT 443
SetEnvIf Request_Protocol ^HTTP/ REQUEST_SCHEME=https

The code above presume the use of one vhost for every protocol (a best practice in Apache - see this and that).

Upvotes: 27

smarteist
smarteist

Reputation: 1421

I'm using this and i think it's best way to get the current scheme

    /**
     * Is Secure?
     * Determines if the application is accessed via an encrypted
     * (HTTPS) connection.
     *
     * @return  bool
     */
    public static function isSecure()
    {
        if (!empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) !== 'off') {
            return true;
        } elseif (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) === 'https') {
            return true;
        } elseif (!empty($_SERVER['HTTP_FRONT_END_HTTPS']) && strtolower($_SERVER['HTTP_FRONT_END_HTTPS']) !== 'off') {
            return true;
        } elseif (isset($_SERVER['SERVER_PORT']) && intval($_SERVER['SERVER_PORT']) === 443) {
            return true;
        }
        return false;
    }

Define this function and check server ssl connection to get current scheme

        $scheme = isSecure() ? 'https' : 'http';

Upvotes: 3

Aurovrata
Aurovrata

Reputation: 2289

Its interesting to see how WordPress resolves this issue with its is_ssl() function which makes use of $_SERVER variable,

function is_ssl() {
    if ( isset( $_SERVER['HTTPS'] ) ) {
        if ( 'on' == strtolower( $_SERVER['HTTPS'] ) ) {
            return true;
        }

        if ( '1' == $_SERVER['HTTPS'] ) {
            return true;
        }
    } elseif ( isset( $_SERVER['SERVER_PORT'] ) && ( '443' == $_SERVER['SERVER_PORT'] ) ) {
        return true;
    }
    return false;
}

Upvotes: 3

Arno
Arno

Reputation: 1359

This value depends on your web-server. If you use nginx (v1.10), in file /etc/nginx/fastcgi_params you can see this following lines :

fastcgi_param  REQUEST_SCHEME     $scheme; 
fastcgi_param  HTTPS              $https if_not_empty;

Generally, this default values are sufficient. But it is possible that it does not work, you can force this values in your vhost :

include fastcgi_params;
fastcgi_param  REQUEST_SCHEME     https; 
fastcgi_param  HTTPS              On;

If you use Apache, you can take a look toxalot's answer

Upvotes: 4

Sinus tentaclemonster
Sinus tentaclemonster

Reputation: 548

Enhancing toxalot's suggestion for CloudFlare users:

RewriteEngine on

RewriteCond %{HTTPS} !on [OR]
RewriteCond %{HTTP:CF-Visitor} '"scheme":"http"'
RewriteRule .* - [E=REQUEST_SCHEME:http]

RewriteCond %{HTTPS} on [OR]
RewriteCond %{HTTP:CF-Visitor} '"scheme":"https"'
RewriteRule .* - [E=REQUEST_SCHEME:https]

Upvotes: 2

S.A.N
S.A.N

Reputation: 91

In new version Nginx, set by default fastcgi_param REQUEST_SCHEME $scheme.

Upvotes: 5

toxalot
toxalot

Reputation: 11820

The REQUEST_SCHEME environment variable is documented on the Apache mod_rewrite page. However, it didn't become available until Apache 2.4.

I only have Apache 2.2 so I created an environment variable. I added the following to the top of my .htaccess file.

RewriteEngine on

# Set REQUEST_SCHEME (standard environment variable in Apache 2.4)
RewriteCond %{HTTPS} off
RewriteRule .* - [E=REQUEST_SCHEME:http]

RewriteCond %{HTTPS} on
RewriteRule .* - [E=REQUEST_SCHEME:https]

Now I can use

  • %{ENV:REQUEST_SCHEME} in other rewrite conditions and rules
  • $_SERVER['REQUEST_SCHEME'] in my PHP code

I don't have to do extra messy conditional checks everywhere, and my PHP code is forward compatible. When Apache is upgraded, I can change my .htaccess file.

I don't know how you'd apply this to a Windows environment. This is probably not a good solution for distributed code, but it works well for my needs.

Upvotes: 62

invisal
invisal

Reputation: 11181

It is hard to prove that it is reliable, but it is easy to prove that it is not reliable (if only I could provide a case which it does not work). And I can prove that it is unreliable because it does not work with IIS 7.0 + PHP 5.3

Upvotes: 37

user1864610
user1864610

Reputation:

I, too, couldn't find a reference to REQUEST_SCHEME, but if you're looking to determine whether a request was made by http: or https: then you can use $_SERVER['HTTPS'], which is set to a non-empty value if a request was made by https:. It's documented on the PHP site here

Upvotes: 11

Related Questions