findXpand
← All categories

SEO

Checks findXpand runs against the crawled HTML of every page — titles, meta tags, canonicalization, headings, and other classic on-page ranking factors search engines have used for two decades. Source: src/lib/htmlAudit.ts (analyzeSEO).

30 rules in this category.

IDRuleSeverityWeight
noindexPage is marked noindexCritical15 pts
titleTitle tag is missingCritical14 pts
alt-textN of N images missing alt textMedium–High6–10 pts (scales with how much of the page is affected — see source)
h1-missingNo H1 heading foundHigh10 pts
meta-descNo meta descriptionHigh9 pts
canonicalMissing canonical tagMedium8 pts
og-imageMissing og:imageMedium8 pts
h1-multipleMultiple H1 headings found (N)Medium6 pts
heading-orderHeading levels skip a stepMedium6 pts
thin-contentPage has very little text content (N words)Medium6 pts
title-lengthTitle tag length is offMedium6 pts
hreflang-invalidInvalid hreflang language code(s) foundMedium5 pts
meta-desc-lengthMeta description length is offMedium5 pts
og-basicMissing core Open Graph tagsMedium5 pts
canonical-cross-domainCanonical tag points to a different domainLow4 pts
hreflang-no-self-referencePage doesn't reference itself in its hreflang setLow4 pts
twitter-cardMissing twitter:cardLow4 pts
twitter-card-invalidUnrecognized twitter:card value ("(actual value found on the page)")Low4 pts
canonical-relativeCanonical tag uses a relative URLLow3 pts
faviconNo favicon declaredLow3 pts
hreflang-no-x-defaultNo x-default hreflang fallbackLow3 pts
meta-charsetNo charset declaredLow3 pts
meta-desc-duplicates-titleMeta description is identical to the title tagLow3 pts
og-urlMissing og:urlLow3 pts
twitter-contentTwitter card is missing NLow3 pts
og-site-nameMissing og:site_nameLow2 pts
og-typeMissing og:typeLow2 pts
url-structureURL structure could be cleanerLow2 pts
h1Exactly one H1 headingPositive signal0 pts
hreflang-validhreflang codes are validPositive signal0 pts

noindex

Page is marked noindex

SeverityWeightCategory
Critical15 ptsSEO

What triggers it & why it matters: A robots meta tag is telling search engines not to index this page.

How to fix it & effect on score: Remove the noindex directive if this page should appear in search results. Fixing this removes the 15 pts deduction from the SEO category score on the next scan.


title

Title tag is missing

SeverityWeightCategory
Critical14 ptsSEO

What triggers it & why it matters: No <title> element was found in the page head, so search results fall back to a generic or unreadable title.

How to fix it & effect on score: Add a unique, descriptive <title> tag, ideally 50–60 characters. Fixing this removes the 14 pts deduction from the SEO category score on the next scan.

What a pass looks like: Title tag is present and well-sized


alt-text

N of N images missing alt text

SeverityWeightCategory
Medium–High6–10 pts (scales with how much of the page is affected — see source)SEO

What triggers it & why it matters: Images without descriptive alt attributes lose potential image-search traffic and provide no fallback content.

How to fix it & effect on score: Add descriptive alt text to every meaningful image. Fixing this removes the 6–10 pts (scales with how much of the page is affected — see source) deduction from the SEO category score on the next scan.

What a pass looks like: All images have alt text


h1-missing

No H1 heading found

SeverityWeightCategory
High10 ptsSEO

What triggers it & why it matters: The page has no top-level H1, leaving both users and search engines without a clear statement of the page topic.

How to fix it & effect on score: Add exactly one H1 that describes the page's main topic. Fixing this removes the 10 pts deduction from the SEO category score on the next scan.


meta-desc

No meta description

SeverityWeightCategory
High9 ptsSEO

What triggers it & why it matters: Search engines are writing their own snippet because no meta description was found.

How to fix it & effect on score: Add a 140–160 character meta description summarizing the page. Fixing this removes the 9 pts deduction from the SEO category score on the next scan.

