Search code examples
apache.htaccessmod-rewriteurl-rewriting

301 redirection showing the old page URL in the query string


I am using the below code in my website and it's working.

RewriteEngine On
RewriteBase /a/b/
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)/?$ index.php?slug=$1 [QSA,L]

I am able to access my website like

https://www.example.com/a/b/testing

Now, I am getting issues with 301 redirections.

I have tried below code

Redirect 301 /oldurl /newurl

but while accessing the URL I am getting below URL

https://www.example.com/a/b/newurl?slug=oldurl

My expected output is the below URL.

https://www.example.com/a/b/newurl

Solution

  • You should avoid mixing Redirect (mod_alias) and RewriteRule (mod_rewrite) directives in the same context as you can get these unexpected conflicts. mod_rewrite is processed before mod_alias, despite the apparent order of the directives in the config and Redirect works on the requested URL-path (not the rewritten URL-path and not the query string).

    So, what is happening when requesting /oldurl and you use Redirect 301 /oldurl /newurl together with the existing mod_rewrite rewrite is:

    1. mod_rewrite internally rewrites the request to index.php?slug=oldurl
    2. Redirect creates a redirect from /oldurl (the originally requested URL-path) to /newurl
      • The query string (if any) is appended to the target URL. This is now slug=oldurl since mod_rewrite modified it.
    3. Resulting URL is /newurl?slug=oldurl.

    (As noted above, this is regardless of the order of the directives, since they are from two different modules.)

    Solution

    You need to use mod_rewrite for the redirect as well and this must go before the existing rewrite (the order is important, since the directives belong to the same module). For example:

    RewriteEngine On
    RewriteBase /a/b/
    
    RewriteRule ^oldurl$ /newurl [R=301,L]
    
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule (.*) index.php?slug=$1 [QSA,L]
    

    Any query string that might be present on /oldurl is preserved. eg. /oldurl?foo=bar is redirected to /newurl?foo=bar (as it would do with Redirect). If you specifically want to remove any query string then include the QSD flag (Query String Discard) on the redirect (first) rule.

    NB: I removed the trailing /? on the regex since it is entirely redundant. Since you made the token optional and the preceding quantifier is greedy then .* would consume the optional trailing slash anyway. If the intention was to exclude the trailing slash (if any) from the captured subpattern then you need to make the preceding quantifier non-greedy. eg. (.*?)/?$ (the start-of-string anchor is not required).

    (I'm assuming your .htaccess file is located in the document root - it looks like it is, from the erroneous redirect you were getting, although the /a/b/ prefix is slightly confusing - see below)


    Aside:

    I have tried below code

    Redirect 301 /oldurl /newurl
    

    but while accessing the URL I am getting below URL

    https://www.example.com/a/b/newurl?slug=oldurl
    

    I would have expected this to have resulted in /newurl?slug=oldurl, not /a/b/newurl?slug=oldurl?