Using Apache's RewriteEngine to redirect requests to other URLS and to https://

by Marion Bates <mbates at whoopis.com>

Scenario: You've changed the location/path of a webpage on your server, but you don't want to break links to the old location. You could just use symlinks, but you want the new URL to appear in the user's browser so that new links/bookmarks point to the new, real location of your page.

AND/OR you want requests to http://www.domain.com/sensitive.html to be redirected to the https:// (SSL) version of that page. Especially if that page is in an Apache realm that requires a login, and you want the login to be encrypted.

This guide will explain how to do both of those things.

NOTE: Strykar <strykar at hackerzlair dot org> writes:

This is what works for me when placed in DocumentRoot:
root@host:/var/www/htdocs# cat .htaccess
RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteRule ^web_mail(.*) https://%{SERVER_NAME}/web_mail$1 [R,L]
A common mistake is to have a leading slash which is meant when this info is put in Apache's config file and not htaccess.
RewriteRule ^/web_mail(.*) https://%{SERVER_NAME}/web_mail$1 [R,L]

Readers, if you find mistakes in, or better solutions than, this howto, please email webmaster so I can post additions and corrections like this one. :)


Step 1: Make sure mod_rewrite is being loaded; in other words, make sure that /etc/httpd/conf/httpd.conf contains the line:

LoadModule rewrite_module modules/mod_rewrite.so

Step 2: Back up and then edit httpd.conf (you will need to be root). If you're using VirtualHost directives (see link), then find the VirtualHost block that corresponds to the url you want to rewrite. Otherwise put it in wherever you set the options for your site, or put it in a separate Directory block -- whatever you prefer. Add the line:

RewriteEngine On
Also, if it's not already set, you will need to add:
Options +FollowSymlinks

Step 3: Rules. If you're a regex king then you can get all kinds of fancy with these. I am just going to post a couple of simple example here:

RewriteRule ^/oldpath/(.*)$ /newpath/$1 [R]
In other words, http://www.domain.com/oldpath followed by anything -- /oldpath/oldpage.html, /oldpath/index.php, or just /oldpath/ by itself -- will be rewritten to http://www.domian.com/newpath (followed by whatever followed the original URL). The R in brackets means "rewrite the URL in the user's browser." You can do "invisible" rewrites by leaving this off.
RewriteRule ^(.*)\.html$ $1.php [R=permanent]
This is nice for when you re-do your entire website in php, but you don't want to break links to http://www.yoursite.com/somepage.html.

SSL Redirects: Are more complicated than that. In httpd.conf you make the (relatively-straightforward) rule, preceded by a conditional:

RewriteCond   %{SERVER_PORT}  !^443$
RewriteRule ^/secret(.*)$ https://www.domain.com/secret/$1 [L,R]
The RewriteCond line says, "if the request is not already going to port 443 (the https port), then rewrite it." This prevents the server from doing redundant rewrites on URLs that are already correct. The rule itself is very similar to the previous example, except that it rewrites with the whole domain name so that it can include the https: part. The bracketed R is explained above, the additional L means "last rule" which I guess tells Apache to stop running the rewrite module or something. I really don't know.

But you're not done yet. Now you need to edit /etc/httpd/conf.d/ssl.conf. We're assuming here that you've already got certificates configured and whatnot (https: actually works). Here's what my Directory block looks like:

<Directory "/home/www.domain.com/html/secret/">
        Options +Indexes
        SSLOptions           +StrictRequire
        SSLRequire           %{SSL_CIPHER_USEKEYSIZE} >= 128
        Order deny,allow
        deny from all        
        ########
        # These next five lines are for requiring an Apache login
        AuthType Basic
        AllowOverride AuthConfig
        AuthUserFile /etc/httpd/conf/users
        AuthName "Restricted Area"
        require valid-user
        ########
        satisfy any
</Directory>
If you're not using Apache realm authentication, then leave out those five lines. If you ARE doing realms, but using separate .htaccess files, there is a way to do the rewrites and keep the .htaccess files but I couldn't make it work so I just moved the directives into httpd.conf. Sorry.


NOTE: A reader, Bonnie Templeton <bonnie dot templeton at colorado edu>, had trouble with this procedure, and found a solution. Reprinted here in case it is helpful to others.

I just read your posting "Using Apache's RewriteEngine to redirect requests to other URLS and to https://"

I am trying to do an SSL Redirect.

I know the rewrite engine is on because I get a rewrite log. The log says "[per - dir /www/htdocs/test] pass through /www/htdocs/test/main"

It does not rewrite. This is a SUSE Linux server. Going from you article and others I added a directory section to default-server.conf that says

<Directory "/www/htdocs/test">
        RewriteEngine on
        RewriteBase /test/
        Options +FollowSymLinks
        Order allow, deny
        Allow from all
        RewriteCond % {SERVER_PORT} !^443$
        RewriteRule ^/main(.*)$ https://vault.colorado.edu/test/main/$1 [L,R]
</Directory>
Then in the SSL-global.conf I added the section
<Directory "/www/htdocs/test/">
        Options +Indexes
        SSLOptions +StrictRequire
        SSLRequire % {SSL_CIPHER_USEKEYSIZE} >=128
        Order deny, allow
        Deny from all
        Satisfy any
</Directory>

We are not using Apache realm authentication. I put the Directory section within the section at the bottom of that section.

I am not getting any error messages, the rewrite is just doing nothing.

I was stumped. A few days later, she wrote back:
I got it to work, but I do not know why. I found similar problem on the internet and the poster solved it by using variables instead of writing out the directory structure. I tried it and it worked. Here is what I ended up with.

default-server.conf that says

<Directory "/svr/www/htdocs/test">
        RewriteEngine on
        Options +FollowSymLinks
        Order allow, deny
        Allow from all
        RewriteCond %{SERVER_PORT} !^443$
        RewriteRule ^.*$ https://%{SERVER_NAME}%{REQUEST_URI} [L,R]
</Directory>
This worked. The section in the SSL conf file was not necessary, so I took it back out. I do not understand why the variables work and the specified path does not. As soon as I put the variables in and looked at the rewrite log, the engine started processing the lines when it had ignored them before.

ANOTHER NOTE: "Tron" writes:

I am running a CentOS Linux server and noticed the same thing [Bonnie] did, the rewrite does not seem to work unless variables are used. I modified my httpd.conf file accordingly and it worked. (I also did not need to modify my ssl.conf file, just as she did not.)

I just wanted to let you know that this seems to be on more than just one person's server.

ANOTHER NOTE: "Thomas" writes:

I can confirm the bottom section of your apache how-to for redirecting SSL using mod_rewrite. The redirect section has to go in the default httpd.conf file and not the ssl subfile like you indicated.

Just wanted to let you know that is what I had to do as well.

ANOTHER NOTE: "Alan" writes:

In my case I needed to redirect to one especific cgi script. I only had to update httpd.conf as follows:
ServerRoot "/etc/httpd"
RewriteLog "logs/rewrite.log"   #This is helpful for troubleshooting
RewriteLogLevel 1               #Set it to 9 for testing purposes

<Directory "/var/www/cgi-bin">
   AllowOverride None
   Options FollowSymLinks      #This was necessary
   Order allow,deny
   Allow from all
#
#  Redirect to https channel if http is requested
#
   RewriteEngine On
   RewriteBase /cgi-bin
   RewriteCond %{SERVER_PORT} !^443$
   RewriteRule ^nameofcgiscript\.cgi$ https://securewebserver.domain.com/cgi-bin/nameofcgiscript.cgi
</Directory>

Thanks folks, keep the comments coming and I will continue to update this page accordingly! :)


References: