Apache rewrite rules to force secure/non-secure pages

on

If you’re running an Apache server there’s no need to hard code your links to force https/http or write server-side code. A few simple rewrite rules will allow you to redirect your secure pages to https and your non-secure pages back to http. We’ll just check the server port to see if it’s using secure port 443 and redirect the page accordingly.

For instance, to redirect the checkout section to use https just use the following in your httpd.conf file (or vhost.conf if using Plesk):

<Directory "/var/www/html">
RewriteEngine on
Options +FollowSymLinks
Order allow,deny
Allow from all
RewriteCond %{SERVER_PORT} !^443$
RewriteRule \.(gif|jpg|jpeg|jpe|png|css|js)$ - [S=1]
RewriteRule ^checkout(.*)$ https://%{SERVER_NAME}%{REQUEST_URI} [L,R]
</Directory>

Of course, you need to make sure that the directory path is valid for your setup. This will force http://www.example.com/checkout to redirect to https://www.example.com/checkout

If you want multiple pages change the RewriteRule to something like:

RewriteRule ^(checkout|login)(.*)$ https://%{SERVER_NAME}%{REQUEST_URI} [L,R]

Just separate the page/folder names you want with the pipe (|) character. Read up on your regular expressions if this doesn’t make sense to you.

The first 4 lines are necessary Apache options we need to use for this to work properly. You can read the Apache manual for more information on those. The RewriteCond is testing if the server port is not 443, meaning we are not requesting via https. The first RewriteRule will ensure that files loaded within the page such as images, CSS, and JavaScript are loaded using the same protocol as the page itself. It matches on those file extensions and then uses the [S] (skip) flag to skip the next rule. This will help to avoid mixed content warnings in browsers. The list isn’t comprehensive so be sure to add to it as necessary. The RewriteRule under that uses a regular expression to test if our URI starts with checkout (or login in the second example) and if so it redirects the request to the same URL but this time using SSL. The [L] flag means that rewrites will end on this rule and the [R] flag means that you redirect the page so that the address changes in the browser. Removing the [R] flag will cause the page to be served via https but the URL to still show http.

This is all well and good but it’s only half of the equation. Once a user leaves a secure page then we should also be able to forward them back to regular http. To do this we just basically set up the reverse of the RewriteCond and RewriteRule:

RewriteCond %{SERVER_PORT} ^443$
RewriteRule \.(gif|jpg|jpeg|jpe|png|css|js)$ - [S=1]
RewriteRule !^(checkout|login)(.*)$ http://%{SERVER_NAME}%{REQUEST_URI} [L,R]

Notice that instead of checking to see if we are not using port 443 in the RewriteCond we are checking to see if we are using it. Then in the RewriteRule we are making all pages that are not checkout or login redirect to http.

I’m pretty sure if you are using httpd.conf or a .htaccess file you can put these two lines directly below the original ones inside of the <Directory> block but I haven’t tested that. Since I’m using Plesk I needed to put the original block in my vhost.conf file (used for configuration of non-secure pages) and the entire block again but this time with the second set of rules in my vhost_ssl.conf file (used for configuration of secure pages). Also, if you are using any of the .conf files you will need to restart apache for your changes to take place.

And just as a final note, these steps will not help you in setting up your SSL certificate. You will already need to have that set up and functional before using this.

References:
Apache mod_rewrite manual
Ask Apache rewrite tips
whoopis.com tutorial
My original post with this answer on Stack Overflow

