How to Migrate from WordPress to UnfoldCMS (Without Breaking SEO)
A developer's playbook: audit, export, import, redirect, cutover
You've decided to leave WordPress. Now comes the hard part: actually moving without breaking SEO, losing content, or watching your client's traffic crater for three weeks while Google figures out what happened.
This guide is the playbook we wish we'd had the first time we did a WordPress migration. It assumes you already know why you're leaving (if you're still on the fence, read why developers are leaving WordPress first) and you want to know how — without the hand-waving.
TL;DR: A safe WordPress → UnfoldCMS migration is five phases: audit existing content, export from WordPress (WXR + database + media), import into UnfoldCMS using the included Laravel commands, set up 301 redirects for every old URL, and verify SEO signals (sitemap, schema, canonicals, Search Console) before flipping DNS. With a proper inventory, a 50-page site takes about a day. A 2,000-page site takes a week — most of which is QA, not the migration itself. Skip the redirect step and you'll lose 40-70% of your organic traffic.
Before You Migrate: The 30-Minute Audit That Saves Weeks
Most botched migrations fail in the first hour — before anyone touches the database. A migration without an audit is a guess. Spend 30 minutes producing a real inventory and you'll know exactly what success looks like.
Run this audit on the live WordPress site:
# Get a complete URL list straight from WordPress
wp post list --post_type=any --format=csv --fields=ID,post_type,post_status,post_title,post_name > inventory.csv
# Or, if WP-CLI isn't available, pull the sitemap
curl -sL https://yoursite.com/sitemap_index.xml > sitemap.xml
Things to record before you start:
| Asset | Why it matters |
|---|---|
| Total URL count | Sets the scale of redirects you'll need |
| Custom post types | These won't map automatically — plan a target model for each |
| Custom taxonomies | Same problem; categories ≠ taxonomies |
| Plugins generating URLs | Forms, calculators, members areas — these don't migrate cleanly |
| Media library size | Large media stores need rsync, not HTTP imports |
| Hard-coded shortcodes | These break on the new platform without conversion |
| Internal link patterns | Anything with /wp-content/, /?p=123, or /index.php/ paths |
"I once migrated a 4,000-page WordPress site without auditing custom post types first. The new CMS imported 3,200 of them. The other 800 were
[event]shortcode pages that lived in plugin tables — invisible to the WXR exporter. Found out three weeks after launch when the client asked why their event archive was empty." — Migration consultant, r/webdev, March 2026
The audit is also where you make the hardest call: what doesn't make the move? Eight years of WordPress almost always means dead categories, retired tag pages, redirected old URLs, and abandoned plugin landing pages. Migrate aggressively, not exhaustively. Less debt is the whole point.
Step 1: Export Everything from WordPress
WordPress has three separate things you need to extract: content (posts, pages, taxonomies), media (uploads), and structured metadata (SEO titles, descriptions, custom fields).
1a. Export Content with WXR
The native WordPress eXtended RSS export is the cleanest path:
# WP-CLI (preferred — works for any size)
wp export --dir=./export --filename_format=wp-export.xml
# Or via Tools → Export in wp-admin (smaller sites only)
WXR captures: posts, pages, custom post types, categories, tags, comments, authors, and core meta. It does not capture: ACF field values stored as serialized PHP, plugin tables, or theme customizer settings. We'll handle those in Step 1c.
1b. Export the Database
Even if you trust WXR, dump the database too — it's your fallback when something turns out missing:
# On the WordPress server
mysqldump -u wp_user -p \
--single-transaction \
--skip-lock-tables \
--default-character-set=utf8mb4 \
wordpress_db > wp-backup.sql
# Compress it for transfer
gzip wp-backup.sql
Two flags that matter: --single-transaction (so the dump is consistent without locking writes) and --default-character-set=utf8mb4 (so emojis and non-Latin characters don't get mangled into ? characters mid-export).
1c. Export Custom Fields and SEO Metadata
This is where most migrations leak data. Plugin metadata sits in wp_postmeta and is invisible to the standard exporter. You need to dump it explicitly:
# Yoast SEO data
mysql wordpress_db -e "SELECT post_id, meta_key, meta_value FROM wp_postmeta WHERE meta_key LIKE '_yoast_wpseo_%';" > yoast-meta.tsv
# RankMath data
mysql wordpress_db -e "SELECT post_id, meta_key, meta_value FROM wp_postmeta WHERE meta_key LIKE 'rank_math_%';" > rankmath-meta.tsv
# ACF custom fields
mysql wordpress_db -e "SELECT post_id, meta_key, meta_value FROM wp_postmeta WHERE meta_key NOT LIKE '\_%' OR meta_key LIKE '\_oembed\_%';" > acf-meta.tsv
Save these alongside your WXR export. UnfoldCMS's import tool reads them on the next pass.
1d. Export Media
Media is the slowest part of any migration. Don't try to copy 12 GB of uploads through PHP — it'll time out.
# On the WordPress server, archive everything
cd /var/www/wordpress/wp-content
tar -czf uploads.tar.gz uploads/
# Or rsync directly to your local machine if you have SSH access
rsync -avz --progress \
user@wp-server:/var/www/wordpress/wp-content/uploads/ \
./wp-uploads/
For sites over 5 GB, run this overnight. There's nothing technically hard about copying files; it's just slow.
Step 2: Stand Up UnfoldCMS
Before you import anything, get a clean UnfoldCMS instance running. The full quickstart lives in the docs, but the short version:
# Clone the starter
git clone https://github.com/unfoldcms/unfoldcms.git mysite
cd mysite
# Install dependencies
composer install
pnpm install
# Set up environment
cp .env.example .env
php artisan key:generate
# Database
php artisan migrate
php artisan db:seed
# Frontend assets
pnpm run build
Within ten minutes you should have an admin login at /admin and an empty site at /. Don't customize the theme yet — that comes after the import works end-to-end.
A note on hosting choice: if you're coming from WordPress shared hosting, UnfoldCMS runs comfortably on the same shared hosts. The CMS is built specifically with this in mind — synchronous job execution, no background workers required, single-cron scheduler. If you want the deeper rationale, self-hosted vs SaaS CMS walks through where each model fits.
Step 3: Import the Content
UnfoldCMS ships with a wordpress:import Artisan command that reads WXR, the meta TSVs, and the uploads directory in a single pass.
php artisan wordpress:import \
--wxr=./export/wp-export.xml \
--uploads=./wp-uploads \
--yoast=./export/yoast-meta.tsv \
--acf=./export/acf-meta.tsv \
--map-cpt=event:Page \
--map-cpt=portfolio:Post \
--dry-run
The flags worth knowing:
--dry-runprints what would happen without writing anything. Always run this first. It surfaces broken WXR entries, missing media, and bad URL slugs before they touch your database.--map-cpt=oldType:NewModelmaps WordPress custom post types to UnfoldCMS models. Anything you don't map gets imported as aPost.--preserve-idskeeps WordPress post IDs as UnfoldCMS IDs, which makes redirect rules trivial later.
Run with --dry-run first. Read the output. Read it again. When the dry run is clean, drop the flag and run for real.
php artisan wordpress:import \
--wxr=./export/wp-export.xml \
--uploads=./wp-uploads \
--yoast=./export/yoast-meta.tsv \
--acf=./export/acf-meta.tsv \
--preserve-ids
A 1,000-post site with 3 GB of media takes 8–15 minutes on a modest server. The slow part is image processing (UnfoldCMS regenerates thumbnails and converts to WebP); content ingest itself is under a minute.
What the Importer Handles Automatically
| Source (WordPress) | Target (UnfoldCMS) |
|---|---|
wp_posts (post_type=post) |
BlogPost |
wp_posts (post_type=page) |
Page |
wp_posts (custom post types) |
Mapped via --map-cpt, default BlogPost |
| Categories + tags | BlogCategory + Tag |
| Featured images | Media model + linked to post |
| Yoast/RankMath title & description | seo table (seo_title, meta_desc, og_*) |
| Author accounts | User model with author role |
| Comments | Comment model (status mapped 1:1) |
Inline <img> tags |
Rewritten to point at the new media library |
What You Have to Handle Manually
- Shortcodes —
[gallery],[contact-form-7],[wpforms], etc. The importer leaves them in the body so you can grep for[and convert them. Most map cleanly to UnfoldCMS section blocks. - Theme-specific page builders — Elementor, Divi, Beaver Builder. These store data in serialized blobs and rarely export usefully. Plan to rebuild high-traffic pages by hand. Low-traffic pages: redirect to the new home page, kill the URL, move on.
- Plugin-generated archives — Events, products, members. If they're driven by a custom post type, they came across in the import. If they're driven by a plugin's own tables, they didn't.
Step 4: Preserve SEO — The Step Most People Skip
Skip this step and you'll lose 40–70% of your organic traffic. This is the part nobody highlights in migration tutorials, and it's the part that decides whether your client calls you angry in three weeks.
Three things have to survive the move: URL structure, on-page SEO metadata, and structured data.
4a. Preserve URL Structure with 301 Redirects
WordPress permalinks rarely match what your new platform produces by default. WordPress: /2024/03/15/post-title/. UnfoldCMS by default: /blog/post-title. Even if both work, Google sees them as different URLs — which means every backlink and every ranking starts from zero unless you redirect.
UnfoldCMS has a built-in redirect manager (admin → SEO → Redirects) and an Artisan command to bulk-import:
# Generate the redirect map from your WXR + new slugs
php artisan wordpress:redirect-map \
--wxr=./export/wp-export.xml \
--output=./redirects.csv
# Edit the CSV if needed, then import
php artisan redirects:import ./redirects.csv
The CSV format is dead simple:
old_url,new_url,status_code
/2024/03/15/why-headless-matters/,/blog/why-headless-matters,301
/?p=4521,/blog/why-headless-matters,301
/category/development/,/blog/category/development,301
/wp-content/uploads/2024/hero.jpg,/storage/media/2024/hero.jpg,301
The redirect manager honors:
- Exact-match URLs
- Wildcard patterns (
/wp-content/*→/storage/media/$1) - Query string redirects (
/?p=ID→ permalink) - Author archive redirects (
/author/jane/→/team/jane)
Test redirects in staging by hitting old URLs with curl -I and checking the Location: header. If it returns 200, your redirect didn't fire.
4b. Preserve On-Page SEO
The importer reads your Yoast/RankMath metadata and writes it to UnfoldCMS's seo table — meaning the title tags, meta descriptions, OG images, canonical URLs, and noindex flags all carry across. Spot-check 10–20 representative pages in the admin under each post's SEO tab. If something looks wrong, fix it in the admin; don't reach into the database.
One detail that catches people: WordPress stores meta titles with placeholders like %%title%% - %%sitename%%. The importer resolves these on import, so what ends up in UnfoldCMS is the expanded string. If you change your site name later, you'll need to update titles manually. We chose this trade-off deliberately — it makes the SEO table self-contained and portable.
4c. Schema and Sitemaps
UnfoldCMS handles structured data automatically using the @jsonld() Blade directive — Article schema for posts, Organization schema globally, BreadcrumbList on every page. You don't need to migrate anything; the new site emits clean schema by default.
For the XML sitemap, run:
php artisan sitemap:generate
This produces /sitemap.xml referencing all published posts, pages, categories, and tags. Submit it to Search Console before the cutover — Google can warm up the new URL list while DNS still points at WordPress. (More on the cutover sequence below.)
Step 5: Cutover — Going Live Without Breaking Production
This is the choreography that turns "we have a working migration" into "we have a live site." Six steps, in order:
- Freeze WordPress writes. Lock the wp-admin to admin-only via
.htaccessIP allow-list, or just put the site into maintenance mode. New content during cutover is the #1 cause of post-launch confusion. - Run a final delta import. Re-export WXR, re-run
wordpress:importwith--update(it upserts based on slug+date). This catches any posts published since your initial import. - Submit the new sitemap to Search Console. Use the live target domain — Google can crawl it before DNS flips because the new server is already responding on its own IP.
- Update DNS. Lower the TTL to 300 seconds 24 hours before launch so the cutover is fast. Then point A/AAAA records at the new server.
- Monitor 404s aggressively. UnfoldCMS logs 404s in
storage/logs/404.log. Tail this file for the first 48 hours; any pattern you see is a missing redirect rule. Add the rule, the next request resolves correctly. - Keep WordPress running for 30 days. Don't delete the WordPress database or files until you've verified no mission-critical URL is missing. Storage is cheap; data loss is forever.
"We left WordPress running on a subdomain for six weeks after launch. Three times in that window we found a redirect we'd missed — including one driving 8% of total traffic. Without the fallback we would've eaten that loss." — Agency lead, Hacker News, January 2026
How Long Does a WordPress Migration Take?
A WordPress to UnfoldCMS migration takes between four hours and two weeks, depending almost entirely on content volume and SEO complexity. The platform isn't the bottleneck — content audit, redirect rules, and QA are.
Realistic estimates from sites we've helped move:
| Site Profile | Total Time | Bottleneck |
|---|---|---|
| 50 pages, no custom post types, 1 author | 4–8 hours | Theme rebuild |
| 200 posts, 1 custom post type, basic ACF | 1–2 days | ACF field mapping |
| 1,000+ posts, multiple CPTs, complex permalinks | 3–5 days | Redirect rules + 404 monitoring |
| 5,000+ pages, ecommerce, 10+ years of permalinks | 1–3 weeks | URL audit + WooCommerce decisions |
Two thirds of that time is QA, not import. The actual php artisan wordpress:import runs in minutes.
What If I Have WooCommerce?
Don't migrate WooCommerce stores to UnfoldCMS — at least not yet. UnfoldCMS is a content platform, not an ecommerce engine. If your WordPress site is primarily a store, the right move is either keeping WordPress for commerce while moving the content layer (blog, marketing pages, docs) to UnfoldCMS, or migrating commerce to a dedicated platform like Shopify or Stripe Checkout while UnfoldCMS handles the marketing front end.
A hybrid setup is more common than people think — UnfoldCMS at the apex domain serving marketing/blog/docs, WooCommerce on a subdomain handling cart/checkout. Best-of-breed beats jack-of-all-trades for storefronts. We'd rather tell you that honestly than oversell the migration.
What Most Migration Tutorials Get Wrong
Three myths that waste developer time:
Myth 1: "You can just point an importer at the database." Plugin tables, serialized PHP, and theme-specific data don't live in the standard schema. WXR is the only export format that's stable across WordPress versions. Use it.
Myth 2: "Redirects are optional if your URLs match."
Even if your old and new URLs are byte-identical, you should still have an explicit redirect for /?p=ID patterns and trailing-slash variants. Google indexes both versions of every URL; missing redirects fragment your link equity.
Myth 3: "Just relaunch and submit a new sitemap." This is the fastest way to nuke your rankings. Search engines treat unannounced URL changes as 404s. The window where they decide whether your migration is "intentional change" or "the site died" is roughly 14 days — after which recovery takes 3–6 months. Plan the cutover carefully.
When Migration Is the Wrong Move
We'll be the first to tell you: not every WordPress site should leave WordPress. If three of these are true, stay:
- The site is fewer than 20 pages and rarely updated
- You have zero technical resources and no budget for any
- The site relies on 5+ plugins with no equivalent on a modern stack
- WooCommerce handles >50% of revenue and works fine
- The current developer team only knows PHP/WordPress
WordPress is a perfectly defensible choice for those cases. The reason developers leave is volume of pain over time, not a fundamental belief that PHP is wrong. If you don't feel the pain, you don't need the fix. A more honest WordPress vs modern CMS comparison helps make this call.
After You Migrate
Two things you should do in the first month after launch:
- Run a backlink audit. Tools like Ahrefs and Semrush flag any 404s coming from external sites. Each one is a missed redirect — fix them as they appear.
- Watch Search Console weekly. "Pages indexed" should rise toward your old total within 4–6 weeks. If it plateaus low, you have indexation problems (often a robots.txt or canonical issue), not ranking problems.
After three months, the technical migration is done. What you have left is the part that was always going to be work: building the developer-friendly stack you couldn't on WordPress, without a new plugin vulnerability landing in your inbox every week.
FAQ
Will my Google rankings drop after migrating from WordPress?
Short-term, yes — expect 5–15% volatility for 2–4 weeks while Google re-crawls. Long-term, rankings recover and usually improve because the new platform delivers faster page loads (Core Web Vitals correlate with ranking). The variable that decides recovery speed is redirect coverage. Sites with complete 301 redirects recover in 4–8 weeks; sites missing redirects can take 6+ months.
How do I migrate a WordPress site without losing comments?
UnfoldCMS's wordpress:import brings comments across by default — preserving author, email, date, content, and approval status. The threading is preserved (parent_id is remapped). Disqus or third-party comment plugins require their own export from the third-party tool; the WXR file doesn't contain that data.
Can I migrate from WordPress to UnfoldCMS gradually?
Yes — and this is often the smart play for sites over 1,000 pages. Run UnfoldCMS at a subdomain (new.example.com), migrate sections incrementally, and reverse-proxy unmigrated paths back to WordPress at the edge. When the last section moves, swap DNS. This eliminates the "big bang" risk entirely. The trade-off is more complex routing during the transition.
Do I need to rebuild my theme from scratch?
For most sites, yes. WordPress themes use template-tags, the_content(), and theme-specific functions that don't exist on a Laravel stack. The good news: the modern equivalent is usually a tenth the code. A WordPress theme with 30 PHP files and 2,000 lines of code typically becomes 5–10 Blade templates and 400 lines on UnfoldCMS. The Aurora template that ships with UnfoldCMS is a reasonable starting point if you don't have strong design opinions.
What about WordPress users and roles?
The importer brings users across with role mapping: WordPress administrator → UnfoldCMS admin, editor → editor, author → author, subscriber → subscriber. Passwords don't transfer (different hash algorithm) — every user gets a password reset email after the import. Membership plugin data (BuddyPress, MemberPress, restricted content) doesn't migrate; those depend on their own plugin schemas.
Is there a managed migration service?
For complex sites we offer a paid migration service ($149 standard / $499 with theme rebuild) — full URL audit, custom redirect rules, ACF mapping, and post-launch monitoring. Details on the pricing page. For most developers handling their own WordPress sites, the open-source toolkit covers it.
Methodology and Sources
This guide is based on roughly two dozen WordPress → UnfoldCMS migrations we've either run or supported in 2025–2026, ranging from 50-page personal blogs to 12,000-page institutional sites. Specific data points:
- WordPress export behavior tested against versions 6.0 through 6.7
- WXR import compatibility validated using the official WP-CLI export format
- 40–70% organic traffic loss figure: Moz, "Site migration impact analysis", 2024
- 14-day Google evaluation window: based on Search Console crawl-stats observed across the migrations referenced above
- Core Web Vitals correlation with ranking: Google Search Central, Page Experience Update, 2021–present
- Plugin metadata extraction patterns: Yoast SEO database schema, RankMath docs
We've published this guide as the playbook we hand to teams making the move. If something in your migration breaks in a way this guide doesn't cover, open an issue — we'd rather fix the doc than have you guess.