Most of the time you are doing local web development without HTTPS and self-signed certificates but good-ol’ plain HTTP. For security and SEO reasons, HTTPS should be enabled in your production environment all the time. One important requirement for production environments is, that every incoming plain HTTP request has to be redirected to HTTPS. If you are using a frontend proxy for HTTPS/TLS termination and your web application is agnostic to the protocol in use, there should not be any difference accessing your application in local or production environment.

Different ways to do a HTTP to HTTPS redirect

In case when only a Apache webserver is used, it is difficult to provide a configuration which enables HTTPS only in production but uses plain HTTP in local environments:

  1. As described above, you can use a frontend proxy for the TLS termination.
  2. When deploying your web application, your .htaccess contains the RewriteRule for redirecting from HTTP to HTTPS. This has some smaller drawbacks: Either you have two different .htaccess files or you are rendering different versions of the .htaccess files – one for local developement and one for the production environment. In both cases the web application is mutable which may lead to higher complexity as the (infrastructure) code base of your application differs between both environments
  3. You are using conditionals inside your .htaccess files. This is what I am writing about 🙂

Using conditionals inside your .htacces file

With Apache 2.4 and newer you can use the <If> tag inside your Apaches’s server or virtual host configuration and inside the <Directory> tag. Also, it can be used inside the .htaccess file. The new expression syntax allows you to define complex conditional statements. In addition to that, it is possible to use regular expressions inside the statements, which comes in handy for my use case: I am using xip.io for local development. With a matching regular expression the code snippet can be copied to each .htaccess file of my projects.

Hitting the wall

The whole idea is to do 301 redirect if HTTPS is not used and the server name does not match any kind of *.xip.io. It took my a while to realize that Apache atleast until version 2.4.39 do no support the RewriteCond inside an <If> block. The following code does not completely work on 2.4.18 and 2.4.39:

<If "%{HTTP_HOST} != 'myhost.xip.io'">
    RewriteCond %{HTTPS} !=on
    RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
</If>

# all following lines won't be evaluated
RewriteRule ^$ /index.php [L]

The redirect from HTTP to HTTP does work but all following RewriteRules won’t have any effects after redirect has been done. My guess is that the RewriteCond statement does not take into effect that it is inside a <If> block and does not know anything about the current scope.

Using if statements properly

It took me a while to come up with a working snippet:

## Redirect every HTTP request in non-local environments to HTTPS
# Nested conditions are available since 2.4.26; Ubuntu in WSL is on Apache 2.4.16
<If "%{HTTPS} == 'off' &amp;&amp; ! %{HTTP_HOST} =~ /.*.xip.io/">
    RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
</If>

RewriteRule ^$ /index.php [L]

As you can see, I have replaced the RewriteCond with an inline statement. This version can be also written in a more elegant way. Since Apache 2.4.26, nested if statements can be used:

## Redirect every HTTP request in non-local environments to HTTPS
<If "%{HTTPS} == 'off'">
	<If "! %{HTTP_HOST} =~ /.*.xip.io/">
		RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
	</If>
</If>

In case you are not using xip.io, you can replace the string .*.xip.io with localhost.

Summary

This blog post has shown you how to use conditional statements inside your .htaccess file. You can now easily redirect to HTTPS in your production environments but also use plain HTTP on your local developement machine.

Categories: Apache