Pre-Migration Auditing & Risk Assessment

Pre-migration audit workstreams feeding the go/no-go gate Four parallel audit workstreams — crawl baseline, traffic value, risk scoring, stakeholder alignment — converge on a single go/no-go decision gate before cutover. Audit Workstreams Into Go/No-Go Crawl Baseline Traffic Value Risk Scoring Stakeholders URLs, status, canon Sessions, revenue Weighted matrix Sign-off gates Go / No-Go Decision gate Cutover DNS flip No-Go: remediate findings, re-run affected workstreams
Four parallel audit workstreams feed one decision gate; a no-go routes failed checks back into remediation before any DNS change.

Executive Summary

A successful migration is decided before a single record is changed. This playbook standardises the pre-launch diagnostic process so engineering, SEO, QA, and leadership operate from one verifiable picture of the legacy estate. It captures a technical baseline, quantifies organic and revenue value per URL, scores the probability and impact of each failure mode, and locks in the sign-offs that gate the DNS flip. The outcome is a documented go/no-go decision backed by hard thresholds rather than optimism — every claim of readiness traces back to a number you can re-measure after launch. Run this 60–90 days ahead of the planned cutover and treat its checklists as release gates, not advisory notes.

The economics behind that 60–90 day window are worth stating plainly, because it is the number stakeholders push back on hardest. Every defect this phase catches is fixed against a system nobody is using, on a schedule you control, by whoever happens to be free. The same defect discovered after cutover is fixed under traffic, on the incident’s schedule, by whoever is on call — and if it is a ranking defect rather than an availability one, it is not really fixed at all for several weeks while search engines re-crawl. The ratio is not marginal: an afternoon here routinely displaces a fortnight later.

The audit also produces something the migration cannot proceed without, which is a definition of success that predates the work. Without a frozen baseline there is no answer to “is the new site worse?”, only competing impressions — and in the absence of numbers, the loudest impression wins. Every artefact below exists so that after launch you can re-run a measurement and subtract.

Prerequisites

  • Read access to legacy production, staging, and any pre-production environments (origin IPs, not just CDN edge).
  • Enterprise crawler licences with JavaScript rendering: Screaming Frog SEO Spider, Sitebulb, or JetOctopus.
  • Google Analytics 4 admin and Google Search Console owner permissions on the legacy property; BigQuery export enabled if available.
  • 30–90 days of raw server access logs (Nginx/Apache) and CDN logs for the same window.
  • DNS provider control-panel or API access (records, current TTLs, zone export).
  • CDN cache-invalidation and Page/Cache Rule permissions (Cloudflare, Fastly, Akamai).
  • A cross-functional contact directory with named owners for Engineering, SEO, Content, QA, and a single accountable launch lead.
  • A version-controlled repository to store every artefact: crawl exports, redirect maps, the risk matrix, and the go/no-go record.

Two of these are more load-bearing than they look. Origin access rather than edge access matters because a crawl through a CDN measures the cache, not the site — you will capture whatever the edge happened to be holding, miss pages that were never cached, and see status codes rewritten by rules you did not know existed. Point the crawler at the origin with a host header override, and reserve edge crawls for separately verifying what the edge does.

Raw log access matters because it is the only source that knows about URLs nothing links to. A crawler finds what is reachable; logs record what is actually requested, including deep links from emails sent years ago, paths bookmarked by long-standing customers, and endpoints called by integrations nobody documented. The gap between those two sets is consistently where post-migration 404s come from, and it cannot be reconstructed after the legacy logs are rotated away.

Step-by-Step Execution

1. Establish the Crawl Baseline

Capture a complete, unaltered snapshot of the legacy estate as the reference point every later validation is diffed against. Run a full render-enabled crawl plus a log-driven URL list so JavaScript-only links and orphaned-but-trafficked paths are both covered, then archive status codes, canonical tags, and internal link counts. Drive this through Crawl Baseline Generation and store the exports in version control with the crawl date and source environment recorded.

Freeze the baseline explicitly rather than letting it be whatever the last crawl produced. Record the crawl date, the environment, the tool version, the render mode, and the exact URL count in the migration manifest, and commit the export alongside them. The reason for the ceremony is that this artefact will be cited months later in an argument about whether traffic recovered, and a baseline nobody can date or reproduce settles nothing. Re-crawling later to “refresh” it defeats the purpose entirely — the value is that it is a fixed point.

