Canonicals in two forms: www, scheme and trailing slash
Half the site canonicalising to www and half to the apex domain. squirrel compares four form axes across the crawl.
What canonical form drift is
Every canonical on the site can be individually valid and the set can still be wrong. If half the pages canonicalise to https://www.example.com/thing/ and half to https://example.com/thing, the two halves consolidate onto different hosts and the ranking signal splits between them.
No per-page rule can see this, because a page only knows its own canonical. Four things are one decision for a whole site: the scheme, whether the host carries www, whether tracking parameters survive into the canonical, and, within a section, whether paths end in a slash.
What squirrel checks
The rule runs once per crawl and emits a single check named canonical-form-drift. It compares four axes: scheme, host, trailing slash and tracking params.
- Skipped with the message
Only N page(s) crawled; 10 needed to establish a canonical-form norm. - Skipped with the message
No canonical form is shared by 70% of at least 10 comparable pages. - Skipped with the message
No pages available for analysiswhen the crawl produced no pages. That wording comes from the non-streaming path; a streaming crawl with no pages takes theOnly 0 page(s) crawledbranch instead. - Passing with the message
All N comparable canonical(s) share one form (scheme https, host apex)when nothing drifts. The summary lists up to ten judged axes in a fixed order, scheme then host then trailing slash then tracking params, each as the axis name, its section where it has one, and the winning form. - Warning, or failing, with the message
N of M canonical(s) are declared in a different form than the rest of the site (scheme https, host apex). Each item reads40 of 45 canonicals in /blog use trailing slash, these 5 use no trailing slashand carries up to 20 example URLs. The status isfailwhen the deviant pages are 20% or more of the judged pages, andwarnbelow.
A page enters the comparison only when its status was 2xx and its canonical is present, absolute and points at the same hostname as the page once a leading www. is stripped from both. A canonical pointing at a sibling subdomain is treated as cross-host and dropped. Missing, relative, unparseable and cross-host canonicals all belong to core/canonical and are dropped here rather than reported twice.
The trailing-slash axis is compared inside a section rather than site-wide, because using a slash in /blog and not in /product is a normal pattern. The section is the first path segment, or / for a flat path, and each section has to clear the sample floor and the agreement bar on its own. The site root and any path whose last segment contains a dot are excluded from that axis entirely, since neither had a choice.
Tracking parameters are matched by name: _ga, fbclid, gclid, igshid, mkt_tok, msclkid, ref, source, ttclid, yclid, and anything starting utm_ or mc_. Having a query string at all is not the test, because a paginated canonical legitimately carries one.
How to fix it
Build the canonical from one function that everything calls, rather than from a template string per route:
const canonical = new URL(path, "https://example.com").href;That fixes scheme and host by construction. For the trailing slash, pick one convention per section and let the router enforce it. For tracking parameters, strip them before building the canonical rather than after.
The common false positive is a site mid-migration, where the new form is correct and the old pages have not caught up. The finding is accurate and the fix is the migration.
| Rule ID | core/canonical-form-drift |
| Category | Core SEO |
| Scope | Site-wide |
| Severity | warning |
| Weight | 3/10 |
Enable / disable
Disable this rule
[rules]
disable = ["core/canonical-form-drift"]Disable all Core SEO rules
[rules]
disable = ["core/*"]Enable only this rule
[rules]
enable = ["core/canonical-form-drift"]enable replaces the default ["*"], so this is the only rule selected. Leave disable out: a rule that matches any disable pattern is skipped even when enable names it. Only an enabled setting under a rule’s own [rule_options] table overrides both lists.
Related rules
- core/canonical: validity of each canonical, which this rule assumes and never repeats.
- core/canonical-header: the header declaration and whether it agrees with the tag.
- url/trailing-slash: the same question asked of the page URLs rather than the canonicals.
- crawl/canonical-chain: canonicals pointing at URLs that redirect instead of answering.
Core SEO findings ship in every audit next to the performance, security and agent experience rules. See Fix SEO issues with AI for how an agent works through a report.
References
Check your site
Run squirrel audit https://example.com and open the Core SEO section of the report. Each drifting axis is listed with the norm, the deviant form and the URLs. Signed-out local audits need no account and cost nothing. Signed-in audits spend credits.