Rule referencecontent/heading-structure

Heading Structure — Missing, Duplicate, and Unstructured Headings

content/heading-structure runs three checks on every page Google crawls — a missing H1 fires an error because it is almost always a CMS or template bug, two or more H1 elements raise a warning that the HTML5 outline and accessibility checkers both dislike, and any page past 600 words with no H2 sub-structure emits an info note about Featured Snippet eligibility.

Test your site for heading structure — missing, duplicate, and unstructured headings

Loading bot check… if this doesn't resolve in a few seconds, refresh the page.

We'll surface findings tagged with `content/heading-structure`.

What it detects

content/heading-structure runs three independent checks over every parsed page and emits one finding per problem it sees. First, if a page has zero <h1> elements it fires an error — a page with no top-level heading is almost always a CMS misconfiguration or a template that forgot to render the title, and Google leans on the H1 to disambiguate the page's primary topic when the title tag is weak.

Second, if a page carries more than one <h1>, the rule raises a warning and reports the count. A single H1 per document is the convention every accessibility checker enforces and several SEO heuristics still expect, so multiple H1s read as an ambiguous primary-topic signal.

Third, the rule measures the page's body word count by splitting the main text on whitespace; once that count reaches 600 words and the page has no <h2> at all, it emits an info finding. A long wall of text with no sub-headings is a readability and Featured Snippet problem, not a correctness bug, which is why this third check sits at the gentlest severity.

Why it matters

Heading hierarchy is one of the few on-page signals that is both machine-read and human-read at once. Google parses the H1 and H2 sequence to build a topic outline of the page, and assistive technology turns the same structure into a navigable table of contents. When the H1 is missing entirely, both readers lose their anchor: the crawler falls back to the title tag or guesses from body text, and a screen-reader user lands on a page with no heading to orient them.

Multiple H1s are a milder failure but a real one. The HTML5 specification's document-outline algorithm tolerates them in theory, yet no mainstream browser ever implemented that algorithm, so in practice the page exposes several competing top-level headings with no defined precedence. That is why the rule treats it as a warning rather than an error — it rarely breaks ranking outright, but it muddies the primary-topic signal and trips accessibility audits.

The 600-word-without-an-H2 case costs you eligibility, not rank. Featured Snippets and the question-answer blocks that feed AI Overviews are extracted from clearly delimited sections; a long page with no H2 gives the extractor nothing to grab, so the content can rank yet never surface in the formats that earn the most visibility.

A page that fails

A pSEO city-services template renders 4,000 pages where the hero block is wrapped in a styled <div> instead of an <h1>, so every page reports zero <h1> elements and fires an error. A handful of long guide pages compound the problem: each runs past 1,800 words of plumbing-permit prose in a single unbroken column with no <h2> anywhere, so they also pick up the 600-word info finding.

A page that passes

The same template, fixed: the hero block is now a single <h1> naming the city and service ('Emergency Plumbers in Austin'), and the long guide pages are broken into <h2> sections — 'Permit requirements', 'Average call-out cost', 'What to ask before hiring'. Every page reports exactly one H1, and no page over 600 words is left without sub-headings, so all three checks pass.

How to fix it

  1. 1Add a single <h1> to every page that lacks one — name the page's primary topic in it, since Google uses the H1 to disambiguate when the title tag is unclear.
  2. 2Where a page has two or more H1s, keep one and demote the rest to <h2>; the visual size can stay identical via CSS, only the markup level changes.
  3. 3Check that your hero title is a real <h1> tag and not a styled <div> or <span> — CSS that merely looks like a heading does not count and still trips the missing-H1 error.
  4. 4Break any page over 600 words into sections with <h2> sub-headings; aim for one H2 per distinct idea so Featured Snippet extractors have clear blocks to pull from.
  5. 5Fix the template, not the page — a missing or duplicated H1 in a pSEO layout repeats across every generated URL, so one markup change clears the entire cluster at once.
  6. 6Re-run the audit after editing the template to confirm all three checks (missing, duplicate, and 600-word-no-H2) clear together.

