🇹🇷 Türkçe: Bu yazının Türkçesini oku →

I have watched a lot of platforms come and go. I ran an enterprise content system built on Microsoft’s stack that dozens of agencies used. I studied Vignette, Interwoven, FatWire, and Stellent up close during the years when they cost more than most companies’ entire IT budgets. And I have moved content off nearly all of them, onto whatever came next.

Strapi is a good tool. I want to say that before I say anything else, because the internet is full of people who trash the thing you are leaving in order to sell you the thing you are arriving at, and I did not write this to do that. Strapi is open-source, Node.js-based, developer-friendly, and it earned its popularity honestly. If you are feeding many channels from one content source, a headless CMS is the right architecture and Strapi is a legitimate choice.

But a lot of teams running Strapi are feeding exactly one channel — a website — and they are paying the full price of a multi-channel architecture to do it. When those teams decide to move to WordPress, I do not try to talk them out of it. I help them plan it so it does not bite back.

This article is that plan. General terms first, for anyone weighing the decision. Then, near the end, the technical detail that actually decides whether the migration works or quietly falls apart.

Why teams leave Strapi for WordPress

Strapi and WordPress solve overlapping problems with very different assumptions, and the friction usually shows up in the same places:

  • The content team is blocked on developers. In Strapi, the content model lives in code — the Content-Types Builder generates schema files, and any structural change is a code change that has to be deployed. Marketing wants to add a field or ship a landing page today, and they cannot, because it is an engineering task. On WordPress, with ACF and the block editor, most of that becomes self-service.
  • Operations. Strapi is a Node application you host, patch, scale, and keep alive. That is a real operational burden that someone on your team is carrying, often without wanting to. WordPress hosting is a solved, commoditised problem with a thousand vendors competing on price.
  • The ecosystem and hiring pool. WooCommerce, SEO plugins, forms, memberships, and — critically — a workforce that already knows the tool. You can hire a WordPress person tomorrow. Strapi talent is thinner.
  • Longevity. This is the one I weigh most heavily, and I will explain why with a story I do not tell to make myself look good.

A story about ownership, and about carrying the past

Years ago, because I could not sell WordPress to enterprise clients in that era — “anyone can see the code” was treated as a security flaw rather than a feature — we built our own CMS on ASPX. We modelled it on WordPress because we had studied WordPress and knew it was well designed. It came out genuinely well. Fast. Dozens of agencies we had never met built projects on it.

And then we hit a wall that was entirely our own fault. Our CMS had no backward compatibility. Every new version forgot the past. To us that was fine — we were always on the newest build, always moving forward. To everyone using it, every release was a catastrophe, because their data needed migrating by hand every single time. We could not carry that weight. We gave up. A major bank still runs that system today, kept alive by hand, because we walked away and they could not.

I did not learn why WordPress survives by reading about it. I learned it by building the system that did not. Backward compatibility is the whole game. It is unglamorous and it slows you down, and it is the single reason WordPress code from a decade ago mostly still runs while better-funded platforms are footnotes.

So when I choose a platform for a client, I ask the question that the ECM giants’ customers never got to ask in time: who can buy this company, and what happens to my content the day after? Strapi is open source, which genuinely helps — the point of open source is not that it is free, it is that there is an exit. But a self-hosted Node app that your team cannot maintain is its own kind of trap. WordPress has both the exit and the maintenance pool. For a single-channel site, that combination is usually the whole argument.

How I plan the migration

The mistake I see everyone make is treating this as an export-import job. It is not. It is a content-modelling job in disguise, and the modelling is where the whole thing is won or lost.

I come from print, where layout is the last step and structure comes first. That discipline never left me, and it is exactly right for a migration:

1. Inventory the content model completely. Every collection type, every single type, every component, every dynamic zone, every relation, every media field. Draw the actual shape. You cannot move a thing you have not mapped.

2. Decide what to leave behind. A Strapi model that has grown for two years is carrying components nobody uses and relations that were added “just in case.” Migration is your one licensed opportunity to throw things away. Every over-built structure I ever created turned out to be dead weight, and every idea I dismissed as too simple turned out to be the one that worked. Prune hard.

3. Map Strapi’s model to WordPress’s model honestly. Collection types become custom post types. Single types become options pages or single pages. Relations become ACF relationship fields, post-to-post connections, or taxonomies. Components and dynamic zones become — and this is the hard part — ACF flexible content or block patterns. Be willing to flatten structure that does not earn its place.

4. Build the empty target in WordPress first. Post types, ACF field groups, taxonomies, and the block patterns your editors will actually use. Validate the shape with the people who will live in it before real content arrives.

