Why am I getting ?p=oldpage in a 301 redirect? Not sure whether this is directly related to Craft but I’ve been using this method for years and it has always worked with other sites.

I’m replacing an old site and some pages have moved. I want to redirect any old pages to the new url since there are already indexed.

I’m using a simple redirect 301
Redirect 301 /work/project-name http://www.mysite.com/case-studies

I’m getting http://www.mysite.com/case-studies/project-name?p=work/project-name and of course a page not found since this is not where I intended the page to go.
Craft works by taking requests that normally would 404—because they don't actually exist on the server but in the database. The piece that makes this happen is the rewrite rules. 

It's helpful to look at Craft's .htaccess file line-by-line:

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d


The request must not already exist and the file is not already an existing directory on the server.

RewriteCond %{REQUEST_URI} !^/(favicon\.ico|apple-touch-icon.*\.png)$ [NC]


The URL must not be favicon.ico, apple-touch-icon.whatever.png. The NC at the end means no-case (meaning case-insenstive).

RewriteRule (.+) index.php?p=$1 [QSA,L]


This is the rule that actually fires. Take those would-be 404s, redirect to /index.php (the controller). $1 means the match found in (.+). 

The QSA flag means query string append. This is the part that's actually causing you issues. (You could remove it but get URLs with parameters called from controllers might not work in certain situations.)

L means last; if this rule is applied, it won't process anything else. Doesn't matter here unless you have other rules.

What you need to do is tell Apache to exclude this particular request from Craft's processing (put it after the favicon rule):

RewriteCond %{REQUEST_URI} !^/(work/project-name)$ [NC]


Now what will happen is your Redirect 301 rule will fire by itself. Since /case-studies doesn't actually exist on the server,  Apache is going to send that rule back to Craft and fire like any normal "page" which is what you want.

If you have other redirected 301s from the old /work section, you might do something like:

RewriteCond %{REQUEST_URI} !^/(work/.+)$ [NC]


That will let Craft know not to touch any of your 301s in the old work section. You still need your old Redirect 301 rules.  (You could make a RewriteRule for those if you wanted to simply things but this will get you going.)

Your completed file would look like:

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !^/(favicon\.ico|apple-touch-icon.*\.png)$ [NC]
RewriteCond %{REQUEST_URI} !^/(work/.+)$ [NC]
RewriteRule (.+) index.php?p=$1 [QSA,L]

Redirect 301 /work/project-name http://www.mysite.com/case-studies
...


I find some browsers will "cache"  an old page/redirect for awhile. To confirm it's actually working you might want to use curl -I http://site/work/project-name. If you see "moved permanently" message, you're all set.