Writing Nginx Redirects for WordPress

Reading Time: 5 minutes

Let me preface this with the fact that I am in know way a server expert. Basically all of the knowledge I have of Nginx was gathered over a 3-4 week period where I started testing out Digital Ocean for work. I’ve had a bit more experience with Apache server configuration simply because most of the WordPress sites I had previously worked with were on Apache.

Somewhere along the line an article convinced me Nginx was a better configuration for hosting WordPress sites. So I figured I would give it a try.

First off, let me just praise Digital Ocean for a bit. It is great. I was able to go from a complete novice to having a server droplet up and running with WordPress and Apache over a long night just following different guides posted to Digital Ocean’s support community. Then I was able to convert that to an Nginx server. Not to mention the benefits of being able to throw up a new server to test something out and only be charged for the amount of time I kept it active.

That being said, I struggled to find the answer when I came to the sudden realization that I needed to do Nginx redirects.

Now I’ve worked with plenty of redirects before. I’ve done them with WordPress plugins, in the .htaccess, through WP Engine’s admin panel, and through PHP. So there I was ready to switch my domain without a care in the world. I’d just switch the domain over to the Digital Ocean nameservers and drop my redirects in the .htaccess. I didn’t have to redirect much. Well, after the first unsuccessful test it dawned on me that Nginx doesn’t support .htaccess.

So I had to scramble (not that anyone was actually going to try to hit my site). I really just needed to redirect the main domain to the new one, but it got tricky when I decided I would redirect /blog to the homepage but /blog/anything-else to domain.com/anything-else. And my old site was all .html pages *shudder*.

The main issues I hit were getting my redirects in the correct order to not hit too early, and figuring out the regex necessary to make them work. I should probably spend some time with regex, but who has time for that now that FXX is showing early seasons of The Simpsons?

I had some experience working with virtual hosts / server blocks in Nginx, and setting up the rules for WordPress’ pretty permalinks. So I at least knew where to start looking for a solution.

The first thing I needed to do on my site was make sure I had my old domain set up as an A record pointing to my droplets IP and a CNAME rule for a wildcard to catch www and any other subdomains and redirect them to my new site. The next thing to do was to navigate to my sites-available folder within Nginx and create a new server block for this domain. We’ll call it olddomain.com.

That’s easy enough. Just SSH into your server, cd into the /etc/nginx/sites-available folder and create a new server block. For more detailed info on this check out step 6 in this article.

You’ll want it to look something like this:

server {
    listen 80;
    server_name olddomain.com www.olddomain.com;
}

Ok so the new server block is set up. Now we need to add the redirect rules. First, I want to catch any of the old html pages and redirect those to the new homepage since there isn’t going to be a portfolio or anything at this time. So we can do that by checking to see if the the url has .html in it and rewriting the url to the newdomain’s home page. In the future if I wanted to redirect olddomain.com/portfolio.html to newdomain.com/portfolio I could (should – I haven’t tested this yet) be able to add the rule above this one and be good to go. So that will look something like this.

server {
    listen 80;
    server_name olddomain.com www.olddomain.com;

    if ($request_filename ~* \.(html)$) {
        rewrite ^(.*) http://newdomain.com permanent;
    }

}

You’ve got the if statement checking if the page has .html and the rewrite rule. I’m essentially redirecting any url that matches the if check using ^(.*) to redirect to http://newdomain.com. Then the permanent at the end tells it that this is a permanent 301 redirect. Easy Enough

Now comes the tricky part. I need to redirect the old blog posts to the new format, but my old blog was at the /blog subdirectory and the new one is the main site. So I need to redirect olddomain.com/blog/anything-else to newdomain.com/anything-else, but make olddomain.com/blog go to newdomain.com. So I need to make sure the first rule I do is only catching the blog posts and not the main blog, and the second rule then catches /blog and doesn’t overwrite the articles. So that looks something like this:

    rewrite ^/blog/(.*)$ http://newdomain.com/$1 permanent;
    rewrite ^/blog http://newdomain.com permanent;

The first new rule there says take anything with olddomain.com/blog/anything-else and redirect it to newdomain.com/anything-else. How’s it do that? Well the $1 up there works as a variable that catches wildcard statement of (.*)$. Then again it ends with permanent to hit the 301 redirects.

The second rule then catches anything with olddomain.com/blog that the first rules passes over. In this case leaving us with only olddomain.com/blog and redirects it to newdomain.com.

Finally, I want to make sure I catch any other pages from olddomain.com/other-stuff and redirect them to newdomain.com/other-stuff, of course because of the order this is only going to catch items that don’t end in .html. So here’s the rule for that from Engine Yard:

    rewrite ^ $scheme://newdomain.com$request_uri permanent;

In this final rule ^ catches any url for jyingling.com. $scheme:// should make the redirect use http or https depending on what was entered. Finally I’ll let Engine Yard explain $request_url.

$request_uri means “when we redirect, copy the URL part”

So in my case if I entered olddomain.com/derp, I would be redirected to newdomain.com/derp. This one doesn’t catch much on my old site, since it used .html, but it is a good rule to have around. Worst case they hit the 404 on newdomain.com, but at least they’re there and can continue on their search.

So there it is our final server block should look something like this:

server {
    listen 80;
    server_name olddomain.com www.olddomain.com;

    if ($request_filename ~* \.(html)$) {
        rewrite ^(.*) http://newdomain.com permanent;
    }

    rewrite ^/blog/(.*)$ http://newdomain.com/$1 permanent;
    rewrite ^/blog http://newdomain.com permanent;
    rewrite ^ $scheme://newdomain.com$request_uri permanent;

}

Now you’ve got your old html site redirecting to your brand spanking new site. If you’ve got any questions go ahead and ask them in the comments. If I completely screwed something up, let me know there as well.

Update 12/2/2014: Michael Nordmeyer (@coffeemick) sent me this tweet yesterday.

I gave it a whirl today and it worked perfectly. So in the example above we can replace the if statement with rewrite ^/.*\.html$ newdomain.com permanent; and we’re good to go. Thanks for the tip Michael! Here’s the entire updated block.

server {
    listen 80;
    server_name olddomain.com www.olddomain.com;

    rewrite ^/.*\.html$ newdomain.com permanent;

    rewrite ^/blog/(.*)$ http://newdomain.com/$1 permanent;
    rewrite ^/blog http://newdomain.com permanent;
    rewrite ^ $scheme://newdomain.com$request_uri permanent;

}

Pin It on Pinterest