
Migrating 621 Blog Posts Without Losing the Slugs

A practical account of migrating 621 blog posts across CMS platforms while preserving every slug, redirect chain, and SEO signal — without a single 404 on launch day.
Why Slug Preservation Is the Whole Problem
Most CMS migrations treat content as the hard part. It isn't. Content moves fine. A CSV export, a bit of cleaning, a bulk import — done in an afternoon. What breaks things is the URL graph: every slug, every internal link pointing to a slug, every backlink from an external domain pointing to a slug, and every redirect already in place from the last migration someone did three years ago.
Lose a slug and you lose the PageRank that slug accumulated. Google's John Mueller has confirmed that 301 redirects pass "most" link equity, but "most" is not all, and the compounding loss across 621 posts is measurable. In our experience, sites that migrate cleanly and preserve slugs see ranking fluctuations settle within four to six weeks. Sites that rely on bulk redirects often see a 10–20% organic traffic drop that takes months to recover, if it recovers at all.
The goal is zero new redirects introduced by the migration itself.
How Do You Even Audit 621 Slugs Before You Touch Anything?
Before writing a single migration script, you need a complete picture of what exists. That means three separate inventories.
The Slug Inventory
Export every published post from the source CMS with its full URL path, not just the slug. A slug like ai-in-fintech is ambiguous if your site has both /blog/ai-in-fintech and /resources/ai-in-fintech. The full path is the canonical identifier.
We use a simple Python script against the CMS database or REST API (WordPress's /wp-json/wp/v2/posts?per_page=100 with pagination, for instance) and write to a flat CSV: post_id, full_path, canonical_url, redirect_target_if_any, http_status.
Run Screaming Frog or wget --spider against the live site at the same time. The database export and the crawler output will not match. They never do. You will find posts that are in the database but return 404, posts that redirect to somewhere unexpected, and posts with duplicate slugs serving different content depending on query parameters. Reconcile these before you do anything else.
The Internal Link Graph
You need to know which posts link to which. This matters because after migration, internal links that use absolute URLs will still point at the old domain or old path structure. Extract all href values from post body content, filter to your own domain, and build an adjacency list. 621 posts might have three to five thousand internal links between them. Each one is a potential broken link or redirect chain waiting to form.
The Existing Redirect Table
Your source CMS almost certainly has a redirect plugin or .htaccess rules from previous URL changes. Export all of them. Map the full chain: if /old-post redirects to /blog/old-post which redirects to /blog/new-post, you have a redirect chain of depth two. Google will follow chains, but they add crawl overhead and some link equity does bleed at each hop. Flatten any chains before the new platform goes live.
/// Not sure where to start?
Get the architecture before you commit
Tell us what you're building and we'll map the technical approach, stack, and rough timeline. No cost, no obligation, no sales call required.
What Does the Actual Migration Script Look Like?
The script structure depends on source and destination, but the logic is the same regardless of platform pair.
Phase 1: Slug normalization. Your destination CMS may have different slug rules. Ghost, for example, lowercases everything and strips special characters differently from WordPress. Run your slug list through the destination's normalization function before import, and flag any collisions. Two posts that had distinct slugs in the source may produce identical slugs in the destination.
Phase 2: Dry-run import. Import to a staging environment first. Do not skip this. After import, run the same Screaming Frog crawl against staging and diff the output against your source inventory. Every slug in the source must appear in the destination with an HTTP 200. Everything else is a problem to fix before go-live.
Phase 3: Internal link rewriting. Once you know the final slug for every post in the destination, do a find-and-replace pass across all post bodies. If you are moving from WordPress to a headless CMS like Contentful or Sanity, this is a good time to convert absolute internal links to relative paths or content references. If you stay on the same domain, relative links were already fine.
Phase 4: Redirect generation for anything that changed. If even one slug had to change (collision, normalization difference, deliberate cleanup), generate a 301 redirect entry for it. Do not rely on the CMS to do this automatically. Write the redirect list explicitly, review it, and load it into your CDN or reverse proxy (Cloudflare Rules, Nginx rewrite directives, or similar) rather than a plugin. CDN-level redirects respond faster and do not consume PHP or Node cycles.
What About Posts With the Same Slug in Different Sections?
This is the edge case that bites you at 2 AM on launch day.
If your source CMS used taxonomies or post types to disambiguate identical slugs, /news/ai-update and /blog/ai-update can coexist. If your destination CMS uses a flat slug namespace, they cannot. You must rename one. The rule we follow: rename the one with fewer inbound links. Check ahrefs or moz link data, or use the Google Search Console performance report filtered by page to see which URL gets more clicks from search. Rename the lower-traffic one and redirect it.
| Scenario | Action |
|---|---|
| Unique slugs, same path structure | Direct import, no redirect |
| Unique slugs, path structure changes | Import + 301 at CDN level |
| Duplicate slugs, different sections | Rename lower-traffic slug, add 301 |
| Slug normalisation changes on destination | Auto-rename, add 301, flag for review |
What to Check on Go-Live Day
Do not treat launch as the finish line. Treat it as the start of a 72-hour monitoring window.
Submit the XML sitemap to Google Search Console immediately after DNS propagation. Watch the Coverage report for any URLs flagged as "Not found (404)" or "Redirect error." Cross-reference with your internal analytics for any spike in 404 events. If your site uses next-sitemap, gatsby-plugin-sitemap, or similar build-time sitemap generation, confirm that the generated sitemap includes all 621 posts and matches your canonical URL list exactly.
Set up a synthetic monitor (Checkly, UptimeRobot, or a simple cron calling curl -I) against a sample of 20 to 30 URLs covering the highest-traffic posts, posts that changed slug, and posts with the most inbound links. Run it every five minutes for the first 48 hours.
Conclusion
The migration itself is mechanical. The work is in the audit. Get the full URL inventory, the internal link graph, and the existing redirect table before you write any import code. Normalise slugs against your destination's rules in staging, not production. Generate explicit 301 entries for every change and load them at the CDN layer. Then monitor for 72 hours.
If you are mid-migration and already seeing 404s on slug audit, start with the highest-traffic posts in Search Console and work backwards. Fixing the top 50 URLs by organic traffic typically recovers 80% of the impact.
FAQ
Do 301 redirects fully preserve SEO value after a migration? Google confirms 301s pass link equity, but not 100% of it. The exact amount is not published, but multi-hop redirect chains lose more than single hops. The goal is to eliminate redirects for posts that do not need them and keep redirect chains to a maximum depth of one.
How long does it take for Google to re-crawl migrated URLs? For a site with existing crawl authority, expect Googlebot to re-crawl most high-traffic pages within one to two weeks of sitemap submission. Full re-indexing of all 600-plus URLs can take four to eight weeks. Search Console's URL Inspection tool shows the last crawl date per URL if you need to check specific posts.
Should we migrate all posts at once or in batches? All at once, with a clean cutover, is easier to reason about and monitor. Batched migrations create a split state where some content is on the old platform and some on the new, which complicates redirect logic and crawl behaviour. The only reason to batch is if the destination CMS has import rate limits you cannot work around.
What if our CMS does not let us set slugs manually on import? Most headless CMS platforms expose a slug field in their content API. If yours does not, use the API to patch slugs immediately after import before any public DNS change. Contentful, Sanity, and Strapi all support this. For platforms that enforce auto-generated slugs from titles, you may need to temporarily alter post titles during import, then restore them after slugs are set.
Is it worth cleaning up old slugs during the migration? Only if you have a redirect strategy for every change. A migration is a reasonable time to fix slugs that were generated from draft titles and never updated, but each change requires a 301. If you have 50 slugs to clean up, that is 50 redirects to generate, test, and monitor. Do not clean up slugs speculatively — only fix ones that have a clear reason and an owner who understands the redirect consequence.
Have a project in mind? Contact Sodio Technologies to discuss your requirements and explore the right technology solution for your business.
/// Work with us
Talk to the engineers who'd build it
You'll get a technical scope, timeline and cost estimate from the people doing the work, not an account manager. In-house team, no subcontracting, since 2016.
