Crawl the site the way a search engine would
Every reachable URL with its response code, redirect path, canonical, depth and inbound links. Not a sample.
Compare the crawl against what is actually indexed
The pages you have and the pages a search engine kept are rarely the same set. The gap is the finding.
Read the server logs
The only record of what a crawler genuinely requested — usually parameters, old redirects and pagination.
Check what is in the response, not what is on the screen
Raw HTML against the painted page. Anything appearing only after JavaScript, a tab or a scroll is listed as at risk.
Find the duplication and the bloat
Near-identical pages competing, parameter URLs nobody meant to publish, thin pages dropped, exclusions nobody has read.
Fix the canonicals and the sitemap
One canonical per page, pointing where you intend. A sitemap of live indexable URLs, generated rather than hand-kept.
Rebuild the internal linking
Orphans given inbound links, depth pulled in, and navigation built as anchors a crawler can actually follow.
Measure speed and layout stability on a real device
Field data where it exists, plus a mid-range phone on a mobile connection. Every result gets a named cause.
Validate the structured data
Checked for validity and, more often the real problem, for whether it is in the response at all.
Hand over a prioritised fix list, and say who ships it
Ordered by what each defect costs, not by a severity colour. An audit nobody implements is worth nothing.