Validates HTML heading hierarchy and structure
3 of 3 free audits left today
See what Heading Structure finds on your site. No sign-up needed.
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.
Parse β reads every h1βh6 element from the HTML in document order.
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.
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).
Aggregate β FAIL wins over WARNING; when every rule passes the audit is SUCCESS (200) and the message counts the headings per level.
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 |
|---|---|---|---|---|
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. |
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. |
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. |
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). | β | β |
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}. | β |
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}. | β |
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. | While 37AuditsBot is blocked this audit cannot look at the site, so problems on it go unreported. |
| 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). |
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.
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.
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.
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, a developer documentation platform.
Add this auditor to your monitoring setup and start identifying issues on your websites today.