Free for content · Pro for the rest
Move the whole site, not the easy half
Craft's own wp-import is good, and it stops halfway. It does not handle WooCommerce, SEO metadata, redirects, menus, widgets or forms — and it reads WordPress over the REST API only. Passer fixes the source layer first, then the six domains that live in it.
Four ways in, one mapping
A site being moved off WordPress is very often one that is half-broken: behind Basic auth, with /wp-json/ blocked by a security plugin, on a host that will not open a port, or existing now only as a .sql file on someone's laptop. Every source produces the same records, so the mapping you build does not change with the connection you happen to have.
MySQL database Everything A live database, a tunnel, or a restored dump
WXR export file Content, terms, users, comments No credentials, no network, works on a dead site
REST API What REST exposes Easiest to set up, weakest data
wp-cli over SSH Everything A shell, but no database port
# A WXR export cannot see widgets, because WordPress does not export
# wp_options. Passer greys that phase out and says why, rather than
# importing nothing and reporting success.
Features
The data these domains live in — wp_options for menus and widgets, wp_postmeta for SEO, the wc_* tables for commerce — is barely exposed over REST at all. Passer reads them where they actually live.
WooCommerce → Craft Commerce
Products, variations, attributes, categories, coupons, customers and orders — reading legacy post-based orders or High-Performance Order Storage, whichever this shop uses.
- Orders arrive as history, keeping the totals the customer was actually charged
- Anything that does not reconcile is reported, not quietly adjusted
SEO that survives a plugin switch
Yoast, Rank Math, All in One SEO, SEOPress and The SEO Framework. Template variables are expanded, so a title stored as %%title%% %%sep%% %%sitename%% arrives as a title.
- On a site that switched plugins and never cleaned up, Passer takes whichever plugin has more data for that post
Redirects that point at entries
Redirection, Rank Math, All in One SEO, Safe Redirect Manager, Simple 301 Redirects, the EPS plugin, and a pasted .htaccess.
- Destinations are resolved through the ID map to the imported content, so a redirect points at the entry rather than at a URL the migration is about to change
Menus, widgets and forms
Menu items that point at posts become relations, not frozen URLs. Widgets with real content are imported; the ones that are configuration are described rather than faked. Contact Form 7, Gravity Forms, WPForms, Ninja Forms and Formidable, with their stored entries.
- What cannot come across — conditional logic, payment gateways, captcha keys — is listed explicitly rather than silently dropped
Content that arrives intact
Gutenberg blocks are parsed properly, not regexed. Classic content gets the treatment WordPress applies at render time and never stores — paragraphs from double newlines, and bare URLs turned into links. ACF values are read through their field definitions, repeaters and flexible content included.
- A block nothing handles keeps its rendered markup rather than vanishing, and is named in the report
- A shortcode with no handler is left in place, because it marks something that needs a decision
Read-only by construction
The database source attempts a write on connect and refuses the credentials if it succeeds. A migration has no business holding write access to the site it is reading, and the failure mode if it does is unrecoverable.
Nothing is required
Passer hard-requires none of the plugins it writes to. SEOmatic, Retour, FreeNav, Formie, Verbb Comments and Craft Commerce are all preferred destinations — and every one has a fallback that needs nothing but Craft.
- The wizard shows you what it found before you commit to a plan
Detection by evidence
Analyse finds plugins by the traces they left — a table, an option, a meta key — not by asking WordPress which plugins are active. A plugin deactivated last year still has all its data, and that data is exactly what needs migrating.
Seven phases, every one re-runnable
A real migration is never run once: it is run against a copy, examined, corrected, and run again over weeks while the WordPress site is still being edited. Every WordPress record that became something in Craft goes into the ID map, so a second run updates rather than duplicating.
connect → analyse → plan → provision → test-import → run → report
php craft passer/migrate/analyze --plan=1 # what's in the source
php craft passer/migrate/provision --plan=1 -d # dry-run the content model
php craft passer/migrate/run --plan=1 --provision # provision, then migrate
php craft passer/migrate/run --plan=1 --phases=seo,redirects
php craft passer/migrate/resume 7 # continue an interrupted run
php craft passer/migrate/report 7
Frequently Asked Questions
The questions worth answering before you migrate anything.
Lite is, and it is not a trial. It brings across posts, pages, custom post types, users, media, taxonomies and comments from a WXR export or the REST API, with the full Gutenberg, classic-content, shortcode and ACF handling and the whole plan-provision-run pipeline — roughly parity with craftcms/wp-import, from the control panel rather than the command line.
Pro is a one-off $99 with a $79/year renewal, and adds the database and wp-cli sources plus the six domains that only they can reach: WooCommerce, SEO metadata, redirects, menus, widgets and forms.
Reach and sources. wp-import handles content, users, media, taxonomies, ACF and Gutenberg, and explicitly does not handle WooCommerce, SEO, redirects, menus, widgets or forms. It also reads over the REST API only, from the command line only — and the data those six domains live in is barely exposed over REST at all. Fixing the domains means fixing the source layer first, which is what Passer is.
No. If it is installed, Passer reuses its ACF adapters and Gutenberg block transformers for anything it does not handle itself, including adapters you wrote in config/wp-import/. The coupling is deliberately loose, so a bridge that cannot load leaves Passer's own adapters in place rather than failing the migration.
Never. Every connection is read-only, and the database source enforces it: it attempts a write on connect and refuses the credentials if it succeeds.
That is the design. Every WordPress record that became something in Craft is in the ID map, so a second run updates rather than duplicating. Migrate a copy in week one, correct the mapping, and re-run against the live site on cutover day. The map is keyed per plan, so re-run the original plan rather than creating a new one.
Yes, if you have a .sql dump or a WXR export. The database source works against a restored dump, and a WXR file needs no credentials and no network at all.
Imported users need a reset. WordPress hashes with phpass or bcrypt in its own format and Craft cannot verify either. Passer says so in the report rather than implying otherwise.
No. Templates are the developer's job, and always will be.
No. Every domain has a fallback that needs nothing but Craft — plain fields for SEO, a config/redirects.php for redirects, a structure section for menus, global sets for widgets — and the wizard shows you what it found.
Craft CMS 5.3+ and PHP 8.2+. Importing WooCommerce needs Craft Commerce 5.0+.
Start with the content, buy the rest when you need it
Lite is free and covers content from a WXR export or the REST API. Pro is $99 with a $79/year renewal, and adds the database and wp-cli sources plus WooCommerce, SEO, redirects, menus, widgets and forms.