← All playbooks
CMS migration

WordPress → Drupal

Export a WXR file from WordPress and import it with the Drupal wordpress_migrate project. The project documents posts, pages, comments, attachments, tags, and categories. The current release is an alpha for Drupal 10.5 and 11, and it can roll an import back.

Typical timeline8 to 20 business days30 to 70 hours active work
Statusneeds review
Source profileWordPress export documentation reviewed 2026-11-05
Destination profilewordpress_migrate project page reviewed 2026-11-05
Documentation date
Review window
Evidence3 sources
This route needs review.Use it as a planning baseline, then verify the dated sources and test with representative data before production.
Before you migrate

Should you make this move?

Both platforms have a case. Compare what you gain with what you give up before scheduling the cutover.

Current platform

WordPress

Reasons to stay
  • Unmatched ecosystem breadth and content ownership support almost any publishing model
  • Editors get a purpose-built publishing workflow instead of raw files
Reasons to leave
  • Plugin, hosting, security, and performance maintenance accumulate over time
  • Themes, extensions, and content models add maintenance and portability constraints
New platform

Drupal

What gets better
  • Content types, fields, and a deep module ecosystem fit complex publishing sites
  • Editors get a purpose-built publishing workflow instead of raw files
What gets worse
  • Views, custom fields, and module data need an explicit map before they mean anything in another CMS
  • Themes, extensions, and content models add maintenance and portability constraints
Best of the move

Drupal: Content types, fields, and a deep module ecosystem fit complex publishing sites. This removes a major source-side concern: Plugin, hosting, security, and performance maintenance accumulate over time.

Worst of the move

What you lose: Unmatched ecosystem breadth and content ownership support almost any publishing model. What you inherit: Views, custom fields, and module data need an explicit map before they mean anything in another CMS.

Jump to a section
01At a glance

Know the shape of the move.

Transfer outcome10 features audited
Transfer outcome distributionClean transfer: 0, Partial transfer: 6, Manual rebuild: 3, Not transferred: 1.
Clean0
Partial6
Manual3
Lost1
Mapping route8 of 9 fields have a destination path

This timeline assumes

  • The Drupal site is 10.5 or 11 if you use wordpress_migrate 8.x-3.0-alpha9, which the project page says was released on 18 March 2026.
  • You can install the project's dependencies: pathauto, migrate_tools, migrate_plus 6.0.9 or newer, and ctools.
  • You accept an alpha. The project page says there is no supported stable release.
  • The WXR export is posts, pages, comments, custom fields, categories, tags, and authors. WordPress says that is what the file contains. The Drupal module's documented import list is shorter.
  • WordPress stays up until the Drupal import is rolled forward on purpose.
02What transfers

What survives the move.

“Partial” and “manual” are not footnotes. They are work that must be scheduled and verified.

ItemOutcomeImpactWhat happensMitigation
Posts and pagespartialcriticalwordpress_migrate says it imports WordPress posts and pages from a WXR file through Drupal's Migrate API. You map them onto Drupal content types.Import into an empty content type on a staging site. Compare counts.
Comments, attachments, tags, and categoriespartialcriticalThe project page lists those four beside posts and pages. Attachments still need the files reachable, or the import has nothing to fetch.Keep WordPress public until attachment files are on Drupal.
Alpha statuspartialhigh8.x-3.0-alpha9 targets Drupal ^10.5 or ^11. The page says stable releases are covered by the security advisory policy and that this project has no supported stable release. It also says 1,921 sites report using the module.Pin the release you piloted. Do not upgrade it mid-cutover.
RollbackpartialhighThe project says imports can be rolled back completely. That is the module's rollback of imported content, not a hosting rollback.Rehearse the migrate rollback on staging before you need it.
Custom post types and buildersmanualcriticalThe project page does not list custom post types, Gutenberg block nuance, or page-builder markup. WXR can contain custom post types because WordPress says the export includes them. The module still has to map them.Confirm each custom type with a dozen items. Rebuild layouts that arrive as HTML you do not want.
Users and passwordspartialhighAuthors can come across as accounts. WordPress password hashes are not a Drupal password.Force a reset. Do not mail passwords yourself.
Media derivatives and CDN URLspartialhighAn export references files. Generated sizes and CDN hostnames need a check on the Drupal file system.Open a sample of files on a machine that is not your own.
PermalinksmanualcriticalWordPress permalinks do not become Drupal path aliases unless you map them. pathauto is a dependency, which will also try to build paths.Decide who wins, the old permalink or the new alias, and redirect the loser.
PluginsmanualhighForms, memberships, and WooCommerce data are not in the module's import list.Archive or rebuild each plugin that stores data.
RevisionslostmediumThe module description does not say WordPress revisions become Drupal revisions.Keep the WXR and the WordPress database if you need an old revision.
03Field and feature mapping

