Skip to content
Issue docs

Page has no H1 heading

Importanth1_heading_missingIssue 70

What is this issue?

This issue fires when a page contains no <h1> element.

A passing page has exactly one, stating what the page is about:

<h1>Wireless Noise-Cancelling Headphones</h1>

This used to share a check with the heading-order rule under the title Missing H1, or heading levels skipped. Those two conditions can be true of the same page, and sharing a code meant one of the two findings was dropped on any page that had both — so this split is what makes both reportable at once.

Why it matters

  • The h1 is the strongest on-page topic signal after the title. It tells search engines what the page is about in the page's own words, and it is the heading most likely to be reused in a snippet.
  • Screen reader users navigate by heading. Jumping to the h1 is the standard way to reach a page's main content; a page without one has no landmark to jump to.
  • A page without one usually has a structural problem. In practice the title is present but marked up as a styled <div> or <h2>, which means the whole document outline is shifted.
  • The finding includes the heading tree, so you can see what headings the page does have and which one should have been the h1.

How to fix it

  1. Add exactly one <h1> carrying the page's main topic. It should read like a heading, not like a site name — the site name belongs in the header or the <title>.

  2. Promote whatever is playing the part. If the visual title is a <div class="page-title"> or an <h2>, change the element rather than adding a second heading. Styling follows from CSS, not from the tag.

  3. Do not use more than one. Multiple h1s do not divide the signal evenly; they leave the topic ambiguous.

  4. Keep it aligned with the title tag without duplicating it word for word. The title is written for the search result, the h1 for the page.

  5. Check your templates, not just the page. A missing h1 is almost always template-wide.

Examples

1. Styled text standing in for a heading

Problem — the page's subject is marked up as a <div>, so nothing in the document says what the page is about:

<body>
  <div class="page-title">Wireless Noise-Cancelling Headphones</div>
  <h2>Specifications</h2>
  <h2>Reviews</h2>
</body>

Fixed — the same text, in the element that carries the meaning:

<body>
  <h1>Wireless Noise-Cancelling Headphones</h1>
  <h2>Specifications</h2>
  <h2>Reviews</h2>
</body>

2. A template that starts at <h2>

Problem — a common CMS layout where the site logo used to be the <h1> and was later replaced by an image, leaving every page in the site without one:

<header>
  <a href="/"><img src="/logo.svg" alt="Acme Audio" /></a>
</header>
<main>
  <h2>Trail Running Shoes</h2>
  <h3>Sizing</h3>
</main>

Fixed — promote the page subject; the logo stays an image:

<header>
  <a href="/"><img src="/logo.svg" alt="Acme Audio" /></a>
</header>
<main>
  <h1>Trail Running Shoes</h1>
  <h2>Sizing</h2>
</main>

Promoting the existing headings is one valid fix; adding an <h1> above them and leaving the <h2>/<h3> as they are is the other. Either satisfies this check — what matters is that the page states its subject in an <h1>, and that the levels below it stay consecutive, which is heading_levels_skipped (#71).

3. An empty <h1> counts as present

Passes this check, but does nothing useful:

<h1></h1>
<h2>Specifications</h2>

The element exists, so h1Count is 1 and this issue does not fire. An empty heading still tells a reader and a search engine nothing, so give it the page's subject:

<h1>Wireless Noise-Cancelling Headphones</h1>
<h2>Specifications</h2>

How PixyScan detects this

  1. HTML parsing. The crawler collects every <h1>–<h6> in the document body, in source order, into a heading tree.

  2. Count. The issue is raised when the count of <h1> elements is zero.

  3. Payload. The finding carries the full headingTree — each heading's tag and its first 200 characters of text — so the report can show what the page has instead.

  4. Independence. This check and heading_levels_skipped are evaluated separately, and a page can raise both. Distinct codes are what makes both storable: the uniqueness key is (scan, URL, issue), so two findings under one code collided and two under two codes do not.

  5. Scope. Page-level.

What we store

Storage Level

Page Level — evaluated for each crawled URL.


Database Table / Prisma Model

PageSeoBasicsData

One row per crawled URL, keyed by urlId.


Fields Used

Field Type Description
h1Count Int? How many <h1> elements the page body contains. 0 is exactly what this issue reports
headingTree Json? Every <h1>–<h6> in source order, each {tag, text} with text truncated to 200 characters. null when the page has no headings at all

headingTree is stored so the report can show what the page has instead of an <h1>. "There is no H1" is not actionable on its own; "there is no H1, and the first heading is an <h2> reading Related products" tells you where to look.

The same two columns are read by heading_levels_skipped (#71). A page can raise both — they are separate issue codes precisely so both findings survive the (scan, url, issue_code) uniqueness key.


Detection Dependencies

  • HTML Document — the body of the fetched page is parsed for its heading elements

Further reading