H1 tag and heading hierarchy: what matters, and how to check

Short answer

The H1 is the page's main heading: the one line that says what the page is about. Give every page one clear, visible H1, usually close to the title tag. Use H2 to H6 for sections beneath it, in order, without jumping levels. Google says heading order doesn't matter for Search and there's no ideal number of headings, but a clear main heading helps it pick your title, and a tidy outline is how screen reader users find their way around a page.

What the H1 is for

The <h1> is the headline at the top of the content, like the title of a chapter. It tells visitors they're in the right place, gives screen reader users the page's topic, and is one of the places Google looks when it writes the title link for search results.

It's not the same as the title tag. The <title> is what shows in the browser tab and search results; the H1 is what shows on the page. They usually say similar things, and Google uses the main heading as one of the sources when it creates a title link (see title tag length).

What Google says about headings

Here's what Google actually says:

  • Order doesn't affect Search. Google's starter guide says it doesn't matter if headings are used out of order, though order helps screen readers.
  • There's no ideal number. Google says there's no magical, ideal amount of headings a page should have.
  • A clear main heading helps titles. Google's title link guidance recommends one prominent main heading, such as the first visible <h1>, that stands out from the rest of the page.

So treat skipped levels as an accessibility and clarity fix, not a ranking fix. A missing or empty H1 matters more: the page loses its clearest statement of what it's about.

A good heading outline

Headings form an outline of the page, like a table of contents. Each level sits inside the one above it:

Going back up is fine: from an H3 to the next H2 starts a new section. It's jumping down more than one level, H2 straight to H4, that leaves a gap. The W3C says to avoid that where possible.

Skips usually happen because a heading level was chosen for its size, not its place. If an H3 is too big for a sidebar, change its size with CSS, not its level.

The common problems

No H1 Often the page's title is styled text (a <div> or <p>) rather than a heading. An H1 added only by JavaScript is fine for Google, which renders pages, but invisible to most AI crawlers; see JavaScript SEO.
An empty H1 An H1 around a logo image with no alt text, or one a template fills from a field that's blank. An H1 holding a logo with good alt text counts as having text, but a site logo rarely describes an inner page, so it's usually the wrong main heading there.
More than one H1 Not wrong in itself, but often a sign of a theme that makes the logo, the site name or every card title an H1. Check one heading is clearly the main one.
Skipped levels An H4 straight after an H2, usually chosen for its look. Fix the level, then style it.
Headings for styling Bold text made into a heading just to look big, or real section headings that are only bold paragraphs. Both scramble the outline.

How to check by hand

1Print the outline 30 seconds

Open DevTools (F12), go to the Console and paste:

// Prints the heading outline a screen reader meets, and flags missing, empty or multiple H1s and skipped levels
const all = [...document.querySelectorAll('h1, h2, h3, h4, h5, h6, [role="heading"][aria-level]')];
const shown = h => h.checkVisibility({ visibilityProperty: true }) && !h.closest('[aria-hidden="true"]');
const heads = all.filter(shown);
const nameOf = h => {
  const ids = (h.getAttribute('aria-labelledby') || '').split(/\s+/).filter(Boolean);
  const byIds = ids.map(id => document.getElementById(id)?.textContent || '').join(' ');
  const alts = [...h.querySelectorAll('img[alt]')].map(img => img.alt).join(' ');
  return (byIds.trim() || (h.getAttribute('aria-label') || '').trim() || h.innerText + ' ' + alts).replace(/\s+/g, ' ').trim();
};
let prev = 0;
const rows = heads.map(h => {
  const level = Number(h.getAttribute('aria-level')) || Number(h.tagName[1]);
  const text = nameOf(h);
  const notes = [];
  if (!text) notes.push('EMPTY');
  if (prev && level > prev + 1) notes.push('SKIPS from H' + prev);
  if (!/^H\d$/.test(h.tagName)) notes.push('role="heading"');
  prev = level;
  return { heading: '  '.repeat(level - 1) + 'H' + level + '  ' + text.slice(0, 60), note: notes.join(', ') };
});
console.table(rows);
const h1s = heads.filter(h => h.tagName === 'H1').length;
console.log(h1s === 0 ? 'No visible H1' : h1s > 1 ? h1s + ' visible H1s: check one is clearly the main heading' : 'One visible H1');
const hidden = all.filter(h => !shown(h) && /^H\d$/.test(h.tagName)).length;
if (hidden) console.log(hidden + ' hidden heading(s) not listed. SEOSnip leaves these out too.');

It prints every visible heading, indented by level, marks empty headings and levels that skip, and says how many H1s there are.

It lists the headings a screen reader meets: anything hidden with CSS, the hidden attribute or aria-hidden is left out, and it tells you how many it left out. SEOSnip leaves the same hidden headings out, so a hidden H1 in a closed mobile menu isn't counted as a second H1. Headings hidden only visually (a "visually hidden" class) are still listed, because screen readers read them. Headings inside shadow DOM aren't listed.

2Look at the accessibility tree 1 minute

In Chrome DevTools, open the Elements panel, then the Accessibility pane, turn on Enable full-page accessibility tree, click Reload DevTools, then click the accessibility tree button at the top right of the Elements panel. Headings show as "heading" with their level, in page order.

3Navigate by headings 3 minutes

With a screen reader on, jump from heading to heading (the H key in NVDA and JAWS; in VoiceOver on a Mac, open the rotor with VO+U and choose Headings). If the list doesn't read like a sensible table of contents, the outline needs work.

How crawlers report this

If a crawl sent you here, these are the labels to look for. Our guide to Screaming Frog and Sitebulb issues covers other common ones.

Screaming FrogSitebulbWhat it means
H1: Missing<h1> tag is missingThe page has no main heading.
H1: MultipleMultiple <h1> tagsMore than one H1. Often harmless, but worth a look on key templates.

Doing this on every page?

SEOSnip checks headings for you

Open the extension on any page and it reads every heading. You get:

  • A missing H1, or an empty one. An H1 with a logo that has alt text, or an aria-label, counts as having text
  • More than one H1, listed for review rather than as an error
  • Each place the content's outline jumps down more than one level, with the headings around it and the jump labelled (for example H2 → H4, skips H3). Headings in the footer, menus, sidebars and popups are left out of this check, because they rarely follow the content's levels
  • Headings hidden from everyone, such as an H1 in a closed menu or modal, are left out of the count and the outline
  • An H1 in the server HTML that JavaScript changes, through the server vs rendered check
  • A ticket with that evidence, and a "Check if fixed" button that re-runs the check