What a pass looks like: Meta description is present and well-sized


canonical

Missing canonical tag

SeverityWeightCategory
Medium8 ptsSEO

What triggers it & why it matters: Without a canonical tag, pages reachable through multiple URLs (tracking params, trailing slashes) risk being indexed as duplicates.

How to fix it & effect on score: Add a self-referencing <link rel="canonical"> tag to every indexable page. Fixing this removes the 8 pts deduction from the SEO category score on the next scan.

What a pass looks like: Canonical tag is present


og-image

Missing og:image

SeverityWeightCategory
Medium8 ptsSEO

What triggers it & why it matters: Without an og:image tag, shared links render as a plain text card with no thumbnail, which measurably lowers click-through on social feeds.

How to fix it & effect on score: Add <meta property="og:image" content="https://.../preview.jpg"> pointing at a roughly 1200×630 image. Fixing this removes the 8 pts deduction from the SEO category score on the next scan.

What a pass looks like: og:image is present


h1-multiple

Multiple H1 headings found (N)

SeverityWeightCategory
Medium6 ptsSEO

What triggers it & why it matters: Several H1 elements dilute the page's topical signal and can confuse heading-based navigation.

How to fix it & effect on score: Keep a single H1 per page and demote the rest to H2/H3. Fixing this removes the 6 pts deduction from the SEO category score on the next scan.


heading-order

Heading levels skip a step

SeverityWeightCategory
Medium6 ptsSEO

What triggers it & why it matters: Somewhere the page jumps, e.g. H2 straight to H4, which breaks the logical outline for screen readers and crawlers.

How to fix it & effect on score: Restructure headings so each level follows in order without skipping. Fixing this removes the 6 pts deduction from the SEO category score on the next scan.

What a pass looks like: Heading hierarchy is in order


thin-content

Page has very little text content (N words)

SeverityWeightCategory
Medium6 ptsSEO

What triggers it & why it matters: Pages with very little unique body text give search engines little to rank on and are more likely to be treated as thin content, especially if similar pages exist elsewhere on the site.

How to fix it & effect on score: Expand the page with substantive, unique content relevant to its topic, or noindex it if it's intentionally minimal (e.g. a redirect or utility page). Fixing this removes the 6 pts deduction from the SEO category score on the next scan.

What a pass looks like: Page has substantive body content (N words)


title-length

Title tag length is off

SeverityWeightCategory
Medium6 ptsSEO

What triggers it & why it matters: The title is N characters ("(actual value found on the page, truncated)…"). Search engines typically truncate titles outside the 50–60 character range.

How to fix it & effect on score: Tighten the title to roughly 50–60 characters while keeping it descriptive. Fixing this removes the 6 pts deduction from the SEO category score on the next scan.


hreflang-invalid

Invalid hreflang language code(s) found

SeverityWeightCategory
Medium5 ptsSEO

What triggers it & why it matters: One or more hreflang values ((comma-separated list of the specific items found)) don't match a valid ISO 639-1 language code or language-region pair. Search engines ignore alternates with unrecognized codes entirely.

How to fix it & effect on score: Use valid codes like "en", "en-us", or "x-default" for the international fallback. Fixing this removes the 5 pts deduction from the SEO category score on the next scan.


meta-desc-length

Meta description length is off

SeverityWeightCategory
Medium5 ptsSEO

What triggers it & why it matters: The description is N characters, outside the ~140–160 range search engines display in full.

How to fix it & effect on score: Adjust the meta description to roughly 140–160 characters. Fixing this removes the 5 pts deduction from the SEO category score on the next scan.


og-basic

Missing core Open Graph tags

SeverityWeightCategory
Medium5 ptsSEO

What triggers it & why it matters: N missing, so links shared on Facebook, LinkedIn, Slack, and similar platforms fall back to a blank or auto-generated title/snippet.

How to fix it & effect on score: Add og:title and og:description meta tags with content written for social sharing. Fixing this removes the 5 pts deduction from the SEO category score on the next scan.

What a pass looks like: og:title and og:description are present


