> ## Documentation Index
> Fetch the complete documentation index at: https://docs.37audits.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Heading Structure Auditor

> Checks that h1–h6 headings form a consistent outline without skipped levels or nested headings

<Info>
  **Accessibility Auditor** — reads every `h1`–`h6` heading of the audited page in document order and checks that no level is skipped and that no heading sits inside another heading.
</Info>

<CardGroup cols={2}>
  <Card title="Accessibility" icon="universal-access">
    Screen reader users navigate by heading level; a consistent outline lets them jump straight to the section they need.
  </Card>

  <Card title="Rendered pages" icon="browser">
    When the raw HTML has no heading at all, the page is rendered in a headless Chromium so JavaScript-built content is judged too.
  </Card>
</CardGroup>

Headings are the table of contents of a page. Assistive technologies list them so visitors can move between sections, and search engines use them to understand what each part of the page covers. The outline only works when the levels nest: an `h2` under an `h1`, an `h3` under an `h2`. A jump from `h2` straight to `h4` reads as a missing section, and a heading placed inside another heading is invalid HTML that browsers repair in different ways. This auditor parses the page and reports both problems, one check per heading.

## How it works

1. **Fetch** — downloads the audited URL as 37AuditsBot with a `GET` request. A blocked request ends the audit with `590`; any other failure (timeout, DNS, TLS, non-2xx answer) ends it with `500`.
2. **Parse** — reads every `h1`–`h6` element from the HTML in document order.
3. **Render when needed** — when the raw HTML has no heading at all, loads the page in a headless Chromium, waits for the network to go idle and parses the rendered DOM instead. A browser error, after one relaunch on a crash, ends the audit with `501`.
4. **Rules** — a heading more than one level deeper than the previous one fails (`400`, one check per jump); a heading whose nearest heading ancestor is at the same or a deeper level fails (`401`, one check per heading); a page with no heading even after rendering warns (`300`).
5. **Aggregate** — FAIL wins over WARNING; when every rule passes the audit is `SUCCESS` (`200`) and the message counts the headings per level.

## What it audits

Every row maps to one `checkRule*` method and one `CheckCode`, in the order the auditor runs them. CheckCode suffixes use status ranges: `200` success, `300–399` warning, `400–499` fail, `500–599` error.

| CheckCode | Status | When it fires | Recommendation | Why it matters |
| - | - | - | - | - |
| <a id="37A-HeadingStructureAuditor-400" />`37A-HeadingStructureAuditor-400` | `FAIL` | A heading is more than one level deeper than the previous heading in document order (for example an `h4` right after an `h2`). One check per jump. | Change "`{headingText}`" from `<h{level}>` to `<h{expectedLevel}>`, or add an `<h{expectedLevel}>` between the `<h{previousLevel}>` and it, so every heading is at most one level deeper than the one before. | Assistive technologies read heading levels as an outline, so a skipped level makes visitors think content is missing and makes the structure of the page harder for search engines to trust. |
| <a id="37A-HeadingStructureAuditor-401" />`37A-HeadingStructureAuditor-401` | `FAIL` | A heading element is nested inside another heading element whose level is the same or deeper (an `h2` inside an `h2`, an `h1` inside an `h3`). One check per heading. | Move the `<{tag}>` "`{headingText}`" out of the `<{ancestorTag}>` that contains it; a heading element must not contain another heading. | A heading inside another heading is invalid HTML that browsers repair unpredictably, so screen readers may announce the wrong text or level and the outline becomes unreliable. |
| <a id="37A-HeadingStructureAuditor-300" />`37A-HeadingStructureAuditor-300` | `WARNING` | The page has no `h1`–`h6` heading, even after rendering it in a browser. | Add an `<h1>` that describes the page and `<h2>`/`<h3>` headings for its sections to `{url}`. | Screen reader users and search engines rely on headings to learn what a page is about and to jump to its sections; without any, the page reads as one undifferentiated block and ranks for nothing specific. |
| <a id="37A-HeadingStructureAuditor-200" />`37A-HeadingStructureAuditor-200` | `SUCCESS` | All rules passed. Message counts the headings per level, for example `12 headings on {url} form a consistent outline (1 h1, 4 h2, 7 h3)`. | — | — |
| <a id="37A-HeadingStructureAuditor-500" />`37A-HeadingStructureAuditor-500` | `ERROR` | The page could not be fetched (timeout, DNS, TLS, or a non-2xx answer other than a block). | Make sure `{url}` answers with HTTP 2xx, then run the audit again. Last error: `{error}`. | — |
| <a id="37A-HeadingStructureAuditor-501" />`37A-HeadingStructureAuditor-501` | `ERROR` | The raw HTML had no heading and the browser render failed, after one relaunch on a crash. | Make sure `{url}` loads in a headless Chromium browser, then run the audit again. Last error: `{error}`. | — |
| <a id="37A-HeadingStructureAuditor-590" />`37A-HeadingStructureAuditor-590` | `ERROR` | The audited page answered HTTP 403, HTTP 429 or a Cloudflare challenge, so 37AuditsBot was blocked before it could read the page. | Allow 37AuditsBot in your firewall or bot protection: allowlist its user agent, or verify its Web Bot Auth signature. See [https://www.37audits.com/bot](https://www.37audits.com/bot). | While 37AuditsBot is blocked this audit cannot look at the site, so problems on it go unreported. |

## Output documentation

| Status | Description | Test logic |
| - | - | - |
| **SUCCESS** | The headings form a consistent outline | At least one heading, no skipped level and no nested heading (`200`). |
| **WARNING** | The page has no headings | No `h1`–`h6` in the raw HTML nor in the rendered DOM (`300`). |
| **FAIL** | One or more headings break the outline | A heading more than one level deeper than the previous one (`400`); a heading nested inside another heading (`401`). |
| **ERROR** | The auditor could not read the page | Fetch failure or non-2xx answer (`500`); browser render failure (`501`); site blocked 37AuditsBot (`590`). |

## Risks and considerations

### Why the outline matters

* Screen reader users typically press a key to move from heading to heading and read the level of each one; a jump from `h2` to `h4` sounds like a missing `h3` and sends them looking for content that is not there.
* WCAG success criterion 1.3.1 (Info and Relationships) expects headings to convey the structure of the page, and heading levels are the main way to do it.
* Search engines use headings to understand what each section covers; a page without any is harder to summarize and to rank for a specific topic.

### Document order, not DOM depth

* The level check follows the order in which headings appear in the page, which is also the order assistive technologies read them. Where a heading sits in the DOM does not matter for that rule, only which heading came before it.
* Going back up any number of levels (an `h4` followed by an `h2`) is fine: that opens a new section. Only going down more than one level fails.
* Only the first jump after a given heading is reported; a page with several jumps gets one check per jump, each naming the heading that caused it.

### JavaScript-rendered pages

* The browser render only runs when the raw HTML has no heading. A page whose server-side HTML already has headings is judged on that HTML, even when JavaScript adds more headings later.
* Rendering waits for the network to go idle plus a short grace period; headings that appear after a slow request or on scroll may be missed, which can turn into a `300` warning for a page that does have headings once fully loaded.

### What this auditor does not check

* Whether the page has exactly one `h1`, or whether the first heading is an `h1`.
* The wording of the headings, their length, or whether they describe the content below them.
* Elements that look like headings but are not (`<div class="title">`, `role="heading"` with `aria-level`).
* Pages beyond the audited URL.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.