Where each thing goes.

SourceDestinationMethodNotes
WXR postDrupal nodetransformPick the content type before the import.
WXR pageDrupal nodetransformOften a basic page, if that is the type you created.
Category and tagTaxonomy termstransformListed by the module.
CommentDrupal commenttransformListed by the module. Check one thread.
AttachmentFile or mediatransformThe source URL has to stay up.
AuthorDrupal usermanualReset the password.
Custom post typeA content type you mapmanualNot in the module's feature list until you map it.
PermalinkPath alias and a redirectmanualpathauto may rewrite it. Decide first.
Plugin dataNo destinationunsupportedOutside the WXR migration the module documents.
04Before you begin

Make the move recoverable.

Backup procedure

Create the source-of-truth backup

Keep WordPress exportable and Drupal restorable.

  1. Download a full WXR from Tools, Export.
  2. Copy the WordPress uploads directory as well, in case attachment fetches fail.
  3. Back up the empty Drupal database and files before the first import, using the user guide's list: database, sites directory, uploads, and custom code.
  4. Store the alpha release id you installed.

Proof to capture: You can re-download the WXR and restore Drupal to the empty backup.

Transformation · wordpress_migrate 8.x-3.0-alpha9 and Drush, if you follow the project's current branch

WXR import

Load the export with wordpress_migrate on a staging Drupal site.

  1. Install the dependencies the project page names.
  2. Run the import against a copy of the WXR.
  3. Compare posts, pages, comments, and attachments.
  4. Use the module's rollback if the staging content is wrong, then run it again.

Proof to capture: Save the input, output, command or tool settings, warnings, and final item counts.

Transformation · pathauto and a redirect list

Path decision

Keep old WordPress URLs or replace them on purpose.

  1. Record the WordPress permalinks you still need.
  2. See what pathauto generated.
  3. Add redirects where they differ.
  4. Request a sample before DNS.

Proof to capture: Save the input, output, command or tool settings, warnings, and final item counts.

05Handle with care

The things most likely to hurt.

These are operating limits. Treat every “Stop if” condition as a blocked migration, not a suggestion.

Import

An alpha upgrade changes the import

highpossible likelihood

A later dev snapshot does not match the pilot.

Consequence
You cannot tell which release produced the content.
Mitigation
Pin alpha9 or the commit you tested.

Stop if: The running code is not the piloted release.

Transform

Custom types are silently skipped

criticalpossible likelihood

The count of posts looks fine while a custom type is absent.

Consequence
That content never arrives.
Mitigation
Count every post type in the WXR.

Stop if: A type you required is zero.

Cutover

pathauto replaces permalinks

criticalpossible likelihood

Public URLs change even though the nodes imported.

Consequence
Old links 404.
Mitigation
Diff permalinks against aliases before DNS.

Stop if: A priority URL differs and has no redirect.

06Precise timeline

Do the work in this order.

