Noma

Blog

WordPress to Headless CMS Migration Guide for Developers

March 18, 2026

Most WordPress-to-headless projects fail for predictable reasons: URLs break, SEO tanks, or editors lose preview workflows. The fix is not a bigger bang deploy. It is sequencing work so content model and routing are settled before the new frontend owns traffic.

This guide walks through a migration path that teams can run in parallel tracks: content platform, frontend delivery, and SEO continuity.

When headless is worth the migration

Headless fits when you need:

  • Multiple channels (web, app, email, in-product) from one content source
  • Strict API contracts so frontends do not parse theme-specific HTML
  • Performance and caching decoupled from PHP request cycles
  • Developer-owned delivery (Next.js, static generation, edge) without plugin conflicts

If the site is a single marketing blog with light customization, staying on WordPress or using a managed theme may be cheaper. Treat headless as an architecture move, not a feature checkbox.

Prerequisites before you touch schema

Align on these decisions early. They affect every later step:

DecisionWhy it matters
URL strategyDrives redirects, canonicals, and Search Console property behavior
Locale modelWP often uses plugins (Polylang, WPML); headless needs explicit locale fields or separate sites
Preview and draftsEditors expect draft URLs; your new stack must expose preview tokens or staging builds
Media hostingWhether assets move to object storage/CDN vs stay on origin until later
Source of truth for SEOYoast/Rank Math fields must map into structured CMS fields, not stay in HTML blobs

Step 1: Inventory current WordPress content

You cannot map what you have not listed. Go beyond “posts and pages.”

Core entities to catalog

  • Posts, pages, and hierarchical pages (parent/child relationships)
  • Custom post types (CPTs) and their taxonomies
  • Categories, tags, and custom taxonomies (including non-public types)
  • Authors and attribution (byline vs system user)
  • Media library (sizes, alt text, captions, featured images)
  • Menus (often stored separately from page content)
  • Widgets and theme options (footer copy, global CTAs—easy to miss)
  • Shortcodes and Gutenberg blocks (these become structured fields or rich text you must normalize)

How to extract data

Pick one primary path and document it:

  1. REST API (/wp-json/wp/v2/...) — good for incremental pulls and prototyping
  2. WP-CLI — strong for exports, SQL-free batch jobs on the server
  3. Database export + custom scripts — when plugins store data outside standard tables
  4. XML WXR export — useful for editors, weak for automation; pair with validation

For each content type, record: ID, slug, status, modified date, parent ID, featured image ID, and all meta keys you rely on in templates.

Map fields to the new model

Build a field mapping sheet (spreadsheet is fine):

  • WordPress field or meta key → target collection and field name
  • Required vs optional in the new CMS
  • Transform rules (strip HTML, split blocks, parse JSON)

If you skip this, you will discover “mystery meta” halfway through launch.

Step 2: Define the target schema

Headless rewards explicit models. Copying WordPress tables into one-to-one collections often drags legacy problems forward.

Design principles

  • Model for rendering and APIs, not for admin screen parity
  • Prefer repeatable components (hero, FAQ, CTA blocks) over giant WYSIWYG regions when you can
  • Keep SEO and social as first-class fields (title, description, og_image) instead of scraping HTML

Common mapping patterns

WordPress conceptHeadless pattern
Post + categoriesarticle + relation to category
Page treepage with parent_id or nested slug path
ACF repeatersNested objects or related entries
Featured imageAsset relation + alt on asset or entry
Yoast fieldsDedicated SEO object or top-level meta fields

Version the schema if you expect iteration: v1 models can stay stable while you add fields with defaults.

Step 3: URL, redirect, and SEO strategy

This step protects impressions during cutover.

Export a full list of public URLs from:

  • Sitemaps (/wp-sitemap.xml or plugin sitemaps)
  • Crawl logs or analytics landing pages
  • Internal link reports

For each URL, decide:

  • Same path on the new site (ideal for stability)
  • New path with a 301 to the final URL
  • Consolidation (merge thin pages)—use 301 to the surviving URL

