
Every headless migration looks clean on the roadmap. The old monolithic CMS gets replaced by a modern, decoupled stack. The frontend becomes fast. The content team gets a better editorial experience. The SEO improves. The engineering team stops working around a theme. It’s a compelling narrative, and it’s true — eventually. What the roadmap doesn’t show is the five weeks after kickoff where nobody can agree on the content model, the three sprint retros dominated by the preview mode that still doesn’t work, and the six-month post-launch period where organic traffic is doing something nobody wants to explain to the client. Migrating to headless architecture is a legitimate investment with a real return. It just takes longer, costs more, and requires more upfront work than the initial proposal suggested, every single time, without exception.
Here is what the process actually looks like, what goes wrong and when, and how to protect the things that matter — rankings, content, editorial workflow — while the technical infrastructure underneath them gets rebuilt.

Phase 1: Audit Before Anything Else Gets Touched
The single most consistent mistake in headless migrations is starting the build before finishing the audit. Teams eager to show progress stand up the new frontend while the legacy CMS is still live, content model decisions get made without a full inventory of what’s actually on the site, and the redirect mapping gets treated as a post-launch task. All three of these compress timelines on paper and expand them catastrophically in practice.
Here is how each part of the audit actually works:
Content inventory
Start with a crawl. Screaming Frog or Ahrefs Site Audit — crawl the live site and export every indexed URL. Ahrefs is particularly useful here because it combines crawl data with backlink counts and organic traffic estimates in a single export, which saves the manual merge step. Then pull the last three months of performance data from Google Search Console (Performance → Pages, export to CSV). Merge these sources into one Google Sheet on the URL column.
The sheet needs these columns as a minimum:
- URL — full absolute URL
- Page type — blog post, service page, landing page, case study, legal page, etc.
- GSC clicks (3 months) — from the Search Console export
- GSC impressions (3 months) — from the Search Console export
- Avg position — from the Search Console export
- Backlink count — from Ahrefs, even 1 backlink matters
- Internal links in — how many internal links point to this page, from the Screaming Frog inlinks report
- Has structured data — Y/N, type noted (Article, FAQ, BreadcrumbList, etc.)
- Traffic tier — classify as High (top 20% of traffic), Medium, Low, or Zero
- Migration action — this is the decision column. Options: Keep (same URL), New URL (needs redirect), Merge into X (consolidate, needs redirect), Deprecate (no redirect needed — verify it truly has no backlinks or rankings before marking this)
Every URL in the “Deprecate” column needs a second pass before it’s confirmed. A page with zero GSC clicks can still have backlinks or position data you haven’t spotted. The rule: no page gets deprecated without checking the Ahrefs backlink count is zero and the GSC impressions are genuinely zero across the full window, not just three months.
URL and redirect mapping
This is a second sheet, or a second tab, that focuses on the URLs that are changing. Every URL where the “Migration action” column says “New URL” or “Merge into X” needs its own row in the redirect map. Columns:
- Old URL — exactly as it appears on the current live site
- New URL — the exact canonical URL on the new site it should redirect to, or the consolidation target
- Redirect type — 301 for everything except soft launches where you might need temporary 302s
- Reason — one line: “slug cleanup”, “template merge”, “URL restructure”, “deprecated category”
- Backlinks — count from Ahrefs, so you can see which redirects are highest priority if something breaks
- Redirect status — Not started / Implemented / Verified. This column gets updated on launch day as you test each redirect against the live site with a tool or a simple curl loop
According to Google’s documentation on site migrations, incomplete redirect implementation is the primary documented cause of traffic loss during platform changes. The redirect map is not done when someone fills in the spreadsheet. It’s done when every row in the “Redirect status” column says Verified.
Internal link audit
In Screaming Frog, crawl the site, then go to Reports → Internal Links. Export this. The export gives you source URL, destination URL, anchor text, and whether the link is in the navigation, body, or footer. This is your internal link map.
The critical column to add during the migration: New destination URL. Every internal link that pointed to an old URL needs to be updated to point directly to the new canonical URL — not via a 301 redirect. Redirected internal links pass less equity than direct ones, and any internal link where the redirect wasn’t set up becomes a 404 without warning. The full internal linking strategy sits in Internal Linking Strategies That Actually Work — the migration is the moment to rebuild the internal link architecture correctly, not just replicate whatever was there before.
Structured data inventory
In Screaming Frog, go to the Structured Data tab. Export every page with schema markup and note the types present. The output should list each URL alongside the schema types it generates. This becomes the build brief for the developer implementing schema in the new frontend. For every type in the list, the question is: will the new frontend generate this automatically from the content model, or does it need to be hardcoded per template? BreadcrumbList and Article can typically be generated from page metadata. FAQ schema needs to come from a structured content field in the CMS. Neither appears automatically without someone building the generation logic.
Performance baseline
Run PageSpeed Insights on the top 20 pages by traffic — not all of them, just the ones that matter. Record LCP, INP, CLS, and TTFB for each. Keep this in a separate tab with a date stamp. After the new site launches, run the same pages through the same tool and compare. This prevents the post-launch argument about whether the migration improved performance, because you have numbers from before it happened.
If you want to be thorough, run both mobile and desktop. Mobile field data is what Google’s systems actually evaluate for Core Web Vitals ranking signals, and it’s frequently worse than desktop numbers suggest.
Phase 2: Content Modeling — The Real Migration
The technical build is not the hardest part of a headless migration. The content model is. A traditional CMS like WordPress stores content as posts and pages with custom fields attached to a theme’s assumptions about how content will be displayed. A headless CMS stores content as structured data that has to be useful regardless of how it’s eventually rendered. These are fundamentally different approaches, and converting years of content built around one set of assumptions into data structures that work for the other is where headless migrations actually spend most of their time.
The practical consequence: every content type on the old site needs to be redesigned, not just moved. A blog post with a custom featured image field, an author bio section, and a related posts widget isn’t a simple export-and-import operation. The content model for the new headless CMS needs to define what fields exist, which are required, how content references other content, and what happens to the edge cases — the old posts that have three authors, or the pages that use a layout that doesn’t exist in the new system. These decisions take time, they require editorial input, and they have downstream consequences for the frontend build. The content operating system that your editorial team runs after the migration is only as good as the content model that was designed before it. Making those decisions correctly before the build starts saves weeks. Making them during the build or after launch costs months.
The Slug Problem: Why You Never Change URLs in Bulk During a Migration
This is the mistake that produces the most dramatic Search Console incidents and gets blamed on the platform instead of the planning. Teams running a WordPress headless migration, a Drupal migration, or a Shopify move to headless often use the opportunity to clean up years of messy URL structure — removing dates from blog slugs, standardising categories, fixing inconsistent naming conventions. The intent is reasonable. The execution, when done all at once at the moment of migration, is catastrophic.
Here is what happens: Google has indexed hundreds of your old URLs. You change them all in one deployment. Even with 301 redirects in place, Googlebot arrives at the new site and finds that every URL it knew about now redirects somewhere else. The redirect signals take weeks to process. During that window, your Search Console coverage report fills with “Page with redirect” warnings, your crawl budget gets consumed re-evaluating redirect chains, and your click data drops to near zero because the indexed URLs are no longer the canonical ones.
Without 301 redirects — which is more common than it should be — the result is immediate: hundreds or thousands of 404 errors on URLs that Google has indexed, that have accumulated backlinks, and that users have bookmarked. The “Indexed, not submitted in sitemap” count spikes. Rankings disappear. The team spends weeks submitting removal requests and waiting for re-indexation of pages that were fine before the migration started.
The rule: never change slugs in bulk at the same time as a platform migration. If URL cleanup is genuinely necessary, do it as a separate project — either before the migration with a full redirect plan in place, or after the new site has stabilised and the migration-related Search Console noise has cleared. The SEO indexing system OJC Labs sets up for clients includes pre-migration URL mapping and post-migration crawl monitoring specifically to prevent this scenario from developing silently after launch.
The redirect.js trap: what AI coding agents get wrong
When teams raise the redirect problem with an AI coding agent during a headless build, the suggested solution is frequently some variation of a JavaScript redirect handler — a redirect.js or middleware.mjs file that intercepts incoming requests and sends users to the new URL. This looks like it solves the problem in the browser. It doesn’t solve it for Google.
A genuine 301 redirect is an HTTP response header returned by the server before any content loads. Googlebot reads the header, follows the redirect, and transfers link equity from the old URL to the new one. This is what search engines expect and what they process correctly.
A JavaScript redirect — regardless of whether it’s in a middleware file, a route handler, or a client-side script — requires JavaScript to execute before the redirect fires. On a fast connection with Googlebot’s rendering engine, this might occasionally work. In practice, Googlebot crawls billions of pages and doesn’t always execute JavaScript before moving on. Pages behind JS redirects frequently appear in Search Console as having their old URL as the canonical — meaning the redirect wasn’t processed as a 301 at all. The result: a redirect that works perfectly for users and does nothing useful for SEO.
The correct implementations in a Next.js stack, in order of preference:
- Vercel
vercel.jsonredirects — processed at the edge before any Next.js code runs, returns a true HTTP 301, fastest and most reliable - Next.js
next.config.jsredirects array — server-side, returns proper HTTP 301 headers, fine for a managed list of redirects - Not:
middleware.jsusingNextResponse.redirect()— runs in the edge runtime but behaves differently from a static 301 in how it’s processed by crawlers and cached by CDNs - Not: any client-side redirect —
useRouter().replace(),window.location, or a customredirect.mjsmodule — these are not HTTP 301s, full stop
If the AI agent suggests the middleware or client-side approach, the correct response is to ask it to implement the redirects in next.config.js or vercel.json instead. For large redirect maps (hundreds of URLs), the next.config.js array gets unwieldy — use vercel.json or configure redirects at the infrastructure level directly.
Phase 3: Building in the Right Sequence
The correct sequence for a headless migration build: content model first, then CMS configuration, then frontend. Most teams invert this because frontend work feels like visible progress and content modeling feels like meetings. The inversion means the frontend gets built against assumed content structures that then get revised, requiring frontend rework. This is where headless migrations earn their reputation for scope creep.
Two things that have to be built explicitly and are routinely underscoped:
Preview mode. Headless frontends don’t come with preview functionality out of the box the way a traditional WordPress or Drupal CMS does. A Next.js frontend needs a draft mode and preview route specifically built for it. Without this, editors have no way to see content before it publishes. On a traditional WordPress site this takes one click. On a headless site without a built preview implementation, it takes a developer and a sprint. This is not optional — it’s a launch blocker that gets treated as a post-launch nice-to-have until editors start complaining on day one.
Structured data generation. Every schema markup type from the old site needs to be rebuilt programmatically in the new frontend. This includes BreadcrumbList, Article, Organization, and any content-specific schema. These don’t appear automatically and they don’t transfer from the old CMS. They’re developer work that belongs in the sprint, not in the backlog. As covered in detail in How to Structure a Website for SEO at Scale, structured data is a ranking signal that compounds over time — every week it’s missing on the new site is a week of signal lost.
Before your coding agent writes a single file, paste this into it as the opening instruction. It won’t prevent every mistake — but it eliminates the most common ones that are expensive to undo after the build has started.
Before you start building anything on this headless frontend, apply these constraints for the entire project without exception:
REDIRECTS
- Never implement redirects using JavaScript, middleware.js, or any client-side script
- All redirects must be HTTP 301s returned at the server or infrastructure level
- Use next.config.js redirects array for small lists (under 50 URLs)
- Use vercel.json redirects for large lists or when the next.config.js array becomes unwieldy
- If I ask for a redirect and you suggest middleware or a custom handler, stop and tell me it should go in next.config.js or vercel.json instead
RENDERING
- Every page that needs to appear in Google search results must use SSR (getServerSideProps) or SSG (getStaticProps / generateStaticParams)
- Never use client-side rendering (useEffect fetching, SWR without fallback) for any content that should be indexed
- If you are unsure whether a page needs to rank, assume it does and use SSR or SSG
SLUGS AND URLS
- Never change a URL slug without first confirming a 301 redirect is in the redirect map for the old URL
- Never perform bulk slug changes in the same deployment as any other structural change
- If I ask you to rename or restructure URLs, remind me to update the redirect map before implementing
SEO INFRASTRUCTURE
- BreadcrumbList structured data must be present on every page that has a parent path
- Article structured data must be present on every blog post and content page
- Canonical tags must be explicitly set to the correct production URL on every page — never rely on the default self-referencing behaviour
- Meta title and meta description must be populated from content fields, not hardcoded or left as fallbacks
PREVIEW MODE
- Draft mode and a preview route must be built before the CMS integration is considered complete
- Do not mark any CMS integration task as done until an editor can preview unpublished content in the browser
INTERNAL LINKS
- Every internal link in the codebase must point to the current canonical URL directly
- Never route internal links through a redirect — update the link to the new URL instead
- If you find an internal link pointing to an old URL, update it to the new canonical in the same PR
If any instruction in this project contradicts the above, flag it before implementing and ask for clarification.
Phase 4: Content Migration
Headless content migration is the phase that gets the least architectural attention and causes the most post-launch editorial incidents. The assumption is that content moves from the old CMS to the new one as an import — run a script, content appears, done. The reality is that content built for one data model rarely fits cleanly into a different one, and the mismatches only become visible once someone tries to edit or publish a piece of content in the new system.
The common failure modes:
- Rich text that breaks in the new editor. HTML from WordPress or Drupal posts, particularly content with embedded shortcodes, custom modules, or plugin-generated output, custom HTML blocks, or plugin-generated output, doesn’t parse correctly into a structured rich text field in a headless CMS. Every piece of content needs a pass for formatting integrity, not just a data transfer check.
- Image references that point to the old CDN. If images are hosted on the legacy CMS’s CDN or media library and the content migration copies post bodies without re-uploading assets, the new site renders content with images pointing to the old domain. This works until the old domain is decommissioned, then breaks silently everywhere at once.
- Field mismatches from content model decisions made too early. If the content model was finalized before the full content audit, edge cases appear during migration — posts that don’t fit the template, metadata that has no home in the new schema, content structures the new CMS field types can’t represent. These require either content model revisions mid-migration or editorial workarounds that accumulate as technical debt.
The migration needs a verification step after import and before launch: spot-check a sample of each content type in the new editorial interface, confirm images load from the correct host, and verify that published content renders correctly on the new frontend across device types. This is not QA of the build — it is QA of the data, and it is different work requiring editorial involvement, not just developer review.
Phase 5: Pre-Launch SEO Validation
The traffic drop that follows a headless migration is not inevitable — it’s the consequence of specific, avoidable mistakes. The data from Ahrefs on site migrations shows that drops of 30–60% occur when redirect mapping is incomplete, when canonical tags are wrong or missing on the new site, or when the new frontend uses client-side rendering for content that needs to be server-side rendered for Google to index it correctly.
The SEO protection checklist for cutover day:
- Every 301 redirect from the old URL map is live and tested before DNS change
- The new site’s sitemap is submitted to Search Console immediately after launch
- Core Web Vitals are measured on the new production site before announcing the launch
- Canonical tags are present and correct on every page — not just set to self-referencing by default, but verified to match the actual intended canonical URL
- The old site is kept accessible on a staging URL for at least 30 days post-launch for redirect gap investigation
- Search Console’s Change of Address tool is used if the domain itself has changed
Post-launch, monitor Search Console daily for the first four weeks. Crawl errors that appear in the first week need to be resolved that week — errors that persist compound into indexation problems.
Phase 6: Launch
Launch day is not a deployment — it’s a sequence of steps that happen in a specific order. Getting the order wrong introduces gaps between when the new site goes live and when Google knows where to find it.
- Verify all redirects are live on the new server before changing DNS. Run a curl loop or redirect checker against the full redirect map from a non-cached connection.
- Change DNS and set TTL to a low value (60–300 seconds) beforehand so changes propagate in minutes, not hours.
- Submit the new sitemap in Search Console within the first hour of DNS propagating. Don’t wait until the next day.
- Use URL Inspection in Search Console on your five to ten most important pages to trigger immediate recrawl requests.
- If the domain changed, use Search Console Settings → Change of Address to directly signal Google to transfer ranking signals to the new property.
- Keep the old site accessible on a staging URL — not redirecting, but viewable — so you can diagnose redirect gaps by comparing old and new URL structures side by side.
Phase 7: Stabilise
Stabilisation is the 4–8 weeks after launch during which organic performance cannot be meaningfully evaluated and several classes of post-launch problems surface and need fixing. Most migration proposals don’t include it. Most post-launch incidents are caused by skipping it.
What happens during stabilisation that requires active monitoring:
- Googlebot recrawls the new URL structure and processes the redirect map. Pages that were ranking on old URLs will show position volatility during this period regardless of how well the migration was executed. This is expected.
- Redirect gaps surface in Search Console’s Coverage report. URLs missed in the mapping show up as 404s. Fix them weekly — a backlog of missed redirects is an ongoing crawl budget drain that doesn’t self-correct.
- Canonical tag errors appear. Pages where the self-referencing canonical doesn’t match the intended canonical show up as “Duplicate without user-selected canonical.” Fix these before they accumulate.
- Internal links pointing to old URLs via 301s should be updated to point directly to the new canonical URLs. Redirected internal links pass less equity than direct links. Run an Ahrefs crawl at week 4 to find these at scale.
Don’t evaluate SEO impact until stabilisation has fully cleared. Performance data during this window is noisy. The decision about when to move to headless and readiness signals is in When to Move to Headless Architecture; the performance implications of the architecture choice are in Headless CMS vs WordPress for SEO.