Crawl the origin with JavaScript rendering enabled, and crawl it twice if the site has any client-side routing. Render-disabled crawls miss links that only exist after hydration, which on a modern framework can be most of the internal link graph, and the resulting baseline will look tidy while omitting entire sections. Compare the rendered and unrendered URL counts as a diagnostic in its own right: a large gap tells you how much of the site is invisible to anything that does not execute JavaScript.

2. Quantify Traffic and Revenue Value per URL

Not all URLs deserve equal migration effort; rank them by what they earn. Join the crawl inventory to GA4 sessions, conversions, and assisted revenue, and to Search Console clicks and impressions, so redirect priority follows business value rather than alphabetical order. Use Traffic & Conversion Mapping to tier the inventory and flag the top revenue-driving paths that must never 404 or chain.

Use assisted conversions and not just last-click. A category page that rarely closes a sale but appears in most journeys is high-value in exactly the way a last-click model hides, and treating it as low-priority because it converts poorly is how migrations lose revenue while every dashboard says the important pages were handled. Where the analytics platform supports it, pull a path-based or data-driven attribution view; where it does not, use entrance counts as a cheap proxy for the same idea.

Tier the result rather than ranking it. A list of eighteen thousand URLs sorted by revenue is not actionable, but four tiers are: pages that must have a verified 1:1 redirect and be tested individually, pages that must resolve correctly but can be verified in bulk, pages that may consolidate to a category, and pages that may legitimately be retired with a 410. Tiering also gives the redirect work a natural sequence and gives QA a defensible sampling strategy.

3. Score Migration Risk

Convert known weaknesses into a ranked, owned remediation list. Score each failure mode — redirect-chain depth, canonical drift, template parity gaps, DNS propagation delay, CDN cache poisoning — on likelihood and business impact, then sort by the product. Apply Risk Assessment Frameworks to produce a weighted matrix, and tie each high-score item to a sprint ticket and an owner before the build freezes.

Score likelihood from evidence rather than intuition where you can. Redirect-chain depth, canonical drift, and template parity gaps are all measurable on the current estate — you can count how many chains exist today, how many pages already disagree with their canonical, how many templates differ between environments. A likelihood score derived from a measurement is defensible in a way that one derived from a workshop is not, and it also updates itself as remediation lands.

Attach an owner and a ticket to every item above the line, and be explicit about what happens to items below it. An unowned risk is a risk nobody is working on, and a risk marked “accepted” without a named accepter is a risk that will be re-litigated during the incident it causes. Accepting risk is a legitimate outcome; leaving it ambiguous is not.

4. Align Stakeholders and Set Gates

A migration fails on coordination as often as on code. Publish the timeline, the escalation matrix, and the explicit approval checkpoints so no environment switches without documented sign-off. Formalise this through Stakeholder Communication Plans, schedule the cutover window inside a historical low-traffic period, and confirm every gate owner has acknowledged their veto.

Publish the escalation matrix somewhere people will actually find it during an incident, which is rarely the document it was authored in. A one-page summary pinned in the channel the team already uses beats a comprehensive plan in a shared drive, because the question it answers — who do I call, and who decides — is asked under time pressure by whoever happens to be awake. Include out-of-hours contacts and a named deputy for every gate owner, since a migration scheduled at 02:00 will not be attended by the people who signed off at 15:00.

5. Prepare Redirect and Routing Inputs

Hand the downstream routing work a clean, prioritised input set rather than a raw crawl dump. Export the value-tiered URL inventory as a redirect source map, marking 1:1 targets for high-value paths and category fallbacks for retired ones. Feed this directly into URL Mapping & Redirect Architecture so rule generation starts from validated, deduplicated mappings.

Make each gate a veto rather than an opinion. The failure mode this prevents is the one where sign-off becomes a formality collected by email in the final week, with the people asked to approve having no realistic ability to refuse against an announced launch date. A veto only means something if declining it is survivable, which in practice means the date must be presented as contingent on the gates from the beginning rather than fixed in advance and defended.

Schedule the window from your own traffic data rather than convention. The lowest-risk hour is the trough in your traffic curve, which is frequently not the small hours in the head-office timezone, and it should also avoid the hours immediately preceding a period when nobody is available. A cutover that goes wrong at the start of a long weekend has all the risk of the migration and none of the staffing.

6. Run Pre-Launch Validation and Lock the Gate

Verify readiness against the baseline and record a single go/no-go. Run staging diffs for status codes, canonical inheritance, robots and hreflang directives, and analytics continuity; any threshold breach is a no-go that routes back to the relevant workstream. Only when all gate owners sign off does the cutover proceed.