canonical-cross-domain

Canonical tag points to a different domain

SeverityWeightCategory
Low4 ptsSEO

What triggers it & why it matters: The canonical URL ("(actual value)") points to a different domain than the page being audited ((actual value)). This is sometimes intentional (e.g. syndicated content), but if unintended it tells search engines to index the other domain's URL instead of this one.

How to fix it & effect on score: Confirm this is intentional; otherwise point the canonical tag at this page's own domain. Fixing this removes the 4 pts deduction from the SEO category score on the next scan.


hreflang-no-self-reference

Page doesn't reference itself in its hreflang set

SeverityWeightCategory
Low4 ptsSEO

What triggers it & why it matters: Google's guidelines expect every page in a hreflang group to include a self-referencing alternate; without one, the whole annotation set can be disregarded.

How to fix it & effect on score: Add a hreflang alternate for this exact URL pointing back to itself. Fixing this removes the 4 pts deduction from the SEO category score on the next scan.


twitter-card

Missing twitter:card

SeverityWeightCategory
Low4 ptsSEO

What triggers it & why it matters: X/Twitter will not render a rich preview at all without a twitter:card tag, even when Open Graph tags are present.

How to fix it & effect on score: Add <meta name="twitter:card" content="summary_large_image"> (or "summary" for a smaller preview). Fixing this removes the 4 pts deduction from the SEO category score on the next scan.

What a pass looks like: twitter:card is present and valid


twitter-card-invalid

Unrecognized twitter:card value ("(actual value found on the page)")

SeverityWeightCategory
Low4 ptsSEO

What triggers it & why it matters: X/Twitter only recognizes "summary", "summary_large_image", "app", and "player" as twitter:card values — anything else causes the preview to silently fail.

How to fix it & effect on score: Set twitter:card to "summary_large_image" for most content pages. Fixing this removes the 4 pts deduction from the SEO category score on the next scan.


canonical-relative

Canonical tag uses a relative URL

SeverityWeightCategory
Low3 ptsSEO

What triggers it & why it matters: The canonical href ("(actual value)") isn't a full absolute URL. Google's own guidance recommends absolute URLs for canonical tags to avoid ambiguity if the tag is copied elsewhere or the base URL changes.