Implement redirects

Where redirects live depends on hosting:

  • Edge or CDN (fast, centralized)
  • Application router (Next.js redirects in config or middleware)
  • Origin web server (nginx, Apache) during hybrid phases

Rules should be ordered: specific paths before wildcards. Test with curl or your HTTP client: status code must be 301 (or 308 if you standardize HTTPS + method).

Canonicals and duplicates

  • One canonical URL per piece of content
  • Align rel="canonical" in the new frontend with your CMS “canonical slug”
  • If you serve both www and non-www, pick one and 301 the other consistently

Search Console and sitemaps

After launch:

  • Submit the new sitemap
  • Use Change of address only if the domain itself changes (not required for same-domain replatforms)
  • Monitor coverage and redirect errors for the first two weeks

Step 4: Migrate in batches with validation

Avoid a single “import everything” day.

Batch by risk

Typical order:

  1. Low-risk content (old posts, legal pages)
  2. High-traffic URLs (validate redirects and rendering first)
  3. Complex templates (home, product hubs, localized sections)

ETL and idempotency

Write importers that are rerunnable:

  • Stable external IDs (WordPress post ID stored on the new entry)
  • Upsert by ID so you can fix mapping and re-import without duplicates
  • Log failures to a file, not only console output

Validation after each batch

Check:

  1. Required fields populated
  2. Relations resolve (categories, parents, assets)
  3. Locales consistent with your strategy
  4. Rendered output matches expectations (especially rich text and embeds)

Keep a rollback plan: snapshot CMS data or maintain a branch deploy until the batch is signed off.

Step 5: Switch frontend delivery to API-first

The frontend should treat the CMS as a contract, not a database dump.

Delivery patterns

  • Static generation for stable pages; ISR or on-demand revalidation when content changes often
  • Webhooks from CMS → rebuild or revalidate paths (faster than polling)
  • Preview routes that accept a secret token and load draft content

What “done” looks like

  • Components consume typed fields, not raw WordPress HTML soup
  • No business logic hidden in theme PHP—logic lives in app code or in structured CMS fields
  • Performance budgets owned by the app layer (images, fonts, JS), not by plugin stacks

Media and assets

Plan this explicitly:

  • URLs: Will image URLs change? If yes, add redirects or keep a stable CDN hostname
  • Sizes: WordPress generates multiple sizes; decide which you keep and reprocess if needed
  • Alt text: Migrate from media meta, not only from content body

See Asset Management for how a dedicated asset layer fits headless workflows.

Editors, auth, and preview

Headless does not remove the need for safe editing:

  • Roles mapped from WordPress roles where possible
  • Preview that mirrors production layout
  • Staging environment for content QA before publish

If preview is missing, teams will resist cutover regardless of API quality.

Timeline and cutover (suggested shape)

PhaseFocus
DiscoveryInventory, mapping sheet, URL list
Schema v1Collections, relations, SEO fields
Importer v1One CPT or section, end-to-end
Frontend parityKey templates + redirects for top URLs
PilotLimited traffic or subdomain
Full cutoverDNS/hosting switch, monitor Search Console

Cutover day checklist

  • Top 100 URLs return 200 or correct 301
  • New sitemap live and submitted
  • robots.txt allows intended crawling
  • Analytics and tag managers fire on new property or same container
  • 404 page helpful; no soft-404s on real content
  • On-call owner for 48 hours after switch

Common pitfalls

  • Importing HTML blobs without a plan for shortcodes and embeds—they will break silently
  • Ignoring scheduled posts and revision history expectations
  • Redirect loops between old and new hosts during DNS overlap
  • Duplicate content when both stacks serve the same paths briefly without noindex discipline

Summary

A successful WordPress-to-headless migration is mapping + redirects + batched validation, not a single import. Nail URLs and SEO continuity early, treat schema as a product, and cut over in stages so you can recover without drama.

Related: