Home / Blog / 10 lessons migrating WordPress to code
Migration · Lessons learned

10 Lessons We Learned Migrating WordPress to Code (the SEO Edition)

Short answer: Moving a WordPress site to code is rarely a content problem. It is an SEO problem in disguise. The migration looks finished on launch day, then over the next few weeks Google Search Console starts reporting 404s, redirect chains, wrong canonical tags and lost rankings. These are the ten lessons we learned the hard way, each one a mistake, the fix, and how to avoid it on day one.

We migrated a real, multi-language WordPress site to code. The cutover itself went smoothly. Every page was there, the site was faster, hosting was cheaper. It looked like a clean win.

Then the long tail arrived. Over the following six weeks we spent roughly ten follow-up rounds fixing damage that only showed up after launch, in Google Search Console: pages returning 404, chains of redirects pointing at redirects, split ranking signals, broken language tags, and traffic that quietly slid. None of it was visible on day one.

This article is the playbook we wish we had started with. It is written for site owners and the people who help them, in plain language, but it does not skip the specifics. If you are about to move off WordPress, read this before you flip the switch, not after.

The one theme that ties all ten together: Google does not recheck your whole site the moment you go live. It revisits pages gradually over weeks. So mistakes do not announce themselves on launch day. They show up later, when they are harder to connect to the cause. Get the details right before cutover.

1. Trailing slashes can quietly tank your rankings

What broke: WordPress had indexed almost all of our traffic on URLs ending in a slash, like /blog/post/. The new site redirected every one of those to the slashless version, /blog/post. That sounds harmless. It was not. Redirecting your entire indexed URL base at once tells Google "every page just moved", which triggers a full reconsolidation of the index. Rankings dropped for months.

The fix: serve both forms as a normal page (a 200 response), and pick one as the canonical. Google then consolidates them gently through the canonical hint instead of a hard redirect. Weeks of recovery instead of months.

Avoid it: decide your trailing-slash strategy before cutover and match whatever WordPress was serving, which is almost always the trailing slash. Never 301 your whole URL base on day one.

2. WordPress is forgiving about URLs; code is strict

What broke: WordPress happily served the same content under slightly wrong addresses and variants. The new site did not. It returned a clean 404 for every variant that did not match exactly. Search Console reported 247 of these broken URLs that we did not know existed. A hand-written list of about 80 redirects only patched a fraction of them.

The fix: generate the redirects automatically from your content instead of typing them out. We read the information already stored with each article and produced a redirect for every wrong-address combination, which came to over 700 rules.

Avoid it: never hand-maintain a large redirect map. If it can be derived from your content, derive it. Humans miss cases; a generator does not.

3. The order your rules run in actually matters

What broke: a redirect rule written for the slashless URL never fired for the slashed version, because the platform evaluated redirect rules before the step that strips the slash. The slashed URL slipped straight through to a 404.

The fix: create both the slashed and the slashless version of every single redirect rule, as a pair. Do not assume the system will normalise them for you.

Avoid it: treat "with slash" and "without slash" as two URLs that both need handling, every time, for every rule.

4. Do not redirect a missing translation, show a fallback instead

What broke: when a French page did not exist yet, our first approach redirected the visitor to the English version. That technically worked, but it broke the experience we wanted. A link to the French page should still open in French, with the English content shown inside the French layout, so the link is never dead.

The fix: when no translation exists, do not redirect. Serve the English content wrapped in the requested language's shell, tell Google the English page is the canonical, and mark the fallback as noindex so it does not compete in search. Only redirect when a real translation actually exists at a different address.

Avoid it: separate the two cases up front. Translation exists means redirect to it. Translation does not exist means serve a fallback, mark it noindex, and point the canonical at the original.

5. Every page needs its own canonical tag

What broke: a shared site-wide template hardcoded the canonical tag to the homepage. So every page on the site told Google "the real version of me is the homepage". That is a recipe for mass deindexing, where Google drops your inner pages because they all claim to be something else.

The fix: generate a correct, page-specific canonical for each page, and remove the hardcoded one from the shared layout.

Avoid it: audit the canonical tag on every template before launch. A single wrong canonical in a shared layout poisons the entire site at once.

6. Language tags must point both ways and be complete

What broke: on a multi-language site, the tags that tell Google which pages are translations of each other (called hreflang) were incomplete and one-directional. The English page pointed at the German one, but not the reverse, and the language switcher sent visitors to pages that 404'd.

The fix: give every article a stable translation key in its content, then drive everything from that one key: the redirects, the language tags, the sitemap, the language switcher, even the social preview images. One source of truth keeps them all in sync.

Avoid it: establish that per-article key from the very start. It is the backbone the whole multi-language setup hangs off.

7. Your sitemap must describe the new site, not the old one

What broke: the first sitemap we shipped still carried the old URL structure, missed some language pages, and had no language tags. A sitemap is the map you hand Google. Ours described a site that no longer existed.

