agentspeed.
← all fixes
Issue remediation

Poor heading structure

No H1, multiple H1s, or skipped levels, so agents can't build an outline of your page.

failed checkcontent.text_ratiomarkup.title
What this means

HTML headings (<h1> through <h6>) form a tree that describes how your page is organised: an H1 is the page topic, H2s are top-level sections, H3s are sub-sections, and so on. Agents read this tree to understand what the page covers and where to find specific information. "Poor heading structure" means the tree is broken: no H1 at all, multiple competing H1s, or jumping from H1 straight to H4 without the levels in between.

Why it matters for AI agents

Agents extract structure before content. They look for the H1 to label the page topic, then walk H2s to enumerate sections, then drill into H3s when answering specific questions. A page with no H1 has no topic from the agent's perspective. A page with five H1s ("About", "Products", "Reviews", "Contact", "Blog") looks like five separate pages stitched together, and the agent does not know which one is the answer. Clean heading structure makes you findable; broken structure makes you invisible.

Business impact

Heading structure is the cheapest readability fix on this list: it's pure HTML, no infrastructure changes, fixable in an afternoon. The payoff: agents start citing specific sections of long pages ("according to Acme's seasoning guide…"), FAQ schema starts working (it depends on H2/H3 to delineate questions), and accessibility scores improve as a side-effect. Skipping this fix costs every long-form page on your site.

How to check manually

Open the page and run the following in your browser console: `Array.from(document.querySelectorAll("h1, h2, h3, h4, h5, h6")).map(h => h.tagName + ": " + h.textContent.trim().slice(0, 60))`. Read the output as an outline. There should be exactly one H1, and the levels should never skip (H1 → H2 → H3, never H1 → H3). If the outline is empty, has multiple H1s, or has gaps, the structure is broken.

The exact fix

One <h1> per page, naming the page topic. Use <h2> for top-level sections, <h3> for sub-sections under each H2, <h4> for further nesting. Don't pick heading levels for visual styling; pick them for semantics, then style with CSS. If you need a small heading visually, use a class, not <h6>.

Copy-paste snippet
Reference snippethtmlarticle-page.html
<article>
  <h1>How to season cast iron — a guide</h1>
  <p>Seasoning is the polymerised oil layer that gives cast iron its non-stick surface…</p>

  <h2>What you'll need</h2>
  <p>You need a cast-iron pan, oil with a high smoke point, and an oven.</p>
  <h3>Choosing an oil</h3>
  <p>Flax oil is traditional, grapeseed is modern, vegetable oil is fine.</p>
  <h3>Choosing a pan</h3>
  <p>New, stripped, or rusted — all three follow the same process.</p>

  <h2>The process</h2>
  <h3>Step 1 — strip the surface</h3>
  <p>If the pan is rusted or has old coating, scrub down to bare iron.</p>
  <h3>Step 2 — apply a thin coat</h3>
  <p>Use a paper towel to spread oil; less is more.</p>
  <h3>Step 3 — bake</h3>
  <p>Inverted, 450°F, 1 hour. Repeat 3–6 times for a deep finish.</p>

  <h2>Common mistakes</h2>
  <p>Too much oil pools and turns sticky. Too little leaves bare spots.</p>
</article>
Platform-specific implementations

The reference snippet above is platform-agnostic. Below are the concrete steps for the four most common stacks plus a generic fallback for any other host.

Next.js

In React/Next.js components, the heading tags are just JSX (<h1>, <h2>, …). The trap is reusing components across pages and ending up with no H1 anywhere, since every page is wrapped in <Layout> which ships an <h2 class="page-header">. Audit your top-level page.tsx files: each should render exactly one <h1> and the rest should be <h2>/<h3>. Avoid heading components that auto-pick a level; pick explicitly.

Next.jstsxapp/blog/[slug]/page.tsx
import { getPost } from '@/lib/posts';

export default async function BlogPost({
  params,
}: {
  params: Promise<{ slug: string }>;
}) {
  const { slug } = await params;
  const post = await getPost(slug);
  return (
    <article>
      <h1>{post.title}</h1>
      {post.sections.map((section) => (
        <section key={section.id}>
          <h2>{section.heading}</h2>
          {section.body}
          {section.subSections?.map((sub) => (
            <div key={sub.id}>
              <h3>{sub.heading}</h3>
              {sub.body}
            </div>
          ))}
        </section>
      ))}
    </article>
  );
}