5. Write the transform and dry-run it on staging, repeatedly. Never migrate straight to production. I spent years burning discs, and the worst feeling in this business is a mistake you cannot recall. Staging is your recall button. Run it until the output is boring.

6. Redirects and SEO before cutover. Your URLs will change. Map old to new before you flip the switch, or watch years of ranking evaporate in a week.

7. Cut over, then keep watching. The migration is done when the content team publishes three real things without calling you — not a minute before.


The Technical Part: Where a Strapi Migration Actually Breaks

Everything above is strategy. Here is where the migration lives or dies on the details. If you are the developer doing this, this is the section that matters. These are the specific traps.

Which rich-text field are you even on?

Strapi changed its rich-text story. Older projects store rich text as Markdown in a text field. Newer projects use the blocks rich-text field, which stores content as structured JSON. These migrate completely differently:

  • Markdown converts cleanly to HTML and then to Gutenberg blocks — this is the easy path, and a Markdown-to-blocks step gets you most of the way.
  • Blocks JSON is a nested node structure (paragraphs, headings, lists, links, images) that you must walk and map node-by-node to Gutenberg blocks — closer in spirit to serializing an abstract rich-text tree.

Find out which one your project uses before you estimate the work, because they are not the same job. And check every collection type — mixed projects exist, where old content is Markdown and new content is blocks.

Components and dynamic zones have no clean WordPress equivalent

This is the trap that swallows Strapi migrations.

A Strapi component is a reusable field group embedded inside a content type. A dynamic zone is a field where an editor picks from a list of components and stacks them in any order — a page builder, essentially. There is no 1:1 concept in WordPress core.

You have to make a deliberate architectural decision here, and there is no automatic answer:

  • Map dynamic zones to Gutenberg block patterns — each component becomes a block or block pattern, and the zone becomes the post’s block content. This is the most native, most future-proof result and the most work.
  • Map dynamic zones to ACF flexible content — each component becomes a flexible-content layout. Closer to Strapi’s own mental model, faster to build, but it keeps your content in meta rather than in the editor.

Whichever you choose, you must handle every component type by hand. Miss one and that content silently disappears from the migrated pages. Inventory every component and every dynamic-zone layout before writing a line of transform code. This is the single most underestimated part of a Strapi migration — I have seen teams budget a week for the whole move and then find three weeks of component mapping hiding inside it.

Relations, ordering, and the two-pass problem

Strapi relations — oneToOne, oneToMany, manyToMany — point at records by ID. When you import into WordPress, those IDs mean nothing. And you cannot resolve them in one pass, because the target record may not exist yet when you import the source.

Do it in two passes: first import every record and build a map of Strapi ID to new WordPress post ID; then, in a second pass, rewrite every relation into ACF relationship fields, post connections, or taxonomy terms using that map. Preserve the original Strapi IDs in post meta until the second pass completes.

Watch ordering specifically. Many-to-many relations in Strapi often carry a meaningful order that editors set by hand. If your transform does not preserve it, related items come out shuffled, and nobody notices until a client points at a homepage where the featured products are in the wrong sequence.

Media URLs, providers, and rewriting

Strapi’s media library uses an upload provider. In local mode, files live under /uploads and URLs are relative. But many production Strapi installs use Cloudinary, S3, or another cloud provider, and then media URLs are absolute and point at that provider’s domain.

Two things follow. First, you must download every asset from wherever it actually lives and re-upload it into the WordPress media library. Second — and this is the one that gets missed — you must rewrite every embedded media URL inside your rich-text content, because Markdown and blocks fields contain hard-coded links to the old provider domain. Miss this and your posts keep silently loading images from a Strapi/Cloudinary account you are about to shut off. When that account goes dark, so do all your images.

Draft and publish state

Strapi has a draft-and-publish system, and in Strapi 5 the Document Service models drafts and published versions as distinct states of the same document. If your export grabs everything without filtering, you will import unfinished drafts as if they were live posts. Decide the rule — published only, or drafts as WordPress drafts — and filter on publication state during the transform.

Exporting the data — do not use strapi export

The built-in strapi export produces an encrypted archive designed for Strapi-to-Strapi transfer. It is useless for moving to WordPress. Pull the data instead through the REST API with populate to pull nested components and relations, or through the GraphQL plugin, or — if you have direct database access and the schema is stable — straight from the underlying Postgres or MySQL.

If you go the API route, respect pagination limits. Strapi caps pageSize, and a naive export that asks for everything at once silently returns a truncated set. You will think you migrated all 4,000 articles and quietly have 100. Paginate, count, and reconcile the totals against the source before you trust the export. Whatever route you choose, snapshot the data to a file and run your transform against that frozen copy — not against live production. A repeatable, safe-to-fail snapshot is the whole point.

The business logic that lived in Strapi

