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.
Should you make this move?
Both platforms have a case. Compare what you gain with what you give up before scheduling the cutover.
WordPress
- Unmatched ecosystem breadth and content ownership support almost any publishing model
- Editors get a purpose-built publishing workflow instead of raw files
- Plugin, hosting, security, and performance maintenance accumulate over time
- Themes, extensions, and content models add maintenance and portability constraints
Drupal
- Content types, fields, and a deep module ecosystem fit complex publishing sites
- Editors get a purpose-built publishing workflow instead of raw files
- 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
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.
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
Know the shape of the move.
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.
What survives the move.
“Partial” and “manual” are not footnotes. They are work that must be scheduled and verified.
| Item | Outcome | Impact | What happens | Mitigation |
|---|---|---|---|---|
| Posts and pages | partial | critical | wordpress_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 categories | partial | critical | The 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 status | partial | high | 8.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. |
| Rollback | partial | high | The 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 builders | manual | critical | The 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 passwords | partial | high | Authors 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 URLs | partial | high | An 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. |
| Permalinks | manual | critical | WordPress 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. |
| Plugins | manual | high | Forms, memberships, and WooCommerce data are not in the module's import list. | Archive or rebuild each plugin that stores data. |
| Revisions | lost | medium | The module description does not say WordPress revisions become Drupal revisions. | Keep the WXR and the WordPress database if you need an old revision. |
Where each thing goes.
| Source | Destination | Method | Notes |
|---|---|---|---|
| WXR post | Drupal node | transform | Pick the content type before the import. |
| WXR page | Drupal node | transform | Often a basic page, if that is the type you created. |
| Category and tag | Taxonomy terms | transform | Listed by the module. |
| Comment | Drupal comment | transform | Listed by the module. Check one thread. |
| Attachment | File or media | transform | The source URL has to stay up. |
| Author | Drupal user | manual | Reset the password. |
| Custom post type | A content type you map | manual | Not in the module's feature list until you map it. |
| Permalink | Path alias and a redirect | manual | pathauto may rewrite it. Decide first. |
| Plugin data | No destination | unsupported | Outside the WXR migration the module documents. |
Make the move recoverable.
Create the source-of-truth backup
Keep WordPress exportable and Drupal restorable.
- Download a full WXR from Tools, Export.
- Copy the WordPress uploads directory as well, in case attachment fetches fail.
- 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.
- Store the alpha release id you installed.
Proof to capture: You can re-download the WXR and restore Drupal to the empty backup.
WXR import
Load the export with wordpress_migrate on a staging Drupal site.
- Install the dependencies the project page names.
- Run the import against a copy of the WXR.
- Compare posts, pages, comments, and attachments.
- 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.
Path decision
Keep old WordPress URLs or replace them on purpose.
- Record the WordPress permalinks you still need.
- See what pathauto generated.
- Add redirects where they differ.
- Request a sample before DNS.
Proof to capture: Save the input, output, command or tool settings, warnings, and final item counts.
The things most likely to hurt.
These are operating limits. Treat every “Stop if” condition as a blocked migration, not a suggestion.
An alpha upgrade changes the import
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.
Custom types are silently skipped
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.
pathauto replaces permalinks
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.
Do the work in this order.
- 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 checkpointExport?
Go when: Each type is mapped or explicitly dropped.
Stop when: A type has no Drupal destination.
- 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 checkpointImport the pilot?
Go when: The WXR opens and the Drupal backup restores.
Stop when: Either file is missing.
- 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 checkpointImport the full file?
Go when: Counts match and rollback worked.
Stop when: Attachments or comments are short.
- 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 checkpointSwitch DNS?
Go when: Drupal counts match the final WXR.
Stop when: WordPress is still changing, or a URL fails.
- 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 checkpointRetire WordPress?
Go when: Blocking checks stay green.
Stop when: A type or redirect fails.
Cut over with a way back.
Cutover
Publish the Drupal import and stop changing WordPress.
- Freeze WordPress posts.
- Export a final WXR and copy new uploads.
- Import into the Drupal site you will serve.
- Check counts, files, and the URL sample.
- Change DNS only after that.
Proof to capture: Drupal serves the host, and the node counts match the final WXR.
Rollback
Serve WordPress again, and use the module rollback if Drupal content should be removed.
- Point DNS back at WordPress.
- Stop editors in Drupal.
- Run the migrate rollback if you want the imported nodes removed, after you have exported any Drupal-only edits.
- Review those edits before they go anywhere near WordPress.
- Confirm the old permalinks respond.
Proof to capture: WordPress is live, and Drupal imported content is either rolled back or clearly unused.
- A documented type is short of the WXR count
- Attachments fail
- Priority permalinks 404
- Rollback on staging did not remove what you imported
Prove the migration worked.
Every blocking check must pass. Capture the evidence before cleanup begins.
| Pass | ID | Check | Method | Expected result | Evidence |
|---|---|---|---|---|---|
V-01Blocking | Counts | Compare WXR types with Drupal nodes. | Posts, pages, and any mapped custom type match. | Count sheet | |
V-02Blocking | Threads | Open a post that had comments. | The comments are present. | Comment sample | |
V-03Blocking | Attachments | Open files as an anonymous visitor. | They load from Drupal. | File sample | |
V-04Blocking | Tags and categories | Check a sample of terms. | They match the WXR. | Term sample | |
V-05Blocking | Aliases | Request old permalinks. | Each one resolves. | Redirect log | |
V-06Blocking | Staging rollback | Roll back a staging import and count nodes. | Imported nodes are gone. | Rollback note | |
V-07Blocking | Resets | Send a reset to a test author. | The account is not using a WordPress hash. | Reset note | |
V-08Blocking | Pinned code | Read the module version on the server. | It is the alpha you piloted. | Version note |
Remove the scaffolding safely.
Safe after: Drupal has been the live site through a publishing cycle, and the WXR is archived.
- Store the WXR and the uploads copy.
- Remove migration credentials.
- Keep the pinned module version noted in the runbook.
- Leave WordPress up until redirects are no longer your rollback.
- Turn on Drupal backups from the user guide list before you turn WordPress off.
Verify against the primary material.
Platform behavior changes. Check these sources and the review dates above before executing a production migration.
- WordPress: Tools Export screenAccessed 2026-11-05
- Drupal.org: WordPress MigrateAccessed 2026-11-05
- Drupal User Guide: data backupsAccessed 2026-11-05