How to fix it & effect on score: Use a full absolute URL (https://...) in the canonical tag rather than a relative path. Fixing this removes the 3 pts deduction from the SEO category score on the next scan.


favicon

No favicon declared

SeverityWeightCategory
Low3 ptsSEO

What triggers it & why it matters: No <link rel="icon"> (or shortcut/apple-touch-icon) tag was found. Google displays a page's favicon next to its listing in both mobile and desktop search results, so a missing one leaves search results looking generic.

How to fix it & effect on score: Add <link rel="icon" href="/favicon.ico" sizes="any"> in <head> (plus an SVG/PNG variant for higher resolution). Fixing this removes the 3 pts deduction from the SEO category score on the next scan.

What a pass looks like: Favicon is declared


hreflang-no-x-default

No x-default hreflang fallback

SeverityWeightCategory
Low3 ptsSEO

What triggers it & why it matters: Multiple language/region alternates are declared but none is marked hreflang="x-default", so visitors whose language doesn't match any listed variant get no explicit fallback page.

How to fix it & effect on score: Add <link rel="alternate" hreflang="x-default" href="..."> pointing at your default/international page. Fixing this removes the 3 pts deduction from the SEO category score on the next scan.


meta-charset

No charset declared

SeverityWeightCategory
Low3 ptsSEO

What triggers it & why it matters: Neither a <meta charset> tag nor a Content-Type meta tag with a charset was found in the page head. Without an explicit charset, browsers have to guess the encoding, which can occasionally garble non-ASCII text before it's indexed.

How to fix it & effect on score: Add <meta charset="UTF-8"> as the first element inside <head>. Fixing this removes the 3 pts deduction from the SEO category score on the next scan.

What a pass looks like: Charset is declared


meta-desc-duplicates-title

Meta description is identical to the title tag

SeverityWeightCategory
Low3 ptsSEO

What triggers it & why it matters: The meta description just repeats the title verbatim instead of adding new information, so the search snippet wastes the chance to give searchers an extra reason to click through.

How to fix it & effect on score: Write a meta description that summarizes the page's content rather than repeating the title. Fixing this removes the 3 pts deduction from the SEO category score on the next scan.


og-url

Missing og:url

SeverityWeightCategory
Low3 ptsSEO

What triggers it & why it matters: Without og:url, shares of the same page reached through different query strings or paths can be treated as separate URLs, splitting likes and shares across duplicates.

How to fix it & effect on score: Add <meta property="og:url" content="N"> using the canonical URL of the page. Fixing this removes the 3 pts deduction from the SEO category score on the next scan.

What a pass looks like: og:url is present


twitter-content

Twitter card is missing N

SeverityWeightCategory
Low3 ptsSEO

What triggers it & why it matters: twitter:title, twitter:description, and twitter:image fall back to their og: equivalents when absent, but neither the twitter:* nor the og: version is set here, so the card renders incomplete.

How to fix it & effect on score: Add the missing twitter:* tags directly, or add the matching og: tags so Twitter can fall back to them. Fixing this removes the 3 pts deduction from the SEO category score on the next scan.

What a pass looks like: Twitter card has title, description, and image (directly or via Open Graph fallback)


og-site-name

Missing og:site_name

SeverityWeightCategory
Low2 ptsSEO

What triggers it & why it matters: Without og:site_name, some platforms omit the brand label that normally appears above the title in a shared card.

How to fix it & effect on score: Add <meta property="og:site_name" content="Your Site Name">. Fixing this removes the 2 pts deduction from the SEO category score on the next scan.

What a pass looks like: og:site_name is present


og-type

Missing og:type

SeverityWeightCategory
Low2 ptsSEO

What triggers it & why it matters: Open Graph silently defaults to "website" when og:type is absent, but declaring it explicitly (article, product, etc.) unlocks richer, type-specific card layouts on some platforms.

How to fix it & effect on score: Add <meta property="og:type" content="website"> (or "article", "product", etc. as appropriate). Fixing this removes the 2 pts deduction from the SEO category score on the next scan.

What a pass looks like: og:type is present


url-structure

URL structure could be cleaner

SeverityWeightCategory
Low2 ptsSEO

What triggers it & why it matters: This page's URL N. Google treats hyphens as word separators but not underscores, and mixed-case or heavily-parameterized URLs are more prone to being treated as duplicates of a cleaner canonical version.

How to fix it & effect on score: Prefer lowercase, hyphen-separated paths and keep query parameters minimal on the canonical/indexable URL. Fixing this removes the 2 pts deduction from the SEO category score on the next scan.

What a pass looks like: URL structure follows SEO-friendly conventions


h1

Exactly one H1 heading

SeverityWeightCategory
Positive signal0 ptsSEO

What triggers it & why it matters: This rule only ever surfaces as a passing signal in the current source — it fires (as a pass) when: Exactly one H1 heading. There is no separate failing issue() call under this exact id; the failure condition is the logical negation of the pass condition, and in most cases is covered by a related rule id documented elsewhere in this category (see the table above for a sibling rule on the same feature).

Effect on score: Meeting this condition costs nothing and confirms a positive signal. Missing it doesn't directly deduct points under this id — it typically shows up as a deduction under the related failing rule instead.


hreflang-valid

hreflang codes are valid

SeverityWeightCategory
Positive signal0 ptsSEO

What triggers it & why it matters: This rule only ever surfaces as a passing signal in the current source — it fires (as a pass) when: hreflang codes are valid. There is no separate failing issue() call under this exact id; the failure condition is the logical negation of the pass condition, and in most cases is covered by a related rule id documented elsewhere in this category (see the table above for a sibling rule on the same feature).

Effect on score: Meeting this condition costs nothing and confirms a positive signal. Missing it doesn't directly deduct points under this id — it typically shows up as a deduction under the related failing rule instead.