The fix: generate the sitemap from the same content source as the pages themselves, so it can never drift out of sync. Include one entry per language per page, real last-modified dates, and the language tags. Then submit it in Search Console the moment you go live.

Avoid it: never maintain the sitemap by hand or let a plugin's old export linger. Generate, then submit at cutover.

8. Kill the WordPress-specific junk URLs on purpose

What broke: WordPress quietly creates whole families of URLs you never think about, and Google has them all indexed. After launch, Search Console kept surfacing them for weeks. The full kill-list we eventually needed included:

  • Taxonomy pages: /category/, /tag/ and /author/ archives.
  • Feeds: the main RSS feed and, easy to forget, a separate feed for every single post.
  • WordPress system files: anything under /wp-content/ and /wp-includes/.
  • Old localized slugs: the translated page names WordPress used, which differ from your new clean paths.
  • Pricing and date variants: things like monthly and yearly pricing pages, and slugs with a year stamped on the end.

The fix: redirect each family to a sensible destination, for example all taxonomy pages to your blog index.

Avoid it: export the full WordPress URL inventory before you switch it off, from your sitemap, your SEO plugin and your server logs. Build the kill-list from that real data, not from memory.

9. The migration tooling has its own hidden traps

What broke: some of the worst bugs came not from our content but from a default setting in the framework. One default made the site quietly send visitors back to whichever language they last viewed, stored in a cookie, so pages "stuck" on the wrong language no matter what link you clicked. Another wiring mistake meant the language switcher itself lived outside the part of the page that knew about translations, so it 404'd.

The fix: keep the configuration simple and make the URL the single source of truth for which language a visitor sees, never a cookie. Then test the switcher and the menus, not just the article pages.

Avoid it: assume the defaults are tuned for a generic case, not your migration. Read what each one does before you trust it.

10. There are hard limits, so verify with real checks

What broke: generating redirects automatically is powerful, but it can explode the rule count. Ours reached 770 rules against a hosting limit of 1024, close enough to be a real constraint. Separately, the build ran out of memory partway through because the content set was large.

The fix: watch the limits your host imposes, and give the build enough resources for a big site. Most importantly, verify the result with real checks. For a list of actual URLs, confirm each one returns the status you expect: a clean page with no redirect chain, or a redirect that lands on the right destination in one hop.

Avoid it: do not trust "it looks fine in the browser". Check the status codes, and keep Search Console's coverage and redirect reports open for four to six weeks after launch to catch the long tail.

Want all ten handled for you?

Every lesson here is something that can be generated and verified automatically rather than learned the hard way in Search Console. That is exactly what ShiftPress does when it moves you off WordPress. Join the waitlist and skip the six weeks of cleanup.

Join the waitlist

The pattern behind all ten

Look back over the list and one shape repeats. The mistakes are almost never about the content itself. They are about the thousand small URL and metadata details that WordPress handled invisibly and that a new site has to reproduce on purpose: trailing slashes, redirects, canonical tags, language tags, the sitemap, and the junk URLs Google still remembers.

The good news is that nearly all of it can be generated from your content rather than maintained by hand, and checked automatically before launch. When that happens, the migration that looked clean on day one actually stays clean in week six. That is the difference between learning these lessons yourself and having them already built in.

Frequently asked questions

Why do rankings drop weeks after launch, not on day one? +
Google does not recrawl your whole site the moment you go live. It revisits pages over the following weeks, and only then discovers broken links, redirect chains and wrong canonical tags. That is why a migration can look perfect on launch day and still cost rankings a month later. Fix the SEO details before cutover, not after Search Console complains.
What is the single biggest SEO mistake when migrating? +
Changing your URLs without matching what WordPress served. The most common case is the trailing slash: if WordPress indexed pages ending in a slash and the new site redirects every one to a slashless version, you create a site-wide redirect chain that forces Google to reconsolidate its index and can suppress rankings for months.
How do I know which old URLs to redirect? +
Export your full URL inventory before switching WordPress off, using your sitemap, your SEO plugin and your server logs. Build the redirect map from that real data. WordPress also generates families most people forget, like category, tag, author and per-post feed pages, and each one needs a destination.
Can I avoid all this without doing it by hand? +
Yes. Almost every lesson here can be generated automatically from your content rather than maintained by hand: redirects, canonical tags, language tags and the sitemap. A managed platform such as ShiftPress handles them for you and verifies the result, so you do not learn the lessons the hard way in Search Console.

The bottom line

Migrating WordPress to code gives you a faster, safer, cheaper site. But the win is only real if your rankings survive the move, and that comes down to details Google checks slowly, over weeks. Plan the trailing slashes, redirects, canonical tags, language tags and sitemap before you launch, verify them with real checks, and keep an eye on Search Console afterward. Or use a platform that does all of it for you, which is exactly what ShiftPress is built to do.