Redirecting post IDs to search engine friendly URLs when WordPress is in a new directory

In this instance, I had a client site with static HTML files in the root and a WordPress blog in the /wordpress/ folder. The old site was using the classic ‘website.tld/?p=123’ URL format, where ‘123’ is the ID of the post.

In the rebuild, the point was three fold:

  1. Get all the static HTML pages into WordPress and editable;
  2. Give the blog posts a more useful SEF URL structure; and
  3. Move everything into the domain root (not down in a /wordpress/ folder).

Luckily for me, WordPress does a good job at matching that ‘123’ ID with the new ‘/2015/12/20/fancy-post-stub/’ structure all on its own—if only the query was looking in the correct directory!

A simple ‘RewriteRule ^wordpress/(.*)$ /$1 [R=301,L]’ in your .htaccess file should work, right? Nope! The trouble is, because the old URL is a query string, you need to detect the query as its own string, then append it to your new URL.

In .htaccess, see my last three lines:

# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
# END WordPress

RewriteCond %{QUERY_STRING} ^p=([0-9]+)$
RewriteRule ^wordpress/?$ http://website.tld/?p=%1 [R=301,L]
RewriteRule ^wordpress/$ http://website.tld [R=301,L]


Closing thoughts:

  • This setup allows me to keep the old /wordpress/ directory with all of its uploaded media files, so I don’t have to create redirects for any linking that’s already out there. For that, I deleted everything else from that directory, aside from anything in /wordpress/wp-content/uploads/.
  • You’ll notice I also specified as much of the query string as possible and restricted the value to numbers only—i.e., ‘p=([0-9]+)’ rather than being lazy with ‘(.*)’ for the whole thing. This was more for future thinking, in case there was something else I needed to redirect for some reason, and to have it point elsewhere. It was also because I like my regular expressions to be as specific as possible for best practice, to prevent unintended consequences. I know the IDs are numbers only, so I’m only accepting numbers.
  • Lastly, I added the catch-all for the /wordpress/ directory to redirect to the root if nothing follows it. Note the $ is the end of the string, so my ‘/wordpress/wp-content/uploads/something/’ URLs still go untouched.

Hopefully this is helpful to the Internet. I’m pretty good with mod_rewrite, but this was something I had to troubleshoot because I’d not used {QUERY_STRING} before.