Estimate forA WordPress WXR imported by wordpress_migrate 8.x-3.0-alpha9 onto Drupal 10.5 or 11.
Total elapsed8 to 20 business days
Active work30 to 70 hours
BufferAdd time for custom post types, a large uploads directory, or a theme you are building at the same time.
01
Days 1 to 3List post types2 to 3 days
02
Days 3 to 5WXR and a Drupal backup1 to 2 days
03
Days 5 to 10Import and maybe roll back3 to 6 days
04
Days 10 to 15Freeze WordPress and import2 to 5 days
05
Days 15 to 20Watch Drupal3 to 5 days
  1. Days 1 to 3 · inventory

    List post types

    6 to 12 hours active2 to 3 days elapsedEditors waiting
    • Count posts, pages, and custom types.
    • Read the module's dependency list.

    Depends on: WordPress admin, Drupal staging

    Stop / go checkpoint

    Export?

    Go when: Each type is mapped or explicitly dropped.

    Stop when: A type has no Drupal destination.

  2. Days 3 to 5 · backup

    WXR and a Drupal backup

    3 to 6 hours active1 to 2 days elapsedExport job waiting
    • Download the WXR and the uploads.
    • Back up empty Drupal.

    Depends on: Approved map

    Stop / go checkpoint

    Import the pilot?

    Go when: The WXR opens and the Drupal backup restores.

    Stop when: Either file is missing.

  3. Days 5 to 10 · pilot

    Import and maybe roll back

    10 to 24 hours active3 to 6 days elapsedAttachment fetches waiting
    • Import a subset.
    • Roll it back once, so you know how.
    • Import it again.

    Depends on: Verified WXR

    Stop / go checkpoint

    Import the full file?

    Go when: Counts match and rollback worked.

    Stop when: Attachments or comments are short.

  4. Days 10 to 15 · cutover

    Freeze WordPress and import

    6 to 16 hours active2 to 5 days elapsedDNS waiting
    • Freeze WordPress.
    • Export a final WXR and import it.
    • Switch DNS after the URL sample passes.

    Depends on: Passed pilot

    Stop / go checkpoint

    Switch DNS?

    Go when: Drupal counts match the final WXR.

    Stop when: WordPress is still changing, or a URL fails.

  5. Days 15 to 20 · observe

    Watch Drupal

    4 to 10 hours active3 to 5 days elapsedEditors publishing waiting
    • Watch missing files and bad aliases.
    • Keep the WXR.

    Depends on: Verified cutover

    Stop / go checkpoint

    Retire WordPress?

    Go when: Blocking checks stay green.

    Stop when: A type or redirect fails.

07The point of change

Cut over with a way back.

Go live

Cutover

Publish the Drupal import and stop changing WordPress.

Recommended window: A morning when you can point DNS back at WordPress.

  1. Freeze WordPress posts.
  2. Export a final WXR and copy new uploads.
  3. Import into the Drupal site you will serve.
  4. Check counts, files, and the URL sample.
  5. Change DNS only after that.

Proof to capture: Drupal serves the host, and the node counts match the final WXR.

Return to safety

Rollback

Serve WordPress again, and use the module rollback if Drupal content should be removed.

Deadline: While the WordPress host and the WXR still exist. The project warns there is no stable release, so do not depend on a later upgrade for the rollback.

  1. Point DNS back at WordPress.
  2. Stop editors in Drupal.
  3. Run the migrate rollback if you want the imported nodes removed, after you have exported any Drupal-only edits.
  4. Review those edits before they go anywhere near WordPress.
  5. Confirm the old permalinks respond.

Proof to capture: WordPress is live, and Drupal imported content is either rolled back or clearly unused.

Rollback immediately when
  • A documented type is short of the WXR count
  • Attachments fail
  • Priority permalinks 404
  • Rollback on staging did not remove what you imported
08Verification report

Prove the migration worked.

Every blocking check must pass. Capture the evidence before cleanup begins.

0%
Interactive report preview0 / 8 checks passed
PassIDCheckMethodExpected resultEvidence
V-01BlockingCountsCompare WXR types with Drupal nodes.Posts, pages, and any mapped custom type match.Count sheet
V-02BlockingThreadsOpen a post that had comments.The comments are present.Comment sample
V-03BlockingAttachmentsOpen files as an anonymous visitor.They load from Drupal.File sample
V-04BlockingTags and categoriesCheck a sample of terms.They match the WXR.Term sample
V-05BlockingAliasesRequest old permalinks.Each one resolves.Redirect log
V-06BlockingStaging rollbackRoll back a staging import and count nodes.Imported nodes are gone.Rollback note
V-07BlockingResetsSend a reset to a test author.The account is not using a WordPress hash.Reset note
V-08BlockingPinned codeRead the module version on the server.It is the alpha you piloted.Version note
09Post-migration cleanup

Remove the scaffolding safely.

Safe after: Drupal has been the live site through a publishing cycle, and the WXR is archived.

  1. Store the WXR and the uploads copy.
  2. Remove migration credentials.
  3. Keep the pinned module version noted in the runbook.
  4. Leave WordPress up until redirects are no longer your rollback.
  5. Turn on Drupal backups from the user guide list before you turn WordPress off.
Sources and evidence

Verify against the primary material.

Platform behavior changes. Check these sources and the review dates above before executing a production migration.

  1. WordPress: Tools Export screenAccessed 2026-11-05
  2. Drupal.org: WordPress MigrateAccessed 2026-11-05
  3. Drupal User Guide: data backupsAccessed 2026-11-05