This is a worked example, not a client result
It shows the investigation order, the finding format and the reasoning we use on real projects, built from a pattern we encounter repeatedly. The figures are illustrative. We publish client numbers only when they are real, measured, and the client has agreed to them being published.This is a worked example. It shows the finding format, the investigation order and the reasoning we use on real projects, built from a pattern we encounter repeatedly. The figures are illustrative. We publish client numbers only when they are real, measured and the client has agreed to them being published.
Executive summary
A mid-market retailer with roughly 12,500 products had flat organic performance for three quarters. Total impressions looked stable, so nothing triggered an escalation. Underneath that stable total, one entire class of demand — searches for specific product model names, the highest-intent queries in the catalogue — had collapsed to almost nothing.
The cause was a single template-level canonical rule, added during a duplicate-content cleanup, that pointed every product URL at its parent category. It was a correct fix applied to the wrong scope.
Business problem
The company sells a catalogue where buyers frequently search by exact model name. Those searches convert several times better than category searches because the buyer has already decided what they want.
The symptom presented to us was not "we lost traffic". It was "we keep investing in SEO and nothing moves". Total impressions had not fallen, so the internal read was that the market had softened.
Baseline
Before analysing anything we established what the site was actually worth, per URL:
- Full crawl with rendering enabled: 12,480 product URLs discovered.
- 16 months of Search Console data, exported at page and query level.
- GA4 landing pages and revenue by URL for the same period.
- Query set classified by intent: brand, category, model-specific, informational.
Segmenting by query class was the step that made the problem visible. Site totals hide class-level collapse, which is exactly why the problem survived three quarters of reporting.
Investigation
The order matters, because each step eliminates a category of cause.
- Segment by page type and query class. Category impressions were stable. Model-specific impressions had fallen off a cliff. That immediately ruled out a site-wide cause.
- Check indexation, not position. No product URL appeared in the index. This is a completely different problem from ranking poorly, and it points at technical causes rather than content or authority ones.
- Read the coverage report by category. The product URLs sat under "Alternate page with proper canonical tag" — Search Console was reporting the cause plainly, but nobody was reading coverage by page type.
- Crawl and diff. 11,902 of 12,480 product URLs declared a canonical pointing at the parent category. The remaining 578 were newer products on a template variant that had not yet been migrated.
- Find the date. The crawl diff and the release log agreed: the rule shipped on 14 March, in a ticket titled "fix duplicate variant URLs".
- Confirm the mechanism. Category pages were receiving impressions for model-specific queries at position 20+ — they were being offered as the answer and rejected, because a category page is not the right answer to a model query.
Evidence
| Check | Finding |
|---|---|
| Product URLs crawled | 12,480 |
| Declaring a category canonical | 11,902 (95.4%) |
| Indexed product URLs | 0 |
| Coverage category | Alternate page with proper canonical tag |
| Model-query impressions, pre-release | ~4,300/month |
| Model-query impressions, post-release | ~380/month |
| Release date in change log | 14 March, "fix duplicate variant URLs" |
Every one of those rows is checkable. That is the standard: a finding that cannot be verified by the client is not finished.
Root cause
The original problem was real. Colour and size variants were generating duplicate URLs, and something needed to consolidate them.
The fix was implemented at the product template level rather than at the variant level. Because variants render through the same template as base products, the rule caught both. The ticket was tested by confirming that variant duplicates had disappeared from the index — which they had. Nobody tested whether base products were still there.
This is the most common shape of a self-inflicted technical SEO problem: a correct fix, applied one level too broadly, validated against the symptom it was written for rather than against the system.
Actions
Written as exact changes, with acceptance criteria, because a recommendation a developer has to interpret is not a recommendation.
- Scope the canonical rule to variant parameters only. Base product URLs self-canonicalise. Acceptance: a crawl of 500 sampled base product URLs shows a self-referencing canonical; a crawl of their variant URLs shows a canonical pointing at the base product, not the category.
- Remove product URLs from the category sitemap; publish a dedicated product sitemap. Acceptance: sitemap index lists a product sitemap containing only canonical, indexable, in-stock product URLs.
- Add internal links from category pages to the top 200 products by revenue. Acceptance: crawl shows each of those 200 URLs receiving at least one in-body internal link from its category.
- Request validation in Search Console for the affected coverage category after release.
- Add a release check: any change touching canonical, robots or status-code logic requires a staging crawl diff against production before merge.
Implementation
One sprint. The rule change itself was a few lines; the sitemap and internal linking work was the larger part. It shipped to staging first, and we ran the acceptance crawls there before release — which caught a second issue, where out-of-stock products were being excluded from the new sitemap but still linked internally.
Priority was P0, because the affected pages carried the catalogue's highest commercial intent and the fix was cheap. High impact plus low effort is the "do first" quadrant, and it does not need a debate.
Result
The honest answer, for a worked example, is what we measure and when we expect to see it:
- Indexed product URLs — target above 90% of in-stock products within 60 days of release. This is the primary signal and it moves first.
- Non-brand impressions on model-specific queries — measured against the 28 days before release, on the affected URL cohort only. Recovery of an indexation problem is usually visible within two to six weeks of recrawl.
- Revenue per landing page on product templates, against the pre-release baseline.
We report the cohort, not the site total. A site total is what hid this problem in the first place.
What we learned
Three things worth carrying to the next project:
- Site totals hide class-level failures. If reporting is not segmented by page type and query class, an entire demand class can disappear without moving the headline number.
- Validate a fix against the system, not the symptom. The variant ticket was tested correctly and still caused this. The missing check was "what else does this rule now apply to?"
- Coverage reports are diagnostic, not decorative. The cause was named explicitly in Search Console for 94 days. Someone had to be looking at it by page type.
What happens next
For a site in this position, the sequence after the fix is: confirm indexation recovery on the cohort, then look at whether the product template actually answers a model-specific query well enough to rank once it can. Indexation removes the barrier. It does not create the demand or win the position.
The data behind it
Impressions on model-specific queries against total site impressions
Indexed illustrative values. The site total masked a complete loss on one query class.
- Model-specific queries
- All site impressions
View the data as a table
| Period | Model-specific queries | All site impressions |
|---|---|---|
| Jan | 4,200 | 41,000 |
| Feb | 4,380 | 42,500 |
| Mar | 3,100 | 41,800 |
| Apr | 610 | 39,900 |
| May | 420 | 40,100 |
| Jun | 380 | 39,600 |
| Jul | 360 | 40,300 |
| Aug | 350 | 39,800 |
Source: Illustrative reconstruction of the pattern this investigation follows