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

I have moved content off more systems than I can list. CD-ROMs to the web. Flash to HTML. Enterprise content management platforms that cost more than a house. Our own homegrown CMS. Drupal, Sitecore, Kentico, Umbraco, Contentful, Strapi, Sanity — the specific source keeps changing, and every time, someone tells me this one is different.

It never is. Not really. The format on the outside changes; the problem underneath does not. Get the content to the right place, keep it accurate, keep it alive. That was the job on a plastic disc in 1996 and it is the job migrating a headless stack today. Once you understand that, you stop treating each migration as a new adventure and start treating it as the same problem wearing a different costume.

This article is the platform-agnostic version — the method I apply to any source CMS. Read it alongside the platform-specific guides in this series; those cover the quirks, this covers the method that does not change.

The one idea that governs everything

A migration is not an export-import problem. It is a content-modelling problem wearing an export-import costume. Teams that treat it as “get the data out, push the data in” produce migrations that technically complete and practically fail — content in the wrong shape, editors who cannot work with it, structure that made sense in the old system and makes none in the new one.

I come from print, where layout is the last step and the text arrives before you arrange it. That discipline never left me: structure before style, always. In a migration, that means you model the content properly before you move a single row. Everything else follows from getting the shape right.

Why WordPress, specifically

Before the method, the reason. I do not send people to WordPress because it is fashionable. I send them there because of things I learned the hard way:

Platforms die of ownership, not technical failure. I watched every serious ECM platform of the 2000s — Vignette, Interwoven, FatWire, Stellent — get acquired, absorbed, renamed, and quietly retired. Not one died because the software was bad. They died because someone bought the company and their customers’ content strategy became a line item in an integration plan. WordPress cannot be acquired. Nobody owns it. That is not a small thing.

Backward compatibility is the whole game. I learned this by building a CMS that did not have it. We built our own system, dozens of agencies used it, and it had no backward compatibility — every version forgot the past, so every release forced our users to migrate by hand. We could not carry that weight and we abandoned our own users. WordPress made the opposite bet for twenty years: code from a decade ago mostly still runs. When I tell a client their site will still be maintainable in ten years, I am not repeating marketing — I am telling them what it cost me to learn.

Open source means an exit. The point is not that it is free. The point is that if anything goes wrong — a fork, a fight, a change of direction — the code is yours and you can leave. Most of the platforms people migrate away from lack that exit. WordPress has it.

That is why the destination is WordPress. Now the method.

The playbook

1. Inventory the content model — completely

Every content type, every field, every relationship, every taxonomy, every media reference, every language variant, every reusable component. Not a vague list — an actual map of the shape. You cannot move what you have not mapped, and you cannot estimate what you have not counted. Most blown migration budgets are blown right here, because someone guessed at the model instead of inventorying it.

2. Decide what NOT to migrate

This is the step everyone resists and everyone regrets skipping. Any content model that has grown for a few years is full of fields nobody fills in, content types created for a campaign that ended, and structure that exists because a developer was being thorough. Migration is the one moment you are allowed to throw things away. Take it.

I learned to trust the obvious here. Every idea I ever dismissed as too simple to matter turned out to matter; every over-built structure I created turned out to be dead weight. Prune ruthlessly. A leaner model migrates faster, validates easier, and serves editors better.

3. Map the old model to WordPress — honestly

This is the craft. Content types become custom post types. Fields become native fields, ACF fields, or taxonomy. Relationships become ACF relationship fields, post connections, or terms. Hierarchical trees become WordPress page hierarchy where the hierarchy is real, and flatten to posts-plus-taxonomy where it is not. Reusable components and page-builder blocks — the single hardest thing in almost every migration — become Gutenberg blocks/patterns or ACF flexible content, handled type by type, by hand.

The rule: do not force the old system’s shape onto WordPress, and do not force WordPress’s defaults onto content that needs structure. Map to what the content actually is.

4. Build the empty target first

Stand up the custom post types, ACF field groups, taxonomies, and block patterns in WordPress — empty — and validate the shape with the people who will live in it. I used to make rubber stamps of UI elements so clients could see structure before I built anything; the principle is the same here. Get the shape right and agreed before real content arrives, because restructuring after import is ten times the work.

5. Extract to a frozen snapshot — never work live

Pull the content out via the source system’s export tool, API, or a direct database read — and freeze it to a file. Then run your transform against that frozen copy, not against live production. I burned CD-ROMs for a living; the worst feeling in this business is a mistake you cannot recall. A frozen snapshot is your recall button: you can run the transform a hundred times, get it wrong ninety-nine, and never touch the source.