The Timeline Reality
Small site (under 50 pages, one content type, no multi-language): 6–10 weeks from audit to launch. Medium site (50–200 pages, 3–5 content types, existing backlink profile to protect): 12–20 weeks. Enterprise site (200+ pages, multiple content types, multi-language, integration dependencies): 6–12 months, and anyone who quotes you less than four months for this scope is not accounting for content modeling, editorial training, and the stabilization period.
The stabilization period — the 4–8 weeks after launch before the new site’s performance can be reliably evaluated — is the most commonly omitted phase in migration proposals. During this period, Googlebot is recrawling the new structure, the algorithm is re-evaluating the content, and any residual redirect or canonical issues are surfacing in Search Console. Evaluating SEO performance before this period ends produces misleading data and incorrect conclusions about the migration’s success.
Internal Link Integrity: The Silent 404 Factory
Every piece of content on the old site contains internal links pointing to other pages by their old URLs. When those pages move to new URLs during the migration, one of two things happens: either you have 301 redirects in place for every old URL, in which case the internal links still technically work but are now passing through a redirect chain that loses some equity — or you don’t have complete redirect coverage, in which case some of those internal links are now pointing to 404 pages.
The redirect scenario is bad but recoverable. The 404 scenario is worse. A 404 that appears because an internal link was missed in the redirect map doesn’t just cost the authority that link was passing — it signals to Googlebot that the site has broken pages, which affects crawl priority across the whole domain. And it surfaces in a way that’s easy to miss: not as a glaring error on the page the user is reading, but as a failed crawl request in the Search Console coverage report weeks after launch.
The correct process: after the migration is live, run a full internal link audit using a crawl tool. Every internal link that still points to an old URL — even via a working 301 — should be updated to point directly to the new canonical URL. Redirected internal links are better than broken ones, but direct links are better than redirected ones. The full strategy for maintaining internal link integrity across a growing site is in Internal Linking Strategies That Actually Work — the migration creates an opportunity to audit and rebuild the entire internal link architecture correctly, not just to replicate whatever existed on the old site.
What Teams Get Wrong, In Order
- Treating the redirect map as a post-launch task. It is a pre-launch requirement. A redirect that goes live two weeks after launch has already let Google index a 404 on the new site — and 404s that get indexed take months to flush from the index.
- Underestimating editorial training. Moving editors from a WordPress block editor, a Drupal content form, or a Sitecore interface to a headless CMS is a workflow change, not a software update. Allow for it. Plan for it. Budget for it. Editors who can’t use the new system are editors who stop publishing, and a stopped content operation is expensive.
- Building the frontend against a content model that isn’t finished. Every time the content model changes after frontend development starts, frontend work gets undone. Finish the content model first. Start the frontend second.
The deeper mistakes
- Not measuring performance before the migration. If you don’t have a Core Web Vitals baseline from the old site, you can’t prove the new one is faster. Given that the business case for a headless migration almost always centers on performance improvement, this is a significant omission.
- Launching without preview mode. Editors will find this on day one. Build it before launch.
- Treating the API as automatically SEO-safe. A Next.js frontend using client-side rendering for page content is not SEO-safe regardless of how fast the page loads. The HTML response must contain the actual page content. Any page that needs to rank requires server-side rendering or static generation. This is the architecture decision covered in Why Website Speed Affects SEO Rankings — rendering strategy determines what Google can read.
This is the migration work OJC Labs manages for clients: technical headless architecture built around the complete process — content modeling, redirect mapping, structured data, performance baseline, and post-launch monitoring — rather than just the build itself.
If the migration is on the roadmap and you need to plan the scope correctly, get in touch.