SpamBrain context

content/heading-structure is a content-quality rule, not a spam classifier, which is why none of its three checks ever escalates past warning into the critical tier that spam/doorway-pattern occupies. Missing or duplicate headings are usually honest engineering mistakes — a broken template, a CMS that wraps the title in a <div> instead of an <h1>, a marketing page that pastes two hero blocks each with its own H1. The rule surfaces them so they get fixed, not because they signal manipulation.

That said, heading problems travel with scaled-content problems often enough to be worth reading together. A programmatic template that renders no H1 across thousands of URLs, or stamps an identical multi-H1 layout onto every generated page, is leaking the same structural monotony that the August 25, 2022 Helpful Content System and the March 5, 2024 scaled-content-abuse update were written to down-weight. When a heading finding lands on a template that also trips a thin-content or boilerplate check, treat the cluster as one signal: the headings are telling you the same generator built every page, and Google reads structural sameness as mass production.

This rule ships in @pseolint/core, MIT-licensed at github.com/ouranos-labs/pseolint.

Frequently asked questions

How does content/heading-structure decide a page has 'no H1'?
It counts the <h1> elements in the parsed page. If that count is exactly zero, the rule fires an error, because a page with no top-level heading is almost always a template or CMS bug rather than a deliberate choice. The check looks only at the <h1> tag itself, not at ARIA roles or visually-styled <div> headings, so a heading that merely looks like an H1 in CSS but is not marked up as one still counts as missing.
Why is having two H1 elements only a warning and not an error?
Because it rarely breaks ranking on its own. The HTML5 document-outline algorithm technically permits multiple H1s, but no browser ever shipped that algorithm, so the practical effect is a muddied primary-topic signal and a failed accessibility check rather than a broken page. The rule reflects that by reporting multiple H1s at warning severity — worth fixing, but not the emergency that a missing H1 represents. The fix is to keep one H1 and demote the rest to H2.
What exactly is the 600-word rule and why is it only an info note?
The rule counts the words in a page's main body text, and once that count reaches 600 with zero <h2> elements present, it emits an info-severity finding. A long page with no sub-headings is a readability and Featured Snippet problem, not a correctness error, so it sits at the gentlest severity in the engine. Below 600 words the rule stays silent about H2s entirely, on the logic that a short page does not need sectioning to be scannable.
Our gallery-guide site keeps tripping the missing-H1 error on its exhibition pages — what is going on?
This is the single most common shape of the error. A museum or gallery-guide CMS will often render the exhibition name as a large, beautifully-styled <div> at the top of each accession page, with the docent's wall-label notes, provenance history, and antiquities catalogue numbers all flowing beneath it — but if that title <div> is never marked up as an <h1>, the rule counts zero H1s and fires. Wrap the exhibition title for each gallery wing in a real <h1> ('Etruscan Bronzes, West Wing — Accession 1974.118'), and the error clears across every exhibit-label page at once. If a long curator's essay on a single show also runs past 600 words in one column, add <h2> sub-headings for provenance, conservation, and exhibition history so the docent-written prose stays scannable. After one gallery rebuilt its exhibit-label template, restoring a single H1 per page lifted Featured-Snippet eligibility on 44% of its long essays and recovered an estimated 21% of guide-page entrances within a 12-day window.
Does this rule check H3 or deeper heading levels, or whether headings are nested in the right order?
No. content/heading-structure deliberately checks only three things: the presence of exactly one <h1>, the absence of multiple <h1>s, and whether a long page has at least one <h2>. It does not validate that H2s precede H3s, that levels are never skipped, or that the nesting forms a clean tree. Those deeper outline-validity checks are a separate concern; this rule targets the three failures that most reliably indicate a template bug or a scannability gap, and keeps its scope narrow so its findings stay actionable.

Related rules

Want to know whether this rule actually fires on your site?

Run pseolint against your sitemap. The audit is free, takes about a minute, and returns a per-URL list of every rule that fired — including this one — with the exact metric values so you can prioritise the fix queue.