WordPress to Headless CMS Migration

Our company is engaged in the development, support and maintenance of sites of any complexity. From simple one-page sites to large-scale cluster systems built on micro services. Experience of developers is confirmed by certificates from vendors.
Development and maintenance of all types of websites:
Informational websites or web applications
Business card websites, landing pages, corporate websites, online catalogs, quizzes, promo websites, blogs, news resources, informational portals, forums, aggregators
E-commerce websites or web applications
Online stores, B2B portals, marketplaces, online exchanges, cashback websites, exchanges, dropshipping platforms, product parsers
Business process management web applications
CRM systems, ERP systems, corporate portals, production management systems, information parsers
Electronic service websites or web applications
Classified ads platforms, online schools, online cinemas, website builders, portals for electronic services, video hosting platforms, thematic portals

These are just some of the technical types of websites we work with, and each of them can have its own specific features and functionality, as well as be customized to meet the specific needs and goals of the client.

Our competencies:
Development stages
Latest works
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    822
  • image_crm_chasseurs_493_0.webp
    CRM development for Chasseurs
    847
  • image_website-sbh_0.png
    Website development for SBH Partners
    999
  • image_website-_0.png
    Website development for Red Pear
    451

Migrating from WordPress to Headless CMS

WordPress to headless CMS migration isn't just a platform change. It's an architecture review: separating content storage from frontend, changing editor workflow, different deployment. Requires planning, phased implementation and parallel operation period.

Pre-Migration Assessment

Before choosing target CMS, answer:

  • How many content types (Custom Post Types) and fields (ACF)?
  • Are plugins with custom logic used (WooCommerce, Events Calendar)?
  • What technical competency level has content team?
  • What's budget for licenses (Contentful, Sanity) vs. self-hosted?
  • Do you need draft and preview support in new CMS?

Target Platform Comparison

Criterion Contentful Sanity Strapi KeystoneJS
Hosting SaaS SaaS/Self Self Self
License From $300/mo From $15/mo Open Source Open Source
Editor Good Excellent Basic Basic
API REST + GraphQL GROQ + GraphQL REST + GraphQL GraphQL
Media CDN included CDN included Own storage Own

Stage 1: Content Audit and Mapping (1–2 weeks)

Inventory existing content:

# Export from WordPress via WP-CLI
wp export --post_type=post,page,product --status=publish --path=/var/www/html

# Analyze ACF fields
wp acf field-group export --group_id=all --output=json > acf-fields.json

# Statistics by type
wp post list --post_type=post --format=count
wp post list --post_type=page --format=count

WordPress → target CMS mapping:

# mapping.yaml
wordpress_types:
  post:
    target: blogPost
    fields:
      post_title: title
      post_content: body (RichText)
      post_excerpt: excerpt
      post_date: publishedAt
      _thumbnail_id: featuredImage (Asset)
      categories: categories (Reference[])
      tags: tags (Reference[])
      acf.seo_title: seoTitle
      acf.seo_description: seoDescription

  product:
    target: product
    fields:
      post_title: name
      _regular_price: price (Number)
      _stock_qty: stock (Number)
      product_cat: categories
      acf.gallery: gallery (Asset[])

Stage 2: New CMS Setup (1 week)

Create Content Types in target CMS exactly per mapping. Configure validations, localization, roles.

Stage 3: Migration Script (1–2 weeks)

// scripts/migrate-from-wp.ts
import axios from 'axios';
import * as contentful from 'contentful-management';
import TurndownService from 'turndown';

const turndown = new TurndownService({ headingStyle: 'atx' });
const cmaClient = contentful.createClient({ accessToken: process.env.CMA_TOKEN! });

async function migratePosts() {
  const space = await cmaClient.getSpace(process.env.SPACE_ID!);
  const env = await space.getEnvironment('master');

  // Get posts from WP REST API
  let page = 1;
  while (true) {
    const { data: posts } = await axios.get(
      `${WP_URL}/wp-json/wp/v2/posts?per_page=100&page=${page}&_embed`
    );
    if (!posts.length) break;

    for (const wpPost of posts) {
      await migratePost(env, wpPost);
      await delay(200); // Rate limiting
    }
    page++;
  }
}

async function migratePost(env: any, wpPost: any) {
  const featuredImageUrl = wpPost._embedded?.['wp:featuredmedia']?.[0]?.source_url;
  let imageAsset;

  if (featuredImageUrl) {
    imageAsset = await uploadAsset(env, featuredImageUrl, wpPost.title.rendered);
  }

  const entry = await env.createEntry('blogPost', {
    fields: {
      title:       { 'en-US': wpPost.title.rendered },
      slug:        { 'en-US': wpPost.slug },
      body:        { 'en-US': turndown.turndown(wpPost.content.rendered) },
      excerpt:     { 'en-US': wpPost.excerpt.rendered.replace(/<[^>]*>/g, '') },
      publishedAt: { 'en-US': wpPost.date },
      ...(imageAsset && {
        featuredImage: { 'en-US': { sys: { type: 'Link', linkType: 'Asset', id: imageAsset.sys.id } } },
      }),
    },
  });

  await entry.publish();
  console.log(`Migrated: ${wpPost.title.rendered}`);
}

Stage 4: Media Files Migration

// Download and upload all WP media files
async function migrateMedia() {
  const { data: media } = await axios.get(`${WP_URL}/wp-json/wp/v2/media?per_page=100`);

  for (const item of media) {
    const asset = await env.createAsset({
      fields: {
        title:       { 'en-US': item.title.rendered },
        description: { 'en-US': item.alt_text },
        file: { 'en-US': {
          contentType: item.mime_type,
          fileName:    path.basename(item.source_url),
          upload:      item.source_url,
        }},
      },
    });

    await asset.processForAllLocales();
    await asset.publish();
    mediaIdMap[item.id] = asset.sys.id; // for references
  }
}

Stage 5: Parallel Launch and Switch

  1. Launch new frontend on staging with real data
  2. Conduct design review with content team
  3. Set up automatic WordPress → new CMS sync for transition period
  4. DNS switch during low-traffic period
  5. Disable WordPress 2–4 weeks after stabilization

Typical Migration Timeline

Stage Small site (<500 items) Medium (500–5000) Large (5000+)
Audit and mapping 1 week 1–2 weeks 2–4 weeks
CMS setup 3–5 days 1 week 1–2 weeks
Migration script 1 week 1–2 weeks 2–4 weeks
Testing 3–5 days 1 week 2 weeks
Launch 1 day 1–2 days 1 week
Total 4–6 weeks 6–10 weeks 3–5 months