How to Check for HTTPS Request in PHP?

In PHP the most common way of determining whether a request was made through the HTTPS protocol or not is using the HTTPS property on the $_SERVER superglobal like so:

$_SERVER['HTTPS'];

As per the php docs, if the connection is using SSL/TLS then the value of $_SERVER['HTTPS'] is set to a non-empty value (such as on, 1, etc. depending on the web server configuration).

Although this sounds like the go-to solution, it has some caveats:

  • Some servers (such as ISAPI with IIS) might be setting the value of $_SERVER['HTTPS'] to off for example if the request was not made through the HTTPS protocol. Therefore, checking for a "non-empty" value might not always be very reliable.
  • In some instances (such as when using a load balancer/reverse proxy, misconfigured or old servers, broken installations, etc.) we might not even have $_SERVER['HTTPS']; defined even if the connection is being made securely.
  • Some web servers might not use the HTTPS environment variable, or they might be using something else to communicate the SSL status (which could be anything really).

In such cases, we might be able to determine whether the connection is using SSL/TLS by various other means (such as checking other environment variables that might be available). In this article, we'll look at the options we might have available so you can determine the best course of action for your project needs.

Checking the Connection Port

Although not guaranteed, in some cases, a fallback to check the server port could be helpful. For example:

$isHttps = 
    (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on')
    || (isset($_SERVER['SERVER_PORT']) && (int) $_SERVER['SERVER_PORT'] === 443)
;

For most cases and configurations, we can rely on port 443 (and 8443) as per the convention, hence the additional port check might be useful.

In theory, we can use any available port for HTTPS based on how we configure our server; we can even run HTTPS on port 80 for example. However, as per the convention, 443 and 8443 are assigned for HTTPS and should be reliable for most cases. Also, when these port numbers are used, browsers consider the connections to be HTTPS by default.

Identifying the HTTPS Protocol When Using a Load Balancer

A reverse proxy (load balancer) may communicate with a web server using HTTP even if the request to the reverse proxy itself is HTTPS (coming from the client). In such cases, the load balancer may add additional header(s) such as X-Forwarded-Proto (which is the de-facto standard). Some other non-standard variants are:

X-Forwarded-Protocol: https
X-Forwarded-Ssl: on 
X-Url-Scheme: https

# Microsoft applications and load-balancers:
Front-End-Https: on

To access these properties via the $_SERVER superglobal please remember, for example to access X-Forwarded-Protocol, you would use $_SERVER['HTTP_X_FORWARDED_PROTO'], etc.

Depending on the load balancer we're using, we can add checks for these headers as a fallback. For example AWS ELB, HAProxy and Nginx Proxy, all use X-Forwarded-Proto. So, if we're using one of those, we could add a fallback check like so:

$isHttps =
    (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on')
    || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https')
;

Although, X-Forwarded-Proto is the de-facto standard, the RFC-7239 now defines the Forwarded-* which is intended to replace X-Forwarded-* headers. However, the X- prefixing convention is very widely known and used, and it may take time for the RFC-7239 standard to get widely adopted.

Using the REQUEST_SCHEME Environment Variable

Most web servers don't have $_SERVER['REQUEST_SCHEME'] set by default, therefore, it might not be very reliable. Also, there's no mention of it in the official php docs as of yet. It is, however, available with newer versions of popular web servers such as with Apache 2.4+ and Nginx 1.9.2+. So, if you're using one of those, or wish to support modern/popular web servers then it might make sense to add a check for this in the mix. Consider for example:

$isHttps = 
    (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on')
    || (isset($_SERVER['REQUEST_SCHEME']) && $_SERVER['REQUEST_SCHEME'] === 'https')
    || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https')
;

Copy/Paste Solution That Covers Most Cases

$isHttps = 
    $_SERVER['HTTPS']
    ?? $_SERVER['REQUEST_SCHEME']
    ?? $_SERVER['HTTP_X_FORWARDED_PROTO']
    ?? null
;

$isHttps = 
    $isHttps && (
        strcasecmp('on', $isHttps) == 0
        || strcasecmp('https', $isHttps) == 0
    )
;

This should be sufficient for most cases. Still, you could add an extra support to check for the port if you believe the covered checks aren't enough.

Note the use of strcasecmp() to do a case-insensitive match on strings. This might be important, given that there's no hard rule on the case of the values the HTTPS protocol environment variables may have.

Handling Non-Standard Environment Variables

In case of a web server environment where all the commonly used environment variables that check for the HTTPS protocol are absent, we could simply define an overridable environment variable in our server. In Apache for example, we could add the following to a .htaccess file:

RewriteEngine on

RewriteCond %{HTTPS} off
RewriteRule .* - [E=REQUEST_SCHEME:http]

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

This would make $_SERVER['REQUEST_SCHEME'] available to us in PHP (and also %{ENV:REQUEST_SCHEME} in rewrite rules and conditions in apache).

For other web servers, you could simply follow the same rule and do something similar.


This post was published (and was last revised ) by Daniyal Hamid. Daniyal currently works as the Head of Engineering in Germany and has 20+ years of experience in software engineering, design and marketing. Please show your love and support by sharing this post.