Wandering Thoughts archives


How I want to do redirects in Apache, especially in .htaccess files

One of the irritating things about Apache's handling of redirects is that it makes what I suspect is the most common case the more verbose and potentially complex to handle, especially if you want to put it in a .htaccess file instead of in the main Apache configuration. Since I just had to go through working this out for our use, I'm going to write it down in case I need it later.

Suppose that you have an Apache server and you want to redirect the local URL /~user/ off to http://someserver/url2/ by putting a .htaccess file in the user's web home directory; possibly you are even redirecting your own home page somewhere. Then what you generally want in the .htaccess file is:

RedirectMatch permanent /~user/.* http://someserver/url2/

This redirects /~user/ and all URLs under it to the single URL http://someserver/url2/, with a permanent redirection (status code 301) instead of Apache's default of temporary redirection (status code 302).

It's worth talking about what happens with several variants. First:

Redirect permanent /~user/ http://someserver/url2/

This is the same as the following RedirectMatch version, which may make what's going on clearer:

RedirectMatch permanent /~user/(.*) http://someserver/url2/$1

The difference is that this redirects a request for, for example, /~user/bar/ off to http://someserver/url2/bar instead of the base URL we provided. If you're going to do this you should know that Redirect has the same issue with matching trailing /'s on each side as the ProxyPass directive does.

The more tricky case is what you need to put in the URL-path (the second argument here) in a .htaccess file in a subdirectory. Suppose you write the redirect in your .htaccess as:

Redirect permanent / http://someserver/

What you will get from a request for /~user/ is a redirection to http://someserver/~user/. Despite this .htaccess being located at /~user/ (logically speaking) and only applying to URLs under it, everything after the / in the original URL-path has been taken off and put on the end of the target URL. Even if this actually works and is what you want, it's confusing and you should write the redirection with the real URL-path and URL it applies to spelled out explicitly.

(You can create even more confusing versions with RedirectMatch.)

This is mentioned in the documentation for the Redirect directive, although with different amounts of clarity and thoroughness depending on the version of Apache. See, for example, the Apache 2.4 Redirect documentation. And yes, this is inconsistent with how RewriteRule matches the URL-path.

As with rewrite rules, if you want to redirect just the main URL and not any sub-URLs you need to be explicit about this with a RedirectMatch:

RedirectMatch permanent /~user/$ http://someserver/url2/

This redirects only requests for /~user/ itself; a URL like /~user/page.html will either fail with a 404 or wind up showing the real page on your server (if it exists).

(Because the Redirect directives always use the full URL-path, all of this applies to to them regardless of whether they're in .htaccess files or in the main Apache configuration. All that being in a .htaccess file does for Redirect et al is limit what URL-paths they can apply to.)

Sidebar: Redirect and partial matches

Unlike rewrite rules, a Redirect only matches whole components, so that a Redirect for /~user/foo will not match a request for /~user/foobar. This is mentioned in the documentation from Apache 2.2 onwards and probably applied even before then (but I don't have any Apache 2.0 or earlier servers to test with). This does not apply to RedirectMatch, which will match partial components (so if you have a URL-path of /~user/foo it will match /~user/foobar).

You might wonder what happens to the 'bar' portion when you have a partial-component match in RedirectMatch. Why, I'm glad you asked. The answer is that it disappears (it is not appended to the target URL). In fact the behavior of RedirectMatch is basically that there is an implicit '.*' on the end of the URL-path, so my recommended RedirectMatch can in fact be rewritten as:

RedirectMatch permanent /~user/ http://someserver/url2/

Once again, I recommend being explicit and not using this trick.

web/ApacheRedirectHtaccess written at 00:52:37; Add Comment

Page tools: See As Normal.
Login: Password:
Atom Syndication: Recent Pages, Recent Comments.

This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.