WordPress

WordPress's classic editor uses <h1> for the post title and lets authors choose H2–H6 in the body. The Block Editor (Gutenberg) is similar: the post title block is the H1, and Heading blocks default to H2. Two common breakages: theme builders that wrap everything in <h2> for SEO-by-mistake reasons, and authors who skip levels for visual reasons. Audit your theme's single.php / the_title() output and your editor's heading-level defaults.

WordPressphpsingle.php
<?php get_header(); ?>

<article>
  <header>
    <h1><?php the_title(); ?></h1>
    <p class="byline">By <?php the_author(); ?> · <?php echo get_the_date(); ?></p>
  </header>

  <?php // the_content() outputs author-chosen H2s, H3s, etc. ?>
  <?php the_content(); ?>
</article>

<?php get_footer(); ?>

Shopify

Liquid templates emit headings the same way HTML does. Shopify's default Dawn theme uses <h1> for product titles and the page title; collection sections often use <h2>. The common breakage is theme customisations that swap <h1> for <h2> "for ranking reasons." Don't. Open Edit code and grep for <h1; every product, collection, page, and article template should emit exactly one. For long descriptions, encourage merchants to use the rich-text editor's heading dropdown rather than bolding text.

Shopifyliquidsections/main-product.liquid
<article class="product">
  <h1 class="product__title">{{ product.title }}</h1>
  <p class="product__price">{{ product.price | money }}</p>

  <section class="product__description">
    <h2>About this product</h2>
    {{ product.description }}
    {% comment %}
      product.description is rich-text; the merchant authors H2/H3
      headings inside it via the Shopify admin editor.
    {% endcomment %}
  </section>

  <section>
    <h2>Specifications</h2>
    {% render 'product-specs', product: product %}
  </section>
</article>

Webflow

Webflow Designer has explicit heading-level controls in the element settings; every text element can be set to H1, H2, …, H6, or paragraph. Audit your templates: the page should have exactly one H1 (typically the page title or hero copy), and all other headings should be H2 or below. CMS Collection items default to H2; promote one to H1 on detail templates and demote anything that competes with it on list templates.

WebflowtxtWebflow Designer → Element settings → Tag
Hero headline → H1
Section titles → H2
Sub-section titles → H3
Card titles inside a list → H4 (or styled paragraph if visual hierarchy is purely cosmetic)

Static HTML / any stack

Hand-author the heading tree in your HTML or markdown. Markdown's # / ## / ### map directly to H1 / H2 / H3. Most static-site generators preserve the tree; the typical breakage is layouts that hard-code an <h1>Site Name</h1> in the header so every page ends up with two H1s. Demote the site-name heading to <p> or use CSS to style it without an H tag.

Static HTML / any stackmdcontent/blog/seasoning-guide.md
# How to season cast iron — a guide

Seasoning is the polymerised oil layer…

## What you'll need

You need a cast-iron pan, oil, and an oven.

### Choosing an oil

Flax oil is traditional…

### Choosing a pan

New, stripped, or rusted…

## The process

### Step 1 — strip the surface

If the pan is rusted…
Common mistakes
How to re-test

Run the console outline check above and confirm: exactly one H1, no skipped levels, headings are descriptive. Run lighthouse → Accessibility, and heading-related warnings should be gone. Run a fresh AgentSpeed scan; the readability score will move up because heading-derived signals (text-ratio, structure) improve together.

FAQ
Can I have more than one <h1> with HTML5 sectioning elements?

The HTML5 spec technically allows multiple H1s inside <article> / <section>, but agents and screen readers do not implement the sectioning algorithm consistently. The portable rule is one H1 per page. Use H2 for everything else.

Does the H1 have to match the <title>?

No, but they should describe the same thing. <title> is for the browser tab and external citations; <h1> is for the visible page top. They can differ in tone: a blog post title might be playful in <h1> ("How I learned to stop worrying and love cast iron") and informational in <title> ("Cast iron seasoning guide | Acme").

What about heading levels inside reusable components (cards, sidebars)?

Use <h2> or <h3> for the component label depending on where it sits in the page outline. Avoid components that hard-code <h1>; pass the level in as a prop if the component is reused at different depths.

Verify on your site
Run a scan

Free scan, no signup. The score page is permanent and citable, so you can link it from your team's remediation ticket alongside this guide for “Poor heading structure”.

Run a free scan →See all fixes →