How to set up redirects in WordPress: plugin, server or code
Use a 301 redirect when an address has changed for good. Set it in one of three places, a redirect plugin, the web server's configuration or a few lines of code, then check that the old address reaches the new one in a single hop.
- By
- WP Ministry
- Published
In short
- A 301 says the move is permanent. A 302 says the old address is coming back. Pick by which is true.
- WordPress forwards the old address of a published post when its slug changes. It does not do this for a page.
- A plugin suits a list of single addresses. Server rules suit folders and patterns. Code suits a rule that depends on the visitor.
- In .htaccess, put your rules above the line "# BEGIN WordPress". WordPress rewrites what is inside its own block.
- Point every old address straight at its final address. Chains, loops and a blanket redirect to the home page all cost visitors.
- Browsers remember a 301. Check a redirect with a tool that asks the server afresh.
A redirect is the server answering a request for one address with the name of another. The browser goes to the second address and asks again. On a WordPress site you can set one in three places: a redirect plugin, the web server's configuration, or a few lines of code.
For an address that has changed for good, use a 301. Point the old address straight at the page that replaced it, then check what the old address answers.
When a redirect is needed
- An address changed. You edited a slug, moved a page under a different parent or changed the permalink structure.
- A page was deleted and another page replaces it. The old address should lead to the replacement.
- The site moved to a new domain, or from http to https, or between
wwwand nowww. How to change your WordPress URL has the redirects for those three, so they are not repeated here.
A page that was removed with nothing to replace it needs no redirect. Let it answer "not found". Google drops an address that answers 404 or 410 from its index.
What WordPress already redirects
WordPress handles two cases itself, both with a 301.
- A post whose slug changed. When the slug of a published post changes, WordPress saves the old one. A later request for the old address, which would otherwise be a 404, is sent to the post's current address. This covers posts and other content types that have no parent and child structure. It does not cover pages, so a page whose slug you change needs a redirect from you.
- The form of the address. WordPress sends a visitor to the host name in its settings, with or without
www, adds or removes the final slash to match the permalink structure, and turns an address such as?into the post's permalink.p= 123
Which kind: 301, 302, 307 or 308
Two questions decide it. Is the move permanent? And does the method of the request have to survive?
| Code | Meaning | A form post or other non-GET request | How Google treats it |
|---|---|---|---|
| 301 | Moved for good | May be turned into a GET | A strong signal. The new address is shown in results. |
| 308 | Moved for good | Method and body are kept | The same as a 301 |
| 302 | Moved for now | May be turned into a GET | A weak signal. The old address stays in results. |
| 307 | Moved for now | Method and body are kept | The same as a 302 |
For a page that visitors read, the request is a GET and the two permanent codes behave alike. Use a 301. Use a 302 when the old address will come back, such as a page that is away for a short while.
307 and 308 matter when something is sent to the old address, such as a form. After a 301 or a 302 a browser may ask the new address with a plain GET and leave the form's data behind. A 307 or a 308 forbids that change.
The three places a redirect can live
| Where | Suits | Know this |
|---|---|---|
| A plugin's list | Single addresses, added and changed from the dashboard | The plugin does the redirecting. Switch it off and they stop. |
| The web server's configuration | Whole folders, patterns, a long list from a move | Needs access to the server's files. A line the server cannot read can take the whole site down. |
| Code | A rule that depends on who is asking or what was asked for | Someone has to maintain it. |
Keep each old address in one place only. The same address in a plugin's list and in a server rule is how chains and loops start.
A plugin's list
A redirect plugin keeps a list of old and new addresses and answers for each from inside WordPress. Redirection is one such plugin. In its listing's words, it lets you "manage 301 redirections, keep track of 404 errors". It can be set to watch for a post's or page's permalink changing and add the redirect itself, which fills the gap WordPress leaves for pages. The listing says it is free, with no paid version. Its documentation says all of its data is stored in the site's database.
Step 1: Install and activate the plugin
Add it from the Plugins screen. Its screen is then under Tools, then Redirection.
Step 2: Add the old address and the new one
The form for a new redirect asks for a source URL, the address you are redirecting from, and a target URL, the address to send visitors to. Write the source as a path on your site, such as
/.old-page/ Step 3: Check the old address
Follow "Check that it works" below. You should see one 301 and then a 200.
To undo it, delete the entry from the list.
By default the plugin does the redirecting through WordPress, so WordPress loads for every redirected request. Its listing says it can instead save the redirects to .htaccess for Apache to handle, and export them as nginx rewrite rules.
The web server's configuration
A rule here answers before WordPress starts. On Apache it goes in the .htaccess file in the site's main folder. On nginx it goes inside the site's server block, and a change is not applied until nginx is reloaded. On managed hosting you may not be able to edit that file. Ask the host to add the rules.
Where the rules go in .htaccess. Put them at the top, above the line # BEGIN WordPress. There are two reasons. WordPress writes the lines between # BEGIN WordPress and # END WordPress itself, and the note it leaves inside the block says that any changes between the markers will be overwritten. And Apache works through rewrite rules from the top. WordPress's block ends by handing every address that is not a real file or folder to index.php and stopping there, so a rewrite rule placed below the block never gets its turn.
One address. The Apache rule matches the old address with or without its final slash. The two nginx lines do the same.
.htaccess
RedirectMatch 301 "^/old-page/?$" "https://example.com/new-page/"A whole folder. Everything under / goes to the same place under /, so / lands on /. Apache's Redirect matches the start of the address, by whole path segments, and adds whatever follows to the new address. / is left alone.
.htaccess
Redirect 301 "/blog" "https://example.com/news"A pattern with a captured part. Parentheses in the pattern capture a piece of the old address, and $1 puts it into the new one. This sends / to /.
.htaccess
RedirectMatch 301 "^/articles/(.+)\.html$" "https://example.com/articles/$1/"Keep a pattern as narrow as the job. One that is too wide catches addresses you did not mean, WordPress's own among them. If you cannot log in after adding a rule, the login page that keeps refreshing covers a redirect that catches the login.
On Apache you will also meet redirects written with mod_, the module WordPress's own block uses. Use it when a rule needs a condition, such as the host name.
RewriteEngine On
RewriteRule ^old-page/?$ https://example.com/new-page/ [R=301,L]Three details differ. In .htaccess the pattern has no slash at the front. R alone means a 302, so write R=. L stops Apache from running the rules below it on this request.
To undo any of these, remove the lines you added and save. On nginx, reload it again.
Code, for a rule that depends on logic
Some redirects are decisions, not a fixed pair of addresses: send visitors who are not logged in away from a members' page, for example. WordPress has two functions for this. wp_ first checks that the address is on a host the site allows, and if it is not, sends the visitor to the dashboard instead. wp_ does no such check. Use the safe one for any address on your own site.
Neither function stops WordPress afterwards. Follow it with exit;, as WordPress's reference says, or the page carries on loading behind the redirect.
add_action( 'template_redirect', function () {
if ( is_page( 'members' ) && ! is_user_logged_in() ) {
wp_safe_redirect( home_url( '/join/' ), 302 );
exit;
}
} );template_ runs once WordPress knows what was asked for and before it picks a template, which is why checks such as is_ work there. The second argument is the status code, and both functions use 302 if you leave it out. A rule like this one should stay a 302: the answer depends on the visitor, and a browser that had stored a 301 could keep following it after they logged in. How to create a child theme explains where functions.php lives. To undo the rule, remove the lines.
Check that it works
Step 1: Ask for the old address and read the answer
Put the old address into the redirect checker. It shows what an address answers and every redirect on the way: each hop, its status code and where it sends you next. From a terminal,
curl -Iasks for the headers only and does not follow the redirect.bashcurl -I https://example.com/old-page/Step 2: Read the status and the Location line
You should see the code you chose, 301 for a permanent move, and a
Locationline with the new address, written exactly as you meant it.Step 3: Follow it to the end
The new address should answer 200 with no further redirect. The checker shows this as the last hop. With curl, ask for the new address the same way.
For a long list, try at least one address for each rule and each pattern.
What goes wrong
Chains. A leads to B, and B leads to C. A common one runs from http to https, then to the www form, then to the new path. Each hop is another round trip for the visitor. Google's crawlers follow up to 10 hops, and its advice is to redirect straight to the final destination. Write each target as the address the site finally serves: https, the right host name, and the final slash the way your permalinks have it. When a page moves a second time, change the old redirects to point at the newest address.
Loops. A leads to B, and B leads back to A. Or a rule matches its own target: a prefix rule that sends / to / also matches /. The browser gives up and shows an error in place of the page. Remove one of the two rules, or narrow the pattern until the target no longer matches it. When a visitor's browser reports the loop itself, how to fix ERR_
Everything sent to the home page. Google's guidance is not to redirect many old addresses to one unrelated page such as the home page. It confuses visitors, and Google may treat it as a "soft 404", which is an error. Give each old address its own new one.
A target that is itself gone. A redirect is only as good as the page it ends at. If the last hop answers 404, the visitor still meets a dead end, one step later. If a page in WordPress answers 404 at an address that should work, how to fix 404 errors on posts and pages that exist has the fixes.
The browser remembers. A 301 may be stored by a browser, which then goes to the new address without asking the server again. Change or delete the redirect and your own browser can keep following the old one. Redirection's documentation gives the same warning and says to clear the browser's cache. Check with the redirect checker or curl, which ask the server each time. While you are still deciding where an address should lead, use a 302 and change it to a 301 once it is right.
The plugin is switched off. Redirects that a plugin carries out stop when it is deactivated or removed, and the old addresses go back to answering 404. Before you remove a redirect plugin, move its list somewhere else. Redirection can export its redirects as .htaccess rules, nginx rewrite rules, JSON or CSV.
Many redirects at once
A redesign or a move to another platform can change hundreds of addresses. Start with a list of every old address and its new one. The redirect map builder turns a list of old and new addresses into 301 redirect rules for Apache or nginx, with chains, loops and duplicates pointed out. The rules are built in your browser. The website migration checklist covers making that list and when in a move to put the rules in place.
Common questions
How long should I keep a redirect?
Google's guidance for a site move is to keep redirects for as long as possible, and generally for at least a year.
Do I need redirects when I change hosts?
No, not if the domain and the addresses stay the same. Only the DNS changes. Our WordPress migration service moves one site from one host to another and tests the copy on the new host before the DNS is changed.
Should a deleted page with no replacement be redirected somewhere?
No. Let it answer "not found". If you want to say plainly that it was removed on purpose, the server can answer 410, which means "gone": on Apache, Redirect gone "/, and on nginx, return 410; inside a location for that address. Google handles a 404 and a 410 the same way and removes the address from its index.
Can I use a plugin and server rules together?
Yes, as long as each old address is in one of them only. A server rule answers first, before WordPress and the plugin load. If the two disagree about an address, the visitor gets the server's answer, and the entry in the plugin's list is never reached.
- ResourceWebsite migration checklist: before, during and after the move
- ResourceWebsite redesign SEO checklist: what to record, map and check on a rebuilt site
- Cost guideWordPress migration cost: which job you are being quoted for, and what moves the price
- Glossary term301 redirect
- Glossary termCanonical URL
- Glossary termDNS propagation

