Ahosting Logo
Knowledge Base

How to Plan Redirects for a WordPress Redesign

The traffic loss after a redesign is almost never the designBefore launch: export every addressthe complete currentlist, from a crawl orthe sitemapMap old to neweach address to itsequivalent, not to thehomepageLaunch with redirects in placenot a week laterThen watch the 404 logfor the addresses themap missedRedirecting everything to the homepage is treated as a soft 404 and loses most of the accumulated value.

Sites lose traffic after a redesign, and the design is rarely the reason. The addresses changed, and nothing told anyone: visitors, search engines or the sites linking to you, where things went.

The work that prevents it happens before launch. Afterwards it is recovery.

Export what you have now

You need the complete list of current addresses, from three sources because none is complete on its own.

The sitemap lists what the site thinks it publishes.

Analytics shows what people actually visited, including pages the sitemap missed.

Search Console shows what has inbound links and rankings, which is the set that actually matters, because those are the addresses other people wrote down.

With WP-CLI, the post list is direct:

wp post list --post_type=post,page --format=csv --fields=ID,post_name,post_title,post_status > urls.csv

Using WP-CLI goes into getting to it.

Classify every address

Three outcomes, and every URL gets one.

Same address. Nothing to do. Keeping addresses unchanged is always cheaper than redirecting them, and is a good reason to resist restructuring for its own sake.

New address. Needs a permanent redirect to its specific match.

Gone. Genuinely removed with no equivalent. Let it return a 404; that is honest, and it is better than sending someone to something unrelated.

The middle group is the work. The temptation is to redirect it all to the homepage, and that is the mistake this whole exercise exists to avoid: a visitor following a link to a specific article and landing on a homepage has to search again, and most leave. Search engines read it as a page that is gone instead of one that moved.

Permanent, not temporary

A 301 transfers the old page's standing to the new address and is cached by browsers. A 302 transfers nothing.

Choosing 302 by accident is common and quietly expensive. For the difference in full, see redirecting a domain or page.

Where to put the rules

For a handful, a redirect plugin is fine and gives non-technical people a way to add more later.

For hundreds, .htaccess is faster, because the redirect happens before WordPress loads at all. A plugin-based redirect boots the whole application to send someone away.

Redirect 301 /old-page/ /new-section/new-page/
RedirectMatch 301 ^/blog/([0-9]{4})/([0-9]{2})/(.*)$ /articles/$3

That second form handles a common case in one line: dropping dates from permalinks. Take a copy of .htaccess first. A mistake here produces a 500 or a loop across the whole site.

WordPress permalinks and .htaccess walks through how WordPress uses the same file.

Test before launch

On staging, with the new structure in place, check a sample of old addresses:

curl -I https://staging.example.com/old-page/

Confirm 301, and confirm the destination is the matching page instead of the homepage. Test a deep path as well as a top-level one; a rule that works at the root and mangles anything below it is common.

Watch the 404s afterwards

You will miss some. Nobody maps every address of a site with years of content.

For two weeks after launch, read the not-found report daily: from Search Console, or from the server's own access log, which is more immediate:

awk '$9==404 {print $7}' ~/logs/example.com | sort | uniq -c | sort -rn | head -30

Anything appearing repeatedly is a real address people are still requesting. Add it to the map. Ignore the noise about wp-login.php and similar. That is bots, not visitors.

Viewing website statistics walks through reading the log, and a custom error page softens the ones you have not caught yet.

Collapse chains

If this is the second redesign, some old addresses already redirect to addresses that are now changing again. Left alone that becomes A to B to C.

Update the original rules to point at the final destination. Each extra hop is a round trip, and long chains eventually stop being followed at all.

Extract the addresses that actually matter

A large site has thousands of addresses and a small number that carry the value. Redirecting all of them carefully is unnecessary; identifying the second group is the work.

awk '{print $7}' ~/logs/example.com | sed 's/?.*//' | sort | uniq -c | sort -rn | head -100 > top-paths.txt
wc -l < top-paths.txt

That gives the addresses real visitors requested, ranked. Combined with the pages that have inbound links from elsewhere, it is the list worth mapping by hand.

Everything below that line can be handled by a pattern rule or, where genuinely obsolete, allowed to return a 404, which is honest and is better than mapping a thousand addresses to approximate matches.

Patterns before individual rules

A redesign usually changes structure consistently, and a pattern captures hundreds of addresses in one line.

RedirectMatch 301 ^/blog/[0-9]{4}/[0-9]{2}/(.*)$ /articles/$1
RedirectMatch 301 ^/products/([0-9]+)-(.*)$ /shop/$2

Write the patterns first, then test them against the extracted list, then hand-map only what the patterns did not catch.

while read -r p; do
 code=$(curl -s -o /dev/null -w '%{http_code}' -I "https://staging.example.com$p")
 [ "$code" = "301" ] || echo "unmatched: $p"
done < paths.txt

That loop is the difference between believing the map is complete and knowing it, and it runs against staging before anything is live.

Query strings need explicit handling

The address people forget. A redirect rule matching on path alone ignores what follows the question mark, and the parameters are either dropped or appended in a way nobody intended.

RewriteCond %{QUERY_STRING} ^id=([0-9]+)$
RewriteRule ^/old-page$ /articles/%1? [R=301,L]

The trailing question mark discards the original query string; without it the old parameters are carried onto the new address, producing a page that works and a URL nobody wants indexed.

Old sites with addresses like ?p=123 are the common case, and those are frequently the ones with the most inbound links. WordPress permalinks and .htaccess walks through where those come from.

Check the rules did not slow the site

Several hundred individual redirect lines in .htaccess are evaluated on every request, including requests for images.

On a busy site that is measurable. Two ways to avoid it: prefer patterns over individual lines, and put the redirect rules in a location that is not consulted for static files where the server allows it.

Measure rather than assume, before and after. Telling whether a slow site is the server or the site walks through the measurement, and a redirect map is one of the few things that genuinely shows up in it.