Redirect HTTP to HTTPS in WordPress running behind a Google HTTP Load Balancer

We’ve setup a multi-node WordPress installation in Kubernetes running inside the Google Container Engine in the Google Cloud Environment. We’ve also created Google HTTP Load Balancer that routes traffic to our WordPress website. The load balancer is configured to accept requests for both http and https. The only reason why we’re exposing http is to allow people just typing `http://our-website.com` to be redirected to `https` instead of just getting an error. This is of course very common and there are even plugins such as Easy HTTPS Redirection that can handle this for you. The problem is that we’re behind a load balancer so these plugins doesn’t work. To understand why let’s see what the Easy HTTPS Redirection actually does. All it does is to provide a convenient way to write the following rules to the .htaccess file in WordPress root directory:

# BEGIN HTTPS Redirection Plugin

RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

# END HTTPS Redirection Plugin

What this does is to check if HTTPS is “off” (i.e. the request is just http) and if so redirect to https instead. This is fine if Apache does SSL termination but since we’re using the Google HTTP Load Balancer it won’t work. The reason is that ALL requests to our WordPress installation will be sent as HTTP from the load balancer and we’ll end up in an infinite redirection cycle. Fortunately Google follows the defacto standard of adding a header called X-Forwarded-Proto that contains the protocol (http or https) used to access the load balancer. Thus we can change our `.htaccess` file to include this instead:


RewriteEngine On
RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

Now we can access `http://our-website.com` and be redirect to https . . . for a while! After a minute or so you’ll run into an error page saying that the request timed out. This page is served by the backend service created as a part of creating the Google Load Balancer. The reason we’re seeing this page is that our health check associated with the backend service has failed. But why? The health check makes a call to `/` (by default) using HTTP and since the backend service is located behind the load balancer no `X-Forwarded-Proto` header is added to the request. This means that the health check request gets a response that looks like similar to this:

HTTP/1.1 301 Moved Permanently
Date: Fri, 11 Dec 2015 08:24:49 GMT
Server: Apache/2.4.10 (Debian) PHP/5.6.15
Location: https://our-website.com/
Content-Type: text/html; charset=iso-8859-1
Transfer-Encoding: chunked

The problem is that the health doesn’t follow redirects and the response must return a 200 OK which means that it will interpret this response as a failure! And soon enough it’ll mark this backend service as failed and the load balancer will not forward any more requests to it (which means that we’ll see the error page described earlier). Your initial reaction might be the same as mine, can’t we just add the `X-Forwarded-Proto` to our health checker and this way trick Apache that we’re behind a load balancer? Unfortunately Google does not allow you to add a header to the health check so this won’t work. The workaround I came up with instead is to add a query parameter to the `path` used by the health checker, for example `/?healthCheck=true`. Now we need to update the `.htaccess` file to only redirect if `X-Forwarded-Proto` is not “https” AND the query string does not contain `healthCheck=true`. This is how it’s done:


RewriteEngine On
RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteCond %{QUERY_STRING} !.*healthCheck=true.*
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

And that’s it! This means that we’ll allow the Google health checker to access our page using HTTP but all other requests are redirected to HTTPS.

Conclusion

Even though I managed to get this working in the end I find it quite unfortunate that the Google health checker doesn’t support adding headers. This would have simplified not only this use case but also other cases where (for example) your health check is protected by basic authentication. Now one has to resort to various work arounds to get things working correctly.

3 thoughts on “Redirect HTTP to HTTPS in WordPress running behind a Google HTTP Load Balancer

  1. In my case X-Forward-For holds the visitor IP. Your solution is not working. I set up a header in my load balancer for http connection (SSL-Offloaded off). If HTTP:SSL-Offloaded is off then do rewrite.

Leave a Reply

Your email address will not be published. Required fields are marked *