WordPress with 10,000 records, headless architecture for multi-channel publishing — a typical scenario where direct export-import breaks SEO and structure. Data schemas are incompatible, media files are in bindings, Rich Text loses formatting. We solve this with an ETL pipeline using batching and reprocessing. Over 5 years we have completed 30+ migrations — from WP→Strapi to Drupal→Contentful. Budget savings reach 40% (up to 200,000 rubles on a project with 5,000 records) due to automation compared to manual migration. Our team has 10+ years of web development experience, ensuring reliable transfer.
A simple database dump won't work: 60% of time goes into transformation. Content type mapping, taxonomy restructuring, Rich Text conversion — each stage demands its own approach.
How to prepare data for migration?
The first step is inventory. We collect the schema: content types, fields, taxonomies, metadata. For WordPress this means posts, pages, users, shortcodes; for Contentful — models and locales. Mapping is done manually with your team's involvement — this guarantees 100% transfer.
Content type mapping
| Data type | WordPress | Strapi | Sanity |
|---|---|---|---|
| Post | wp_posts.post_type = 'post' |
Post collection |
post document |
| Page | wp_posts.post_type = 'page' |
Page collection |
page document |
| Metafield | wp_postmeta |
Dynamic zone | metadata object |
This mapping becomes the foundation for transformation scripts.
Why an ETL pipeline is faster than manual transfer?
ETL — Extract–Transform–Load — allows automating even complex transformations. Batching 50 records with a 500 ms delay speeds up the process 3x compared to single-item processing. Example script:
// scripts/cms-migration.ts interface MigrationConfig { source: 'wordpress' | 'contentful' | 'ghost'; target: 'strapi' | 'contentful' | 'sanity'; contentTypes: ContentTypeMapping[]; } async function migrate(config: MigrationConfig) { const extractor = getExtractor(config.source); const transformer = getTransformer(config.source, config.target); const loader = getLoader(config.target); for (const mapping of config.contentTypes) { console.log(`Migrating: ${mapping.sourceName} → ${mapping.targetName}`); const items = await extractor.extract(mapping.sourceName); const transformed = items.map(item => transformer.transform(item, mapping)); for (const batch of chunk(transformed, 50)) { await loader.load(mapping.targetName, batch); await delay(500); } } } Rich Text migration
The hardest stage — format transformation. HTML → Portable Text (Sanity) or Markdown (Contentful) requires recursive DOM parsing. Image blocks, embedded shortcodes — each element is processed by rule. For example, migrating 15,000 images from WordPress to Strapi took 2 days for batching and relinking.
// WordPress HTML → Portable Text import { htmlToPortableText } from '@portabletext/html'; function transformWpContent(html: string) { return htmlToPortableText(html, { rules: [ { deserialize(el, next, block) { if (el.tagName === 'IMG') { return block({ _type: 'image', _key: Math.random().toString(36).slice(2), src: el.getAttribute('src'), alt: el.getAttribute('alt'), }); } }, }, ], }); } After content loading, we generate a redirect mapping and configure 301 redirects.
const redirects = oldPosts.map(old => ({ source: old.url, destination: newPosts.find(n => n.slug === old.slug)?.url ?? '/blog', permanent: true, })); What to do with SEO structure after migration?
Old URLs, meta tags, sitemaps — everything transfers without losing positions. We use 301 redirects and error logging. As per WordPress documentation, correct redirects are critical for preserving PageRank. If you are planning a migration, contact us — we will help plan the budget and timeline.
Key migration challenges
Rich Text with attachments
In WordPress, content is stored as HTML with inline shortcodes. In Contentful — Markdown, in Sanity — Portable Text. The algorithm recursively parses the DOM and assembles blocks.
SEO structure
We transfer meta tags, headings, sitemaps. We configure 301 redirects to preserve positions. Redirect setup in Strapi is described in the official Redirects plugin.
Downtime
We minimize downtime to 15 minutes using parallel write and DNS switching.
Users and permissions
We import accounts, roles, and subscriptions, ensuring seamless authentication.
Process flow
- Audit — inventory content, schema, SEO tags, media.
- Mapping — create correspondences of content types and fields.
- Script development — write an ETL pipeline for your CMS pair.
- Test migration — run on a copy, verify data integrity and metadata.
- Production launch — perform transfer during low-traffic window.
- Post-migration — monitor 404 errors, adjust redirects, deliver documentation.
Timeline
| Content volume | Duration |
|---|---|
| up to 500 records | 1–2 weeks |
| 500–5000 records | 2–4 weeks |
| 5000+ records | from 4 weeks |
Cost is calculated individually — write to us, we will evaluate the project in one day. Get a consultation from a migration engineer.
Post-migration checklist
- Verify 301 redirects for top-100 pages
- Compare record count in old and new CMS (count)
- Test forms, search, filters
- Upload sitemap to Search Console
- Set up 404 monitoring
What the work includes
- Migration plan and mapping.
- ETL scripts tailored to your CMS.
- Configuration of 301 redirects and sitemap.
- Staging testing and post-launch monitoring.
- Documentation (redirect list, data schema, instructions).
- 30 days of support after migration.
Contact us for a consultation on transferring your site — we will estimate budget and timeline within one day. Order a migration now to discuss details without obligation. We guarantee data integrity and zero downtime.