Those six steps are deliberately sequenced so each one’s output is the next one’s input, and the whole chain terminates in a single recorded decision. Seeing the dependencies laid out makes clear why running them in parallel to save time tends not to.

How the audit workstreams feed the go/no-go gate The crawl baseline feeds both traffic and revenue valuation and risk scoring; those two produce the tiered redirect input and the remediation list, which together with stakeholder sign-off converge on a single recorded go or no-go decision. Everything downstream is built on the baseline Crawl baseline crawl + logs frozen, dated Value per URL GA4 + Search Console Risk score likelihood × impact Tiered redirect map 1:1 for the top tier Owned remediation ticket + owner each GO / NO-GO one record named sign-offs Any unmet threshold is a no-go that routes back to its own workstream — the gate is a junction, not a rubber stamp.
Value ranking and risk scoring both consume the same frozen baseline, which is why a baseline captured late or through a CDN quietly corrupts everything to its right.

Deduplicate before handing anything downstream, and be explicit about how. A raw crawl export contains the same logical page many times over — with and without a trailing slash, with tracking parameters, with mixed case, under both http and https, and via any alternate hostname the site answers on. Feeding that directly into rule generation produces a redirect map several times larger than it needs to be, with internal contradictions where two variants of one URL map to different destinations. Normalise first, record the normalisation rules you applied, and keep the pre-normalisation export so a surprising mapping can be traced back.

Mark the retirements explicitly rather than letting them fall through as an absence. A URL that should no longer exist deserves a deliberate 410 or a redirect to the nearest meaningful ancestor, decided by someone, rather than a 404 produced by nobody having considered it. The difference matters both to users and to crawl budget, and an explicit list of intentional retirements is also what stops the post-launch 404 report from being an undifferentiated pile in which the real problems are invisible.

Technical Configs

YAML — migration readiness manifest (version-controlled, single source of truth):

# migration.yml — committed to the migration repo and reviewed at the gate
baseline:
  crawl_tool: screaming-frog            # render mode: Ajax/Chrome required
  url_count_legacy: 18432               # frozen baseline count
  drift_tolerance_pct: 5                # no-go if staging deviates beyond this
dns:
  current_ttl_seconds: 3600
  target_ttl_seconds: 300               # lowered 48h pre-cutover
  ttl_reduction_window: 48h
gates:
  staging_qa_signoff: required          # SEO + Eng + Product
  rollback_snapshot_verified: required

Bash — join crawl export to server logs to find orphaned high-traffic URLs:

# Find URLs that appear in 30-day access logs but NOT in the crawl export
# crawl_urls.txt and log_urls.txt are sorted unique path lists
comm -13 <(sort -u crawl_urls.txt) \
         <(awk '{print $7}' access.log | sort -u) \
  > orphaned_urls.txt   # these need explicit redirect decisions
wc -l orphaned_urls.txt

Bash — capture the authoritative TTL before lowering it (rollback evidence):

# dig records the pre-change SOA/TTL so rollback can restore the exact value
dig production-domain.com SOA +noall +answer        # zone serial + minimum TTL
dig production-domain.com A +noall +answer +ttlid   # current record TTLs

JavaScript — Cloudflare Worker to noindex staging without robots.txt drift:

// Block staging indexation at the edge; survives DB syncs that overwrite robots.txt
export default {
  async fetch(request, env) {
    const res = await fetch(request);          // proxy origin first
    const out = new Response(res.body, res);
    out.headers.set('X-Robots-Tag', 'noindex, nofollow'); // hard exclusion
    return out;
  }
}

The comm fragment above deserves a moment because it produces the single most valuable artefact of the whole audit. The set of URLs that appear in logs but not in the crawl is, almost by definition, the set nobody has thought about — and every one of them is currently receiving real traffic that will 404 after the migration unless somebody makes an explicit decision about it.

Why crawl and log inventories must both be captured Two overlapping URL sets: pages found by the crawler, pages recorded in server logs, and the intersection. The crawl-only region is unlinked or retired pages, while the log-only region contains trafficked orphans that no crawl will ever discover. A crawler finds what is linked; logs find what is used Crawl only linked but unvisited — retire or consolidate Both the pages you already know about Logs only trafficked orphans — deep links, old emails, undocumented integrations The right-hand crescent is where post-migration 404s come from, and it disappears the moment the legacy logs rotate away.
Capture both inventories, diff them, and force an explicit redirect decision on every URL in the log-only region before the build freezes.

Validation & Rollback

Enforce the gate against measured numbers, not impressions. Every item below maps to a re-runnable check.

Pre-Launch Validation Checklist:

Common Pitfalls:

  • Skipping JavaScript rendering, so client-side links never enter the baseline
  • Prioritising redirects alphabetically instead of by measured revenue value
  • Letting a database sync overwrite robots.txt and de-noindex staging
  • Defining rollback thresholds after launch instead of at the risk-scoring stage
  • Treating CDN edge responses as origin truth during the baseline crawl

Rollback Protocol:

  1. Trigger if organic sessions drop >15% versus baseline sustained over 24h, OR 5xx error rate exceeds 5% on core templates, OR a critical conversion path returns non-200.
  2. Restore the pre-migration DNS zone from the captured export and confirm authoritative responses.
  3. Force CDN cache invalidation across all edge nodes; verify HTTP 200 on legacy paths.
  4. Restore the database snapshot if content or routing state diverged.
  5. Record the failure mode, update the risk matrix, and re-run the affected workstream before re-attempting.

The staging noindex Worker above solves a specific and recurring problem worth calling out. Staging environments are usually excluded from indexing via robots.txt, which lives in the repository or the database — both of which get overwritten by exactly the sync operations a migration performs. The result is a staging site that silently becomes indexable at the worst possible moment, competing with production for the same content. Enforcing the exclusion as an edge header puts it outside the reach of any content sync, and it fails safe: if the Worker stops running, staging is unreachable rather than indexable.

One closing note on the artefacts. Everything this phase produces — the frozen crawl, the tiered inventory, the risk matrix, the gate record — should live in the same version-controlled repository as the migration configuration, not in a documents folder or a wiki. The reason is traceability: when a redirect rule is questioned six months later, being able to follow it back through the mapping to the tier to the crawl row that justified it is the difference between an answer and a guess. It also means the audit is diffable, so a second migration can start from the first one’s artefacts rather than from a blank page.

FAQ

How far in advance should pre-migration auditing begin? Initiate 60–90 days before the planned cutover. That window covers baseline capture, value mapping, risk scoring, cross-functional alignment, and at least one full QA cycle without compressing critical-path dependencies.

What is the minimum indexation parity threshold before launch? Target 98%+ parity for high-value commercial URLs. Below 95% indicates unresolved canonicalisation, renderability, or redirect-chain issues that will degrade organic traffic after the flip — treat it as a no-go.

How do we handle dynamic URL parameters during the audit? Classify each parameter by function (tracking, sorting, session, pagination) using log analysis and Search Console parameter data, then set canonical rules and crawler directives so faceted and session variants do not waste crawl budget or create duplicates.

When should rollback triggers be defined? During the risk-scoring stage, before staging QA. Fix explicit numeric thresholds — for example >15% organic drop, >5% 5xx rate, or any broken core conversion path — so the cutover team executes rather than debates during an incident.

How much of this can be automated and re-run? More than most teams attempt, and the payoff compounds across migrations. The crawl, the log join, the orphan diff, the parity comparison against baseline, and most of the validation checklist are all scriptable, which turns the gate from a manual exercise someone performs once into a command anybody can run on demand. That matters during remediation, because the question “have we fixed it yet?” gets asked repeatedly and should not cost a day of analyst time each time. Build the checks as a repository script from the start, and the same script becomes your post-launch verification and, later, the starting point for the next migration.

Who owns the go/no-go decision? A single accountable launch lead records it, but it requires documented sign-off from SEO, Engineering, and Product gate owners. Any unmet validation threshold is an automatic no-go regardless of schedule pressure.

What if the schedule will not accommodate a 60–90 day audit? Compress the scope rather than the rigour, and be explicit about what you are choosing not to know. The parts that cannot be dropped are the frozen baseline, the log-versus-crawl diff, and the value tiering of the top revenue paths — without those there is no way to prioritise, verify, or later prove recovery. The parts that can be reduced under time pressure are the breadth of risk scoring and the depth of template-parity testing, both of which can be sampled rather than exhaustive. Write down what was skipped and attach it to the go/no-go record, so the decision is made with the gap visible rather than in ignorance of it.

Does this phase apply to a replatform where the URLs are not changing? Yes, and teams are most often caught out precisely there, because “the URLs are not changing” is taken to mean nothing needs auditing. What changes on a replatform is everything except the URL: rendering, response headers, canonical logic, structured data, pagination behaviour, and how the new system handles the accumulated variety of legacy parameter combinations. The baseline is what lets you prove those stayed equivalent, and the log inventory is what reveals the URL patterns the new platform has never been tested against.

Related

← Back to Home

Explore Sub-topics