.htaccesshttp-redirectmod-rewrite

Position of Redirects and RewriteRules in .htaccess for WordPress site


I need to redirect URLs from an old Drupal site to a new WordPress site with a different domain name. The Drupal site contains thousands of genetic tests, but the client will only continue to offer a hundred tests on the WordPress site.

In the .htaccess file on the WordPress site I created the redirects and tested them all. They're working fine. Here's an example:

Redirect 301 /test/detail/kptn-defect-syndrome-targeted-testing https://www.example.org/genetic_test/kptn-disorder-kptn-targeted-testing/ 
Redirect 301 /test/detail/herc2-defect-syndrome-targeted-testing https://www.example.org/genetic_test/herc2-disorder-herc2-targeted-testing/ 
Redirect 301 /test/detail/hemophilia-b-f9-targeted-testing https://www.example.org/genetic_test/hemophilia-b-f9-targeted-testing/

I also created a wildcard redirect so that all other tests that are no longer being offered will resolve to their new lab page:

RedirectMatch 301 /test/(.*) https://www.example.org/about-our-lab/

This too is working fine.

My question is where in the .htaccess file do I place the rewriterule pointing the old domain to the new domain? And does it all come before the WordPress generated directives? Here's my rewriterule:

#RewriteCond %{HTTP_HOST} ^(?:www\.)old\.example$ [NC]
#RewriteRule ^https://www.newexample.org%{REQUEST_URI} [L,R=301]

I want to get this right so when I shut down the old Drupal site and point the domain to the server with the WordPress site everything works without issues. Currently I have all redirects above the WordPress directives. Would the rewriterule go above also, and would it go above the redirects?

UPDATE:

This is not working. I created the .htaccess file like this:

RewriteCond %{HTTP_HOST} ^(www\.)?newexample\.org [NC]
RewriteRule ^ - [S=52]

RewriteRule ^(index\.php/)?test/detail/factor-v-leiden-f5-targeted-testing$ https://www.newexample.org/genetic_test/factor-v-leiden-f5-targeted-testing/ [R=301,L]
RewriteRule ^(index\.php/)?test/detail/dystonia-6-torsion-thap1-targeted-testing$ https://www.newexample.org/genetic_test/torsion-dystonia-6-thap1-targeted-testing/ [R=301,L]
RewriteRule ^(index\.php/)?test/detail/deafness-and-myopia-slitrk6-targeted-testing$ https://www.newexample.org/genetic_test/deafness-and-myopia-slitrk6-targeted-testing/ [R=301,L]
(... and so on ...)

# Redirect all other tests to generic page
RewriteRule ^(index\.php/)?test/ https://www.newexample.org/about-our-lab/ [R=301,L]

# Redirect everything else to new domain (same URL)
RewriteRule ^ https://www.newexample.org%{REQUEST_URI} [R=301,L]

Worpress directives below.

But it is not working when I test newexample.org/test/detail/factor-v-leiden-f5-targeted-testing it does not redirect me to the new test page corresponding to the end of the applicable RewriteRule. Instead WordPress responds "Post not found."

Why is this not working?

UPDATE:

It does work as shown above. To test it I had to change the RewriteCond to another domain and comment out the generic RewriteRule. Thanks for pointing that our Mr White.

All is well. And many many thanks to Mr White.