5 Comments

  1. Alex

    …and one more try to get this displayed without any omissions (Jonathan: it’d be great if you could post a a syntax legend – thanks!):

    Thanks for the article! It’s the best one I have found since it addresses returning to HTTP for pages that don’t require SSL. Unfortunately though, I couldn’t use your code as posted in my .htaccess file. After making the appropriate substitutions in the code (namely the absolute file system path in the Directory directive and name of the form page (i.e., replacing “checkout”) in the RewriteRule), I received the following:

    (Note: I’m on a Godaddy shared hosting (Linux) account currently running Appache 1.3.33)

    The Directory directive resulted in the following error:
    Internal Server Error
    The server encountered an internal error or misconfiguration and was unable to complete your request.

    Once I removed that line, the Order allow,deny directive, caused the next error:
    Forbidden
    You don’t have permission to access /page.htm on this server.

    Once I removed that line as well, I received:
    Redirect Loop
    Firefox has detected that the server is redirecting the request for this address in a way that will never complete.

    I was able to bypass this error by removing both occurances of the RewriteRule \.(gif|jpg|jpeg|jpe|png|css|js)$ – [S=1] directive. This left the following code:

    #Directory “/my/absolute/filepath/html”
    RewriteEngine on
    Options +FollowSymLinks
    Order allow,deny
    Allow from all
    RewriteCond %{SERVER_PORT} !^443$
    #RewriteRule \.(gif|jpg|jpeg|jpe|png|css|js)$ – [S=1]
    RewriteRule ^(nameOfFormPage)(.*)$ https://%{SERVER_NAME}%{REQUEST_URI} [L,R]

    RewriteCond %{SERVER_PORT} ^443$
    #RewriteRule \.(gif|jpg|jpeg|jpe|png|css|js)$ – [S=1]
    RewriteRule !^(nameOfFormPage)(.*)$ http://%{SERVER_NAME}%{REQUEST_URI} [L,R]
    #/Directory

    However, being that I had disabled the RewriteRule \.(gif|jpg|jpeg|jpe|png|css|js)$ – [S=1] directives, the server delivered the desired secure page via HTTPS and all files referenced from within this page (images etc.) via HTTP, resulting in an appropriate browser warning (something to the extent that the page contains secure and unsecure items). Thus, I came up with the following, which made everything work for me as desired (that is, the page (and its references) I want to have served via HTTPS is served as such and all other pages are served via HTTP — at least as far as I can tell!):

    RewriteEngine on
    RewriteCond %{SERVER_PORT} !^443$
    RewriteRule ^(nameOfFormPage)(.*)$ https://%{SERVER_NAME}%{REQUEST_URI} [L,R]

    RewriteCond %{SERVER_PORT} ^443$
    RewriteCond %{REQUEST_URI} ^.*\.htm$
    RewriteRule !^(nameOfFormPage)(.*)$ http://%{SERVER_NAME}%{REQUEST_URI} [L,R]

    Note the new RewriteCond %{REQUEST_URI} ^.*\.htm$ directive in the second block, which causes the server to revert to HTTP only once a request is made on port 443 (HTTPS) for .htm files with filenames other than the designated page.

    At this point the only thing I’m wondering is:

    A) Does this solution possibly have any side effect I haven’t noticed?
    B) Is this solution efficient or is there perhaps a better way?
    C) Is the use of the .htaccess file for switching to HTTPS and back at all appropriate, given that pretty much all other references I was able to find on this subject elsewhere on the web only dealt with going to HTTPS? — How do other people solve the going back to HTTP?

    For a better understanding of how to use the RewriteRule and RewriteCond directives, I recommend the following URLs:

    http://httpd.apache.org/docs/trunk/mod/mod_rewrite.html#rewritecond
    http://httpd.apache.org/docs/trunk/mod/mod_rewrite.html#rewriterule
    http://httpd.apache.org/docs/trunk/mod/directive-dict.html
    http://www.pcre.org/

  2. Jonathan Dean

    Alex, I don’t think the Directory part is necessary in a .htaccess file because the location of that file already implies the directory structure. What did the rest of it look like when you got the Forbidden error? I’m assuming that it was trying to resolve the path to your file system root and not your web root which is why you got the error. I’m not enough of an expert on Apache to really know how well your method holds up regarding efficiency but it would be nice to figure out a good method for those using shared hosting out there.

    p.s. – I’m just running WordPress so I’m sure there’s some information out there regarding syntax in comments. I’ll have to take a look sometime since I’ve only ever posted code in the post itself and not in the comments.

  3. ema

    Another (possible) solution:

    This redirects urls (donate/whatsoever) to https

    RewriteCond %{SERVER_PORT} !^443$
    RewriteRule ^donate(.*)$ https://%{SERVER_NAME}%{REQUEST_URI} [L,R]

    And this redirect to http in the other cases (files are not switched back to avoid IE messages)

    RewriteCond %{SERVER_PORT} ^443$
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_URI} !donate(.*)$
    RewriteRule (.*) http://%{SERVER_NAME}%{REQUEST_URI} [L,R]

    Other references:
    http://www.askapache.com/htaccess/http-https-rewriterule-redirect.html
    http://www.webmasterworld.com/apache/3516509.htm

  4. Dave Allan

    Brilliant collection of posts. These incantations have now provided me with exactly the solution I was looking for, and taught me a lot about mod_rewrite.

    Thanks very much guys!

Leave a Reply

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

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>