How to do this with regular expression?

Old -> New
http://www.example.com/1.html   -> http://www.example.com/dir/1.html
http://www.example.com/2.html   -> http://www.example.com/dir/2.html
http://www.example.com/3.asp    -> http://www.example.com/dir/3.html
http://www.example.com/4.asp    -> http://www.example.com/dir/4.html
http://www.example.com/4_a.html -> http://www.example.com/dir/sub/4-a.html
http://www.example.com/4_b.html -> http://www.example.com/dir/sub/4-b.html

I've tried this:

Redirect 301 /1.html http://www.example.com/dir/1.html 
Redirect 301 /2.html http://www.example.com/dir/2.html 
Redirect 301 /3.asp http://www.example.com/dir/3.html 
Redirect 301 /4.asp http://www.example.com/dir/4.html 
Redirect 301 /4_a.html http://www.example.com/dir/sub/4-a.html 
Redirect 301 /4_b.html http://www.example.com/dir/sub/4-b.html 

This is a rather more general solution, so in that respect it might not be what you are after, but it should redirect the URLs in your question.

RewriteEngine on
RewriteRule ^(\d)\.(html|asp)$ /dir/$1.html [R=301,L]
RewriteRule ^(\d)_([a-z])\.html$ /dir/sub/$1-$2.html [R=301,L]

The first RewriteRule will match any single digit URL eg. 1.html or 7.asp and redirect to /dir/1.html or /dir/7.html respectively. The $1 backreference matches the first parenthesised sub pattern in the pattern that is matched.

The second RewriteRule matches a URL that contains a single digit followed by an underscore and then a single lowercase letter. The underscore is replaced with a hyphen in the redirect. eg. 6_g.html is redirected to /dir/sub/6-g.html.

Alternative (more restrictive) version

If a more restrictive version is required that only matches the specific URLs stated in the question then you could do something like the following instead. Although it is arguably more "complex"...

RewriteRule ^([12])\.html|([34])\.asp$ /dir/$1$2.html [R=301,L]
RewriteRule ^(4)_([ab])\.html$ /dir/sub/$1-$2.html [R=301,L]

The first rule would perhaps be easier to read if it was split into two. The two backreferences $1 and $2 in the first rule are mutually exclusive, when one is set, the other is empty, hence the presence of both in the substitution string.

If you are not already using mod_rewrite elsewhere in your .htaccess then mod_alias RedirectMatch directives may be preferable, although this does make the regex marginally more complex because of the slash prefix that needs to be matched...

RedirectMatch 301 ^/(?:([12])\.html|([34])\.asp)$ /dir/$1$2.html
RedirectMatch 301 ^/(4)_([ab])\.html$ /dir/sub/$1-$2.html
  • Why the neg vote? Is there anything wrong with the code/answer posted? How can it be improved? Having tested this code myself, it does appear to solve the OPs problem in a more compact, efficient manner than stated in the question, using a regex.
    – MrWhite
    Nov 24 '12 at 10:42
  • 1
    This will cover the examples. Some variations might be appropriate if the examples are over-simplified. \d+ rather than \d to match numbers that are more than one digit. A condition to prevent redirecting from files that actually exist. (html|asp) might be appropriate in both rules than than just the first. [a-z]+ instead of (html|asp) to match any extension at all rather than just the two used in the examples. Aug 31 '21 at 15:35
RewriteEngine on

RewriteCond %{HTTP_HOST} ^example\.com$ [OR]
RewriteCond %{HTTP_HOST} ^www\.example\.com$ 
RewriteRule ^1\.html$ "http\:\/\/example\.com\/dir\/1\.html" [R=301,L]

RewriteCond %{HTTP_HOST} ^example\.com$ [OR]
RewriteCond %{HTTP_HOST} ^www\.example\.com$
RewriteRule ^1_a\.html$ "http\:\/\/example\.com\/dir\/sub\/1\-a\.html" [R=301,L]

RewriteOptions inherit

That should work

  • is it possible to use like RewriteRule ^((1|2)\.html|(3|4)\.asp)$ dir/$1.html [R=301, L] ?
    – Eddie ZA
    Nov 17 '12 at 17:32
  • I wouldn't be able to tell you. Just copy that whole code and change what you need for each redirect. That's how i have in my websites. I also added the code for the one with a - on it. It's a bit trickier. That should work with no problems, just make your changes. Nov 17 '12 at 19:42
  • 1
    @guisasso: The reason for using mod_rewrite and a regex, as opposed to a mod_alias redirect, is presumably to simplify the solution. Your answer does the complete opposite.
    – MrWhite
    Nov 18 '12 at 12:38
  • 1
    Perhaps i know a lot less about the subject than i thought i did. Just trying to help. Nov 19 '12 at 15:16
  • 1
    @guisasso: No worries, we are all learning :). Just a few points about the code you have posted that rang some bells... 1. No need to check against HTTP_HOST unless we are dealing with multiple domains, which doesn't seem to be the case here. 2. No need to escape (forward) slashes, hyphens and colons in the RewriteRule substitution as they have no special meaning in the context they are used. 3. No need to surround the RewriteRule substitution in double quotes.
    – MrWhite
    Nov 20 '12 at 12:52

This is what I came up with:

<IfModule mod_rewrite.c>
  RewriteEngine on
  RewriteBase /
  RewriteRule ^((1|2)\.html|(3|4)\.asp)$ /dir/$1.html [R=301,L]
  RewriteRule ^(4_([a-b]))\.html$ /dir/sub/$1.html [R=301,L]
  • Do you not need a more general solution? Your solution would seem to work just for the 6 redirects you mention in your question. If this is the case then you could argue that your original Redirects are the preferred solution.
    – MrWhite
    Nov 18 '12 at 12:49
  • Actually, your solution does not appear to work - have you tried it?! $1 matches the first parenthesised sub pattern, which will be 1.html or 2.html or 3.asp or 4.asp, you then append .html which becomes /dir/1.html.html. I'll add an answer...
    – MrWhite
    Nov 18 '12 at 16:45
  • ...and the second rule fails to convert _ (underscore) in the source URL to - (hyphen) in the destination.
    – MrWhite
    Aug 31 '21 at 15:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.