Solution

  • ...when I shut down the old Drupal site and point the domain to the server with the Wordpress site everything works without issues.

    If the old and new domains are going to be pointing to the same host then you need to convert all the redirects you have just implemented that use mod_alias Redirect and RedirectMatch directives to use mod_rewrite RewriteRule instead.

    This is because mod_rewrite is processed first, despite the apparent order of the directives in the .htaccess file, so your generic redirect (that uses mod_rewrite) to redirect all remaining URLs would be processed first - resulting in 2 redirects for all your /test/ URLs. Ok, it will still "work", but it's not ideal.

    Generally, you should not mix redirects from both mod_alias and mod_rewrite in the same context as you can get unexpected redirects due to the order of processing.

    Another potential issue with the redirects you have implemented is that you aren't checking the hostname, so you can't have URLs that start /test/ on the new domain. You don't need to check the hostname for every rule, as they can be "grouped" together.

    Would the rewriterule go above also, and would it go above the redirects?

    All these redirects (that use mod_rewrite) must go before the WordPress code block, otherwise they will simply never be processed. mod_alias redirects could potentially be placed anywhere.

    #RewriteCond %{HTTP_HOST} ^(?:www\.)oldexample\.org$ [NC]
    #RewriteRule ^https://www.newexample.org%{REQUEST_URI} [L,R=301]
    

    You are missing a space between the RewriteRule pattern (1st arg) and the substitution string (2nd arg), so this will fail to redirect as written.

    You are also missing the ? after the parenthesised subpattern in the CondPattern, assuming the www subdomain is intended to be optional.

    Bringing the above points together, the redirects could be written like this:

    # If the "new domain" is already requested then skip the next 102 rules
    RewriteCond %{HTTP_HOST} ^(www\.)?newexample\.org [NC]
    RewriteRule ^ - [S=102]
    
    RewriteRule ^test/detail/kptn-defect-syndrome-targeted-testing$ https://www.example.org/genetic_test/kptn-disorder-kptn-targeted-testing/ [R=301,L]
    RewriteRule ^test/detail/herc2-defect-syndrome-targeted-testing$ https://www.example.org/genetic_test/herc2-disorder-herc2-targeted-testing/ [R=301,L] 
    RewriteRule ^test/detail/hemophilia-b-f9-targeted-testing$ https://www.example.org/genetic_test/hemophilia-b-f9-targeted-testing/ [R=301,L]
    # etc....
    
    # Redirect all other tests to generic page
    RewriteRule ^test/ https://www.example.org/about-our-lab/ [R=301,L]
    
    # Redirect everything else to new domain (same URL)
    RewriteRule ^ https://www.newexample.org%{REQUEST_URI} [R=301,L]
    
    # WordPress directives follow...
    

    You just need to be careful to count the number of rules to skip. I've used 102 above as you said they would "continue to offer a hundred tests" (so, 100 redirects), plus the 2 generic redirects that follow.

    You don't need to repeat the RewriteEngine On directive, since that already occurs later in the file (in the WordPress code block). It is the last instance of this directive that does anything anyway, so if you repeat RewriteEngine it is arguably misleading.

    Alternatively, you could have a second .htaccess file in a /redirect-old-urls subdirectory and internally rewrite all requests for the old domain to this subdirectory first. This has an advantage of keeping all the "old" redirects out of the main .htaccess file, with only a minimal change to the main .htaccess file.

    For example:

    In the main .htaccess file in the root:

    # /.htaccess
    
    # Requests for the old domain are internally rewritten to a subdirectory
    RewriteCond %{HTTP_HOST} ^(www\.)?oldexample\.org [NC]
    RewriteRule (.*) redirect-old-urls/$1 [L]
    
    # WordPress directives follow...
    

    Then, in the /redirect-old-urls/.htaccess file:

    # /redirect-old-urls/.htaccess
    
    RewriteEngine On
    
    RewriteRule ^test/detail/kptn-defect-syndrome-targeted-testing$ https://www.example.org/genetic_test/kptn-disorder-kptn-targeted-testing/ [R=301,L]
    RewriteRule ^test/detail/herc2-defect-syndrome-targeted-testing$ https://www.example.org/genetic_test/herc2-disorder-herc2-targeted-testing/ [R=301,L] 
    RewriteRule ^test/detail/hemophilia-b-f9-targeted-testing$ https://www.example.org/genetic_test/hemophilia-b-f9-targeted-testing/ [R=301,L]
    # etc....
    
    # Redirect all other tests to generic page
    RewriteRule ^test/ https://www.example.org/about-our-lab/ [R=301,L]
    
    # Redirect everything else to new domain (same URL)
    RewriteRule (.*) https://www.newexample.org/$1 [R=301,L]
    

    Note that the RewriteRule pattern matches the URL-path relative to the directory-prefix, so it does not include redirect-old-urls/. It is for this reason that I also changed the last "catch-all" rule to use a backreference and not the REQUEST_URI server variable, that includes the full URL-path (including the arbitrary redirect-old-urls/ URL-path prefix).

    Any direct requests to the subdirectory (if any) would be redirected back to root by the last rule.


    Alternatively, If you have access to the main server config then this could be implemented more efficiently with a RewriteMap that contains the mapping of old "test" URLs to new.