Strapi lifecycle hooks — the beforeCreate, afterUpdate, and similar handlers in your content-type lifecycle files — contain real business logic: slug generation, denormalisation, notifications, validation. None of that migrates with the content. It has to be reimplemented as WordPress hooks, or it silently stops happening. Inventory your lifecycle files and custom controllers early; this logic is invisible in the data and easy to forget until something that used to happen automatically simply stops.

The frontend contract changes

If a Next.js, Nuxt, or other frontend consumes Strapi’s REST or GraphQL API today, that frontend does not survive the migration untouched. It now has to consume the WordPress REST API or WPGraphQL, and the response shapes are entirely different — Strapi’s data/attributes envelope and populated relations look nothing like WordPress’s endpoints. Budget for rebuilding the frontend’s data layer, or for retiring the custom frontend and using a WordPress theme. This is frequently the largest hidden cost, and the one clients are least prepared for. Say it out loud in the first meeting.

Users and permissions do not port

Strapi’s users-permissions plugin manages accounts and roles that will not transfer cleanly. Password hashes are not portable across systems. Plan for users to reset passwords, and map Strapi roles onto WordPress roles by hand. Do not promise a seamless account migration you cannot deliver.


My honest closing position

Moving from Strapi to WordPress is not a retreat from modern architecture. For a single-channel website, it is right-sizing — trading a system built for many screens, and a Node app someone has to babysit, for a platform ordinary people can publish on without filing a ticket, hosted anywhere, maintainable by a workforce you can actually hire.

But respect the modelling work, especially the components and dynamic zones, because that is where the hours hide. And remember why WordPress is the destination in the first place: not because it is trendy, but because it carries the past forward. I learned that lesson the hard way, by building a CMS that did not, and abandoning my own users because I could not carry them.

Map the model. Prune what you do not need. Handle every component and every rich-text type by hand. Do relations in two passes and preserve their order. Rewrite the media URLs. Reimplement the lifecycle logic. Dry-run on staging until it bores you. Set your redirects before cutover.

Do that, and the content lands somewhere it can stay accurate and stay alive — which, after thirty years and every format from CD-ROM to headless, is still the only part of this job that has never changed.


One More Thing: But Can’t WordPress Just Do What Strapi Does?

Here is the question I get the moment someone decides to move: if we leave Strapi, do we have to give up the headless model we actually liked? No. You don’t. And this is where people who still think of WordPress as “just a blog” get a genuine surprise.

WordPress is not new to this. The REST API has shipped in core since late 2016 — nearly a decade of production hardening. WPGraphQL has been around almost as long, and it is now backed by Automattic and on its way to canonical status as WordPress’s GraphQL layer. In other words, the exact thing Strapi gives you — structured content delivered as JSON to whatever front-end asks — WordPress has been doing, in the open, for years. WPGraphQL and the REST API replace Strapi’s GraphQL and REST endpoints one for one; ACF and custom post types replace your collection types, components, and dynamic zones as the place your content actually lives.

So you have two honest paths off Strapi, not one:

  • Traditional WordPress — the rest of this article. Give the editors a theme and let them publish without tickets.
  • Headless WordPress — keep the decoupled architecture you liked; just swap the backend. WordPress with WooCommerce, ACF and custom post types as the content store, WPGraphQL (or the REST API) as the query layer, and your existing Next.js / Nuxt / Astro front-end — or Faust.js if you want a WPGraphQL-native framework — sitting on top, deployed exactly as before.

And here is the part that should make you sit up. You were already self-hosting a Node app — patching it, scaling it, babysitting it. Headless WordPress is self-hosted too, but it is not a bare service you keep alive alone; it is a platform. So you gain things Strapi never gave you.

  • A real commerce engine. WooCommerce. Try bolting a mature store onto a headless-only Node CMS and watch the custom code and the bill pile up.
  • Editorial power for humans, not just developers — a block editor, media library, revisions, roles, and a plugin economy for SEO, forms, memberships and more, none of which you rebuild from scratch.
  • No per-seat pricing, no usage meter. Add a hundred editors; the bill doesn’t move.
  • Ownership. The whole argument of this series — nobody can buy WordPress out from under you.

Some of what WordPress does here, Strapi simply doesn’t — commerce, the plugin economy, editorial for non-developers. Strapi is a good tool; I said so at the top and I meant it. But “we want structured content over an API” is not a reason to leave WordPress behind — it is a reason to run WordPress headless. You are not trading down. You are trading a bare Node service you had to keep alive for a platform with the same headless model, a much bigger toolbox behind it, and an owner nobody controls.

That’s the surprise. You didn’t need to choose between headless and WordPress. You can have both.

Leave a Reply

Your email address will not be published. Required fields are marked *

Close Search Window