From c4f218e13917acf84cc912b5394309762a7f19f9 Mon Sep 17 00:00:00 2001 From: Lukce Date: Sat, 7 Nov 2026 21:52:48 -0500 Subject: [PATCH] fix(scoring): canonical-present skips noindex pages --- .changeset/canonical-present-noindex.md | 5 +++++ packages/scoring/src/noindex.test.ts | 10 +++++++++- packages/scoring/src/technical-seo-rules.ts | 6 +++--- 3 files changed, 17 insertions(+), 4 deletions(-) create mode 100644 .changeset/canonical-present-noindex.md diff --git a/.changeset/canonical-present-noindex.md b/.changeset/canonical-present-noindex.md new file mode 100644 index 0000000..7c73c28 --- /dev/null +++ b/.changeset/canonical-present-noindex.md @@ -0,0 +1,5 @@ +--- +"@advance-labs/scoring": patch +--- + +`tech.canonical-present` no longer asks `noindex` pages for a canonical (its own text says "every indexable page"). `tech.canonical-resolves` and `tech.indexable` still see noindex pages. Found auditing aeo.advancelabs.dev, where `/login` (noindex, nofollow) failed the rule. diff --git a/packages/scoring/src/noindex.test.ts b/packages/scoring/src/noindex.test.ts index b801d0d..0eb8796 100644 --- a/packages/scoring/src/noindex.test.ts +++ b/packages/scoring/src/noindex.test.ts @@ -115,8 +115,16 @@ describe('indexability rules still see noindex pages', () => { expect(f?.passed).toBe(false); }); - it('canonical-present keeps inspecting noindex pages', async () => { + it('canonical-present does not ask a noindex page for a canonical', async () => { const f = (await findings(withLogin())).get('tech.canonical-present'); + expect(f?.affectedUrls ?? []).not.toContain(LOGIN); + }); + + it('canonical-resolves still inspects noindex pages (a malformed canonical is flagged)', async () => { + const ctx = withLogin(); + ctx.pages = ctx.pages.map((p) => (p.url === LOGIN ? { ...p, meta: { ...p.meta, canonical: 'not a url' } } : p)); + const f = (await findings(ctx)).get('tech.canonical-resolves'); + expect(f?.passed).toBe(false); expect(f?.affectedUrls).toContain(LOGIN); }); }); diff --git a/packages/scoring/src/technical-seo-rules.ts b/packages/scoring/src/technical-seo-rules.ts index 548af2a..8a239d0 100644 --- a/packages/scoring/src/technical-seo-rules.ts +++ b/packages/scoring/src/technical-seo-rules.ts @@ -304,9 +304,9 @@ export const technicalSeoRules: Rule[] = [ title: 'Canonical URL is declared', description: 'A canonical tag consolidates duplicate URLs to one preferred address.', recommendation: 'Add to every indexable page.', - // Canonical checks keep seeing noindex pages: a canonical on a page you meant to index is - // exactly what they verify, and pages are only excluded from scoring, not from inspection. - includesNoindexPages: true, + // Scored without noindex pages: its own recommendation says "every indexable page", and a page + // that opted out of search has no use for a canonical. (An accidental noindex is caught by + // tech.indexable, which does see those pages.) canonical-resolves below still sees them. evaluate: (ctx) => { const missing = pagesFailing(ctx, (p) => Boolean(p.meta.canonical)); return missing.length === 0 ? { passed: true } : { passed: false, affectedUrls: missing };