301-Redirect-for-HTTPS-traffic-in-WordPress-lazybatman

Have you ever been in need of permanently redirecting traffic from HTTPS to HTTP? There are many cases when you may need it – the most popular one is when your content is indexed by search engines and you don’t want HTTPS version appear there. You can get duplicated cached pages and be penalized for ranking, so unless you have a specific reason for all your pages to be available under HTTPS it definitely makes sense to think about redirection.

Easy case

The most simple case is when you need to redirect *all* your traffic. If you ask Google you will quickly find a solution like this one:

RewriteCond %{SERVER_PORT} ^443$
RewriteRule ^(.*)$ http://yourdomain.com/$1 [R=301,L]

Just these 2 lines in .htaccess file will do the magic for you. The first line could be replaced by other conditions depending on the case (for example you may have SSL on different port), but it’s not a topic of this post.

The problem

The situation I’ve faced recently was a bit complex than this simple case. There were 2 additional things which matter:

  1. The site is running WordPress with nice permalinks.
  2. Not all HTTPS traffic should be redirected to HTTP, but a few pages should remain available under HTTPS.

At the first glance the solution is simple – just by adding RewriteCond with a regular expression you will filter the traffic and will redirect only desired traffic. Let’s say you need to redirect all pages except https://yourdomain.com/sample-page. Then the lines will look like this:

RewriteCond %{SERVER_PORT} ^443$
RewriteCond %{REQUEST_URI} !^\/sample-page
RewriteRule ^(.*)$ http://yourdomain.com/$1 [R=301,L]

This will work in case you don’t have more rewrite rules in your .htaccess (or other webserver config files). However it won’t work with WordPress Drupal or other framework with rewrite rules in .htaccessfile. WordPress by default have the following rules:

RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]

Of course our redirect rules should be placed before default WordPresslines. However the result could be considered as unexpected – all the URLs except https://yourdomain.com/sample-page are redirected correctly to their HTTP versions, but this exceptional URL is also redirected to … the site home page http://yourdomain.com/. In order to explain why it happens we need to understand how rewrite rules work in this case. If you are interested in trying this out yourself you will need to play with RewriteLog and RewriteLogLeveldirectives – the logs will help understanding.

The solution

So what happens when https://yourdomain.com/sample-page is fetched from the server.

  1. We have %{REQUEST_URI} = “/sample-page”, so it doesn’t match our RewriteCond pattern (as there is a negation “!” in front of it). So it means that no rewrite happens at this step
  2. As soon as “/sample-page” is not a file or directory the second RewriteRule works and the URI is rewritten with “/index.php” and internal redirect takes place.
  3. Now the new URI (which is now “/index.php”) is checked against all the rules we have again. And as soon as it matches both RewriteCond (the port is still 443 and the URI doesn’t start with “sample-page”) the 301 redirecting RewriteRule takes place and it redirects to http://yourdomain.com/index.php.

The 3rd step is pretty hard to guess, that’s why I want to share this case. The solution around this problem is simple, we just need to add one more RewriteCond to avoid redirecting “/index.php” the second time. Here is the final code:

RewriteCond %{SERVER_PORT} ^443$
RewriteCond %{REQUEST_URI} !^\/sample-page
RewriteCond %{REQUEST_URI} !^\/index\.php$
RewriteRule ^(.*)$ http://yourdomain.com/$1 [R=301,L]

As it was mentioned this solution will not only work for WordPress , but for every CMS/framework with default rewrite rules like these. I really hope this will save someone’s time.

LEAVE A REPLY

Please enter your comment!
Please enter your name here