Php – .htaccess doesn’t redirect to www-prefixed page properly

.htaccessapache-2.2http-headersmod-rewritePHP

I'm trying to redirect an url without www. to www.version (example.com to www.example.com).
I use the usual

RewriteCond %{HTTP_HOST} ^example\.com [nc]
RewriteRule (.*) http://www.example.com/$1 [R=301,L]

This works on all my other projects. However on this particular site, it ends with a redirect loop. Here's the weird part: I tried to curl the non-www version to see what headers it sends using
curl --get http://example.com --dump-header domain.header > domain.html.
The header file looked like this:

HTTP/1.1 301 Moved Permanently
Date: Mon, 06 Jun 2011 14:45:16 GMT
Server: Apache/2.2.16 (Debian)
Location: http://example.com/
Vary: Accept-Encoding
Content-Length: 310
Content-Type: text/html; charset=iso-8859-1

However, the resulting HTML file was this:

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>301 Moved Permanently</title>
</head><body>
<h1>Moved Permanently</h1>
<p>The document has moved <a href="http://www.example.com/">here</a>.</p>
<hr>
<address>Apache/2.2.16 (Debian) Server at example.com Port 80</address>
</body></html>

(note the address difference between the files)
Does anybody know how to fix this (and what the hell is causing it)?
Any other url rewriting directives work fine.

EDIT: rewrite log contained this: (the site is accessed by a lot of people so the rewrite log got quite long, I'm not 100% sure if this is the right part)

192.168.1.221 - - [06/Jun/2011:17:49:32 +0200] [example.com/sid#b797f948][rid#b7d2c1c8/initial] (3) [perdir /var/www/oup/81/] strip per-dir prefix: /var/www/oup/81/ ->
192.168.1.221 - - [06/Jun/2011:17:49:32 +0200] [example.com/sid#b797f948][rid#b7d2c1c8/initial] (3) [perdir /var/www/oup/81/] applying pattern '(.*)' to uri ''
192.168.1.221 - - [06/Jun/2011:17:49:32 +0200] [example.com/sid#b797f948][rid#b7d2c1c8/initial] (2) [perdir /var/www/oup/81/] rewrite '' -> 'http://www.example.com/'
192.168.1.221 - - [06/Jun/2011:17:49:32 +0200] [example.com/sid#b797f948][rid#b7d2c1c8/initial] (2) [perdir /var/www/oup/81/] explicitly forcing redirect with http://www.example.com/
192.168.1.221 - - [06/Jun/2011:17:49:32 +0200] [example.com/sid#b797f948][rid#b7d2c1c8/initial] (1) [perdir /var/www/oup/81/] escaping http://www.example.com/ for redirect
192.168.1.221 - - [06/Jun/2011:17:49:32 +0200] [example.com/sid#b797f948][rid#b7d2c1c8/initial] (1) [perdir /var/www/oup/81/] redirect to http://www.example.com/ [REDIRECT/301]

The access log line (probably the right one):

192.168.1.221 - - [06/Jun/2011:17:49:32 +0200] "GET / HTTP/1.1" 301 555 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.77 Safari/534.24"

The definition of the virtualhost:

<VirtualHost *:80>
        ServerAdmin webmaster@localhost
        ServerName example.com
        ServerAlias example.com www.example.com
        DocumentRoot /var/www/example/
        <Directory />
                Options FollowSymLinks
                AllowOverride All
        </Directory>
        <Directory /var/www/example/>
                Options Indexes FollowSymLinks MultiViews
                AllowOverride All
                Order allow,deny
                allow from all
        </Directory>

        ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
        <Directory "/usr/lib/cgi-bin">
                AllowOverride All
                Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
                Order allow,deny
                Allow from all
        </Directory>

        ErrorLog ${APACHE_LOG_DIR}/error.log

        # Possible values include: debug, info, notice, warn, error, crit,
        # alert, emerg.
        LogLevel warn

        CustomLog ${APACHE_LOG_DIR}/access.log combined

</VirtualHost>

EDIT2: okay, I just figured out that if I do this (resigned and attempted to redirect this without .htaccess):

//if clause determining that we're running on example.com and not www.example.com
header('HTTP/1.1 301 Moved Permanently');
header('Location: http://www.example.com' . $_SERVER['REQUEST_URI']);
header('Connection: close');

It causes EXACTLY THE SAME redirect loop. Seriously, what the hell? Does anyone have an idea what might possibly be causing this?

Best Answer

What strikes me as odd is the Location: http://domain.cz/ header line reported by CURL. You never redirect to that domain. The redirect log also doesn't contain any mention of it.

Somehow the Location header seems to be altered after modrewrite does its stuff, and since you tried changing the header with PHP as well, the Location header is apparently changed after the request is processed. The only explanation I can think of is that you are modifying the location header with mod_header somewhere.

Did you check all configuration files (httpd.conf, the included .conf files, and the .htaccess file) if you somewhere find a line similar to this:

Header set Location (...)

or

Header edit Location (...)
Related Topic