One universal warning: respect pagination. Every API-based export has limits, and a naive “give me everything” call silently returns a truncated set. Count the source records and reconcile against your export before you trust it. “We migrated everything” is the most dangerous sentence in this work when nobody checked the totals.

6. Transform — and resolve references in two passes

The transform maps each source record to a WordPress post. The universal trap is references: source records point at each other by IDs that mean nothing in WordPress, and you cannot resolve them in one pass because the target may not exist yet when you import the source.

Do it in two passes. First, import every record and build a map of old ID to new WordPress post ID, preserving the old IDs in post meta. Second, rewrite every reference — relationships, taxonomy, in-content links, embedded media — using that map. Get the ordering wrong and your relationships silently point at the wrong things. This pattern is the same for every source system I have ever migrated.

7. Migrate media, and rewrite in-content URLs

Download every asset and re-import it into the WordPress media library. Then — the step everyone forgets — rewrite every media URL embedded inside rich-text content, because those are hard-coded to the old system’s domain or CDN. Miss this and your migrated pages keep loading images from a system you are about to switch off. When it goes dark, so do your images.

8. Redirects and SEO — before cutover, not after

Your URL structure will change. Map every old path to its new one and put the redirects in place before you flip the switch. Skip this and you watch rankings you spent years building evaporate in a week. Most source systems hand you the redirect map for free in their existing URL/alias structure — use it.

9. Dry-run on staging until it is boring

Never migrate straight to production. Run the whole pipeline into a staging WordPress, look at real pages, let editors click around, fix what is wrong, and run it again. When a dry run produces nothing surprising, you are ready. Not before.

10. Cut over, then keep watching

The migration is not done at cutover. It is done when the content team has published several real things without calling you. Watch redirects, watch 404 logs, watch the editors. Then it is finished.

The things that are true no matter what you’re leaving

  • Components and page-builder blocks are always the hardest part and always the most underestimated. Budget for them explicitly.
  • References always need two passes. Always.
  • Rich text always hides embedded, system-specific tokens — media, internal links, macros. Inventory them before you serialize.
  • Presentation never migrates. Templates, layouts, renderings, views — the machinery that arranges content is code, not content. Plan to rebuild it.
  • Passwords never port. Plan for resets.
  • Business logic in the old system doesn’t come with the content. Hooks, lifecycle handlers, workflow — inventory and reimplement.
  • A custom front-end means a second project. If something consumes the old system’s API, moving to WordPress changes the contract to REST or WPGraphQL.

My honest closing position

I have watched the industry treat every new platform as a revolution and every migration as uncharted territory. It is neither. The tools change, the pain moves around, but the job has been identical since I was shipping databases on plastic discs: get the content to the right place, keep it accurate, keep it alive.

Model before you move. Prune what you do not need. Two passes for references. Rewrite the media URLs. Rebuild the presentation. Redirects before cutover. Dry-run until it bores you. Do that, on any source system, and the content lands somewhere ordinary people can keep it alive — which, after thirty years and every format from CD-ROM to headless, is the only part of this that has never once changed.


One More Thing: But Can’t WordPress Just Do What Your Old CMS Does?

Here is the objection I hear the moment someone decides to leave: if we go to WordPress, do we have to give up the things our old CMS was actually good at? No. You don’t. And as The WP Clan, we are not about to shortchange WordPress in front of you — because whatever you are leaving, WordPress can model it, and probably brings more.

Whatever shape your content took over there — structured types, deep relationships, rich taxonomies, many languages — WordPress models it natively. Custom post types and ACF for the types and fields. Taxonomies for the relationships. Multisite, Polylang or WPML for the languages. There is very little a serious CMS holds that WordPress cannot represent honestly.

And if the thing you liked was headless — content over an API — WordPress does that too, and it is not new to it. The REST API has shipped in core since late 2016. WPGraphQL has been around almost as long, is now backed by Automattic, and is on its way to canonical status as WordPress’s GraphQL layer. WordPress did not lose the headless argument; it absorbed it. Keep your Next.js, Nuxt or Astro front-end, swap the backend, query with WPGraphQL, and your front-end barely notices the CMS changed underneath it.

Then it brings what a pure headless SaaS never does:

  • A real commerce engine. WooCommerce, not a store you hand-build and maintain forever.
  • Editorial power for humans, not just developers — block editor, media library, revisions, roles, and a plugin ecosystem for SEO, forms and memberships you never 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.

You don’t have to choose between headless and WordPress. Whatever your old CMS did well, WordPress does — with a much bigger toolbox behind it.

Leave a Reply

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

Close Search Window