WordPress is_ssl() doesn’t work behind some load balancers

WordPress has a function is_ssl() that it uses to check whether a page is loaded with the HTTPS protocol, so that it can use the same protocol to load scripts, stylesheets, and other assets. It relies on the web server giving it a couple of clues, but when your website is hosted behind a load balancer, those clues aren’t always available. In particular, websites hosted by Network Solutions get no clues at all when pages are loaded over HTTPS.

Update: SSL Insecure Content Fixer now handles reverse proxies for you. Just pick the appropriate setting for HTTPS detection.

I ran into this recently when someone using my SSL Insecure Content Fixer plugin wasn’t getting any joy, and basic WordPress functions that should have automatically adapted to SSL were all still returning HTTP protocol links. After a brief investigation, it turns out that Network Solutions sites behind load balancers don’t get any clues at all indicating whether a page was loaded over SSL.

Normally, when a page is loaded with the HTTPS protocol, the web server passes a special server variable to tell the website; in PHP it can be checked by looking at $_SERVER['HTTPS']. If that fails, you can check the IP port that the request came in on, to see if it was port 443 (the standard port for SSL); in PHP, you can get that from $_SERVER['SERVER_PORT'].

When your website is behind a load balancer, the load balancer gets that information because that’s what the user’s browser is actually connecting to. It in turn connects to the real web server, but it uses the HTTP protocol, not SSL, so the real web server can’t tell the difference. Amazon’s load balancers apparently provide an extra variable, $_SERVER['HTTP_X_FORWARDED_PROTO'], so you can check for that. If you have some control over the load balancer configuration, you can make it pass some information. But not everyone is so lucky.

To fix the problem on hosts like Network Solutions, you need to trick WordPress into thinking it was passed useful information. It isn’t safe to just assume you’re on SSL, however, so you need to have some idea that you are. The easiest way around this is to put the whole site on SSL by setting the home and site URLs to HTTPS, and then adding the $_SERVER['HTTPS'] variable yourself. Here’s a brief snippet that does that:

// if site is set to run on SSL, then force-enable SSL detection!
if (stripos(get_option('siteurl'), 'https://') === 0) {
    $_SERVER['HTTPS'] = 'on';
}

Of course, a user can always edit the URL in their browser to put it back to HTTP, so we need another way to ensure that pages end up on HTTPS. This little JavaScript snippet will do that when added to all pages:

<script>
if (document.location.protocol != "https:") {
    document.location = document.URL.replace(/^http:/i, "https:");
}
</script>

To save you time and headaches, I’ve put those things together into a simple WordPress plugin. Download this script from gist and save the .php file into the WordPress plugins folder on your website, then activate it in the Plugins admin. It detects when your site’s URL uses HTTPS, and then activates those code snippets above.

Job is done, even on Network Solutions!

Facebooktwittergoogle_plusredditlinkedinmailFacebooktwittergoogle_plusredditlinkedinmail