Enterprise WordPress · 6 min read

Migrating WordPress to Headless Without Losing SEO

URL parity, redirect maps, who owns metadata and schema after the split, and the verification pass that proves the migration did not cost you traffic.

By Praful Patel · Last updated
Migrating WordPress to Headless Without Losing SEO - article cover
Migrating WordPress to Headless Without Losing SEO - article cover

Short answer

A headless migration loses traffic for boringly mechanical reasons: URLs that changed by one trailing slash, metadata nobody ported, and pages that now render client-side. None of it is subtle, and all of it is preventable with a parity audit before launch and a verification pass after. The risk is process, not technology.

Search engines do not know or care that the rendering layer changed. They compare what a URL returns today against what it returned last week. Keep those equivalent and a migration is invisible.

Establish URL parity before writing any front end

Export every indexable URL from the existing site and treat that list as a contract. Sources: the XML sitemap, Search Console’s page report, server logs for anything receiving traffic, and a crawl to catch what the sitemap missed.

Then decide, per URL: identical, redirected, or intentionally removed. There is no fourth category, and “we will sort out redirects later” is how migrations lose a third of their traffic.

Trap What happens Fix
Trailing slash flip Every URL redirects or 404s Match WordPress’s existing behaviour in Next config
Case sensitivity PHP was lenient; Node is not Normalise in middleware
Paginated archives /page/2/ silently disappears Implement the same pagination paths
Author and date archives Dropped as “not needed” Keep, or redirect to the closest equivalent
Attachment pages Were indexed, now 404 Redirect to the parent post
Feeds /feed/ gone Generate it; subscribers and aggregators use it

The trailing slash one is the most common single cause of a bad migration, and it affects every URL at once.

Move redirects, do not recreate them

An established site has years of accumulated redirects in a plugin table. Those still carry link equity. Export them and load them into the front end — as redirects in the Next config for a manageable number, or read from the API into middleware for thousands.

Keep the status codes as they were: 301 for permanent, 302 only for genuinely temporary. And check for chains — A to B to C should become A to C, because each hop is a delay and a chance to lose the signal.

Decide who owns metadata

This is the thing most often discovered after launch. The SEO plugin was rendering titles, descriptions, canonicals, Open Graph tags, JSON-LD, breadcrumbs, robots directives and the sitemap. In a headless build it renders nothing.

The plugin stays as the editorial interface — editors need a field to write a description in. The front end reads those values through the API and emits the tags. Every one of them needs porting explicitly:

export async function generateMetadata({ params }): Promise<Metadata> {
  const post = await getPost(params.slug);
  if (!post) return {};

  return {
    title:       post.seo.title,
    description: post.seo.description,
    // Self-referencing canonical, or the migration invites duplicate-content issues.
    alternates:  { canonical: `${SITE}/blog/${post.slug}` },
    robots:      post.seo.noindex ? { index: false, follow: true } : undefined,
    openGraph:   { title: post.seo.title, images: [post.seo.ogImage], type: 'article' },
  };
}

JSON-LD moves too, and it must reflect the page it is on rather than being emitted globally — the same principle as any structured data: the markup has to describe visible content.

Do not trade server rendering for client rendering

The one genuine technical risk. WordPress served complete HTML. A front end that ships a shell and fetches content in the browser has made the page dependent on JavaScript execution, which is slower to index and fragile.

Content that matters must arrive in the HTML response — static generation, incremental regeneration or server rendering. Client fetching is fine for anything below the fold and non-essential. Verify by fetching a page with JavaScript disabled: if the article text is missing, the migration is a regression regardless of what the Lighthouse score says.

Sitemaps and robots

Generate the sitemap from the same source the routes are generated from, so it cannot drift. Include only canonical, indexable URLs — a sitemap listing redirects and 404s wastes crawl budget and signals carelessness. Keep lastmod accurate rather than setting it to the build time, which tells search engines every page changed on every deploy.

Check robots.txt deliberately: it now comes from the front end, and a staging Disallow: / shipped to production is a memorable way to deindex a site.

Launch and verification

  1. Before: crawl the old site and store status codes, titles, descriptions, canonicals and word counts per URL.
  2. On staging: crawl the new site and diff against that baseline. Investigate every difference.
  3. Launch at a low-traffic time with the rollback path tested.
  4. Immediately after: re-crawl production, check Search Console for coverage errors, resubmit the sitemap.
  5. For a month: watch impressions and average position by page type, not just sitewide. A single template breaking is invisible in an aggregate.

Expect a small fluctuation for a week or two as pages are recrawled. A sustained decline concentrated in one template is a bug, not the algorithm.

The build-side problems this creates — preview, invalidation, two deploys — are covered in Headless WordPress with Next.js, and whether to decouple at all in Building Scalable Headless CMS & Gutenberg Architectures.

Questions people actually ask

Will going headless hurt my rankings?
Not inherently. It hurts when URLs change without redirects, when metadata is not ported, or when content that was server-rendered becomes client-fetched. Keep URLs and HTML output equivalent and search engines have nothing to react to.

Can I migrate section by section?
Yes, and it is lower risk. Proxy specific path prefixes to the new front end while the rest stays on WordPress, then move more as confidence grows. It also means a problem affects one template rather than the whole site.

Do I need to resubmit the sitemap?
Yes, once the new site is live and the sitemap URL is correct. It is not strictly required – crawlers will find changes eventually – but it materially speeds up recrawling, which is exactly what you want during the window where you are watching for regressions.

How long before traffic stabilises?
Two to four weeks for most sites, longer for large ones, because recrawling is gradual. Small fluctuation in that window is normal. A decline that is concentrated in one page type, or that keeps deepening after three weeks, is a problem to diagnose rather than wait out.

What is the single most common mistake?
Trailing slash behaviour changing. WordPress adds a trailing slash by default; a Next.js app may not. That changes every URL on the site simultaneously, and it is trivially fixable in configuration if anyone checks before launch.

← Back to all insights