Chapter 2
What are you actually migrating?
Not the pages: the URLs, forms, scripts, integrations, redirects, linked files, and legal pages the old site accumulated. One spreadsheet; it's the contract.
# What are you actually migrating?
Not the pages. You're migrating a set of obligations the old site accumulated while nobody was looking: URLs that rank and are linked to, forms and wherever they send what people type, scripts and pixels and what they leak, integrations nobody documented, redirects already in place from the last migration, files linked from other sites, and legal pages with dates on them. Written down in one spreadsheet, that inventory is the contract for the whole project. It defines what done means, it prices the work, and it's what you check against on launch day.
The pages are the visible part. Most of what goes wrong in a migration was never a page.
Why isn't it the pages?
Because pages are the one thing everybody already knows to move, and because on the new site most of them stop being pages.
The failures I've seen or cleaned up after were not missing pages. They were a contact form that had been quietly posting to a marketing vendor for four years, and the vendor had never signed a business associate agreement. A remarketing pixel on condition pages that nobody on the marketing team knew was there. A patient intake PDF that three referring practices linked to from their own sites, gone the day the new site launched. A hundred and forty query-string URLs from an old newsletter tool that still got clicks. An appointment widget whose account was registered to an employee who left in 2021. None of those show up when you count pages.
The other reason is chapter 3: on a structured site, sixty to eighty percent of the old pages become records (providers, locations, services, FAQs) rather than pages. So "migrate 300 pages" is the wrong unit from the start. The right unit is the row in the inventory, and each row is a URL, a form, a script, an integration, or a file, with a fate.
What goes in the inventory?
Seven lists, joined where they share a URL. The first one is the biggest; the other six are where the surprises live.
- URLs. A full crawl of the old site joined with twelve months of Search Console's Pages and Links reports, so every URL carries its traffic, its impressions, and its inbound links. Include the ones the CMS doesn't know about: attachment pages, tag archives, paginated feeds, author pages, campaign URLs with query strings. Chapter 4 covers how to build this list and turn it into the redirect map.
- Forms. Every form on the site: the page it's on, where the submission goes (an inbox, a CRM, a vendor, a plugin's database table), the fields, whether any field is free text, which fields could carry health information, what notice or consent is shown, and whether the receiver is under a BAA. Submit each one with test data and follow where it lands. Plugin-stored submissions on the old host are a data set you'll need to export and then delete.
- Tags and scripts. Export the tag manager container if there is one, then open a fresh browser profile with the network panel on and load one page of each template. Record every third-party request: vendor, what loaded it, which pages it fires on, whether it fires before any consent, and what it sends. This list is what chapter 5 is about; the inventory is where you find out how bad it is.
- Integrations. Scheduling, CRM, email marketing, careers, reviews, chat, maps, payments, patient portal links, the analytics account itself. For each: what it does, who owns the account (the organization, a vendor, an ex-employee, unknown), who has admin access, and whether it's documented anywhere.
- Existing redirects. The old site already has redirects, from a previous migration, a plugin, or the host's configuration. Export them. Any URL that already redirects needs its original address mapped straight to the final destination, not chained through the old hop.
- Assets linked from outside. PDFs (patient forms, price transparency files, research posters), images that other sites embed, and anything a partner, a directory, or a hospital system links to. The Links report and a backlink tool find most of these; the crawl finds the rest. Each one is kept at the same path or redirected individually.
- Legal pages. Privacy policy, terms, accessibility statement, notice of privacy practices, and any state-specific privacy disclosures, each with its effective date and whoever last approved it. These get re-reviewed for the new site because the new site collects different things.
An eighth list isn't content, but it belongs in the same spreadsheet: access. Who can log in to the registrar, DNS, the current host, the CMS, the analytics and Search Console properties, the tag manager, and each integration. Every migration I've run has stalled at least once on an account nobody could get into.
Who fills it in?
The crawl, the joins, the network-panel work, and the redirect export are technical and take a developer or an assessor a few days. The columns that need a human who knows the organization take the client weeks, and that is the part to start first.
The client's columns are: who owns this page or form or integration, whether it should survive, and what it should become. Those questions go to the marketing lead, the practice managers, the person who set up the CRM, and sometimes counsel. In a four-month build, this is the work that determines whether month one is productive or spent waiting. Of the three things clients reliably underestimate (content, approvals, and access; chapter 8 is about all three), two of them are exposed by the inventory in week one, which is the point of doing it in week one.
Why is this the contract?
Because every row gets a fate, and a row with a fate is a testable statement about the finished site.
Each URL is kept, merged into another, redirected, or retired. Each form is rebuilt with a named receiver, or removed. Each script is kept behind consent, replaced, or dropped. Each integration is carried over with a named owner, or cut. Each asset stays at its path or gets a redirect. Once the client has marked those columns up, the inventory does four jobs at once. It defines scope: anything not in it is a change, and a change has a cost. It prices the build: the counts by proposed type are the content model, and the number of forms and integrations is most of the integration work. It sets the launch gate: the parity check in the starter reads the old sitemap and refuses to pass until every address resolves on the new host, and the redirect import takes its rows straight from the map. And it's the record you compare against in the month after launch, when a 404 shows up in Search Console and you need to know whether it was ever supposed to exist.
A number from experience, offered as a range: a site the client describes as "about eighty pages" has between three and nine hundred URLs once the crawl and the Links report are joined. The gap between those two numbers is the migration.
What does the spreadsheet look like?
One workbook, one tab per list, and columns that force a decision. The content inventory is the core tab, one row per URL: template, content type, title, status code, indexable or not, word count, organic and total sessions for the last ninety days, conversions, external inbound links, last modified, owner on the client side, action (keep, merge, redirect, kill), target URL, proposed document type on the new site, priority, notes. The redirect map is generated from the rows where the action is merge or redirect, with columns for testing before launch, a day after, and two weeks after.
The forms tab, the tags tab, and the integrations tab carry the columns described above, each ending in a finding and a recommended action. The access tab is a checklist: item, what it's needed for, requested, granted, by whom, at what level. When I run an assessment, this workbook is half the deliverable and the client keeps it; it's the same workbook the build then runs on.
Keep the free text short. The spreadsheet carries facts and decisions; the reasoning goes in a document.
What can you skip?
Judging content quality, designing the content model, and anything with no traffic, no links, and no owner.
Content quality is a separate pass, after the inventory, and usually after launch; migration day is not the day to rewrite service pages. The content model is chapter 3, and it's designed from the inventory's counts, not alongside them; for now, assign each row a proposed type and move on. And a URL with zero sessions, zero impressions, and zero inbound links in twelve months gets marked kill and a 410, which is honest, rather than a redirect to the homepage, which is a soft 404 that costs crawl budget. Don't spend an hour deciding the fate of a 2019 holiday-hours post.
What you can't skip is the join. A crawl without Search Console data can't tell you which pages matter; Search Console without a crawl misses everything that isn't indexed. The inventory is the intersection, and doing only half of it is how a migration loses the pages nobody remembered were valuable.
Checklist
URLs and files
Everything that isn't a page
People and access - [ ] A client-side owner named for every row that needs a decision - [ ] Access checklist filled at kickoff: registrar, DNS, host, CMS, analytics, Search Console, tag manager, each integration - [ ] Accounts registered to former employees identified and transferred before the build depends on them Companion code in the Healthcare Sanity Starter: docs/MIGRATION.md ("Four weeks out: inventory" is this chapter's checklist in the repo), scripts/check-url-parity.ts (the launch gate that reads from the URL list).