Skip to content

SEO checks

The SEO app checks every crawled page’s on-page fundamentals — the things an editor can fix from the CMS: titles, meta descriptions, canonicals, robots directives, headings, and structured data. It rides the same crawl as Broken Links (no extra requests, no separate metering) and refreshes on every crawl.

Install it with seoEnabled at registration (POST /v1/sites, default on) or later (PATCH /v1/sites/{id}). The report is served at GET /v1/sites/{id}/seo-report.

Rule Severity Fires when
title-missing serious the page has no (non-SVG) <title>
title-duplicate moderate two or more pages share the same title
title-too-long / title-too-short minor the title exceeds 60 characters / is under 10
meta-description-missing moderate no meta[name=description] with content
meta-description-duplicate moderate two or more pages share the same description
meta-description-length minor the description is under 50 or over 160 characters
canonical-missing minor no link[rel=canonical]
canonical-offsite serious the canonical points at a different site
canonical-mismatch moderate the canonical points at a different URL on the same site
meta-noindex serious / critical a robots meta asks search engines to skip the page; critical when the page is in the sitemap (or is the root) — usually a publish gone wrong
h1-missing / h1-multiple moderate / minor the page has no H1 / more than one
structured-data-invalid moderate a JSON-LD block doesn’t parse

| structured-data-malformed | moderate | a property value is in a format search engines can’t read — a non-ISO date, a price with a currency symbol or comma decimal, a broken URL, or a block missing @context | | structured-data-incomplete | moderate | a schema block of a catalogued type (Product, Article, FAQPage, BreadcrumbList, LocalBusiness, Organization, Event, JobPosting) is missing a property Google requires for rich results — e.g. a Product without offers or a price |

JSON-LD presence (which pages carry structured data, which @type values, and which pages have none) is reported in the report’s structured-data panel as information, not as an issue. The required-property catalog is deliberately small and shallow — full schema.org validation is Google’s Rich Results Test’s job.

Each run gets a 0–100 score: every rule is penalised by its severity and by how widely it spreads across the site, so a template-wide missing description counts as one systemic fix, not hundreds of independent hits. Only a completely clean run scores 100. The score’s methodology mirrors the accessibility score.

Separately, the indexability verdict answers the binary question “is anything actively blocking this site from being found?” — it flips to at-risk when a sitemap-listed page carries noindex, or a canonical credits another site.

Issues are derived at read time from per-page field snapshots, so changing the site’s excludePaths globs immediately updates the report (and duplicate detection recomputes over the remaining pages). The score stored on each run feeds the trend chart and is refreshed when the globs change.

Toggling the app off stops the extraction and makes the report respond 404; past runs’ snapshots are kept, so the report returns when the app is re-installed. If SEO is the only crawl-consuming app on the site, turning it off also unschedules the crawl — see Apps.