# HTML Rendering

> How to fix HTML rendering for agents that do not run JavaScript: SPA-shell symptoms, semantic landmarks, a single H1, alt coverage, and a noscript fallback.

This check carries 11% of the score.

*These remediation guides are written in English.*

<a id="no-html"></a>

## Could not fetch homepage HTML

ax-audit could not retrieve any HTML from the root URL. The server returned an empty body, a non-2xx status, or timed out.

- Verify `curl -I https://your-site.com` returns `200 OK`.
- Confirm your CDN / firewall isn't blocking the `ax-audit/<version>` User-Agent.
- Make sure the homepage doesn't require an authenticated session.

---

<a id="js-shell"></a>

## No visible text content (JS-only shell)

The static HTML response contains no readable text, typical of a client-rendered SPA where the entire UI is built by JavaScript at runtime.

Pick the rendering strategy that matches your stack:

- **Next.js / Remix / Nuxt:** use SSR or SSG for top-level routes (`getServerSideProps`, `getStaticProps`, route loaders).
- **Astro:** already SSR by default; verify you're not in `output: 'static'` with all components flagged `client:only`.
- **SvelteKit:** ensure `+page.server.js` or `+page.js` renders text content.
- **CRA / Vite SPA:** add a prerender step (e.g. `react-snap`, `vite-plugin-prerender`) or migrate to a framework with built-in SSR.

> **How to verify**
>
> Run
>
> curl -s https://your-site.com | wc -w
>
> . If the word count is < 50, agents that don't execute JS will see almost nothing.

---

<a id="sparse-content"></a>

## Sparse server-rendered content

Your homepage has < 80 words or < 500 characters of visible text in the static HTML. Some content is rendered server-side, but not enough for an agent to summarize the page.

- Inline at least the hero copy, primary headings, and a paragraph of context.
- If you ship a marketing page above-the-fold and a JS dashboard below-the-fold, server-render the marketing portion.
- Avoid hydration patterns that strip text from the SSR output and re-inject it client-side.

---

<a id="low-ratio"></a>

## Low text-to-markup ratio

Less than 5% of your HTML response is visible text; the rest is markup, scripts, and inline styles. This is a classic SPA-shell symptom.

- Move large inline JSON state blobs (`__NEXT_DATA__`, Apollo cache, Redux state) to a deferred fetch when possible.
- Lazy-load fonts, analytics, and third-party scripts. Defer non-critical bundles.
- Server-render more text content above-the-fold.

---

<a id="empty-spa"></a>

## Empty SPA mount point detected

ax-audit found an empty mount node like `<div id="root"></div>`, `<div id="__next"></div>`, or `<div id="app"></div>` with no children. That means the entire page is rendered client-side.

```
<!-- BAD: agents see nothing -->
<div id="root"></div>
<script src="/bundle.js"></script>

<!-- GOOD: SSR fills the mount point -->
<div id="root">
  <main>
    <h1>Site Title</h1>
    <p>Server-rendered hero copy that agents and search crawlers can read.</p>
  </main>
</div>
<script src="/bundle.js"></script>
```

Enable SSR or SSG in your framework. The mount node should already contain the rendered content before hydration runs.

---

<a id="no-landmarks"></a>

## No semantic landmarks

None of `<main>`, `<article>`, `<section>`, `<header>`, `<footer>`, or `<nav>` appear in your HTML. AI agents use these tags to identify the primary content region and discard chrome.

```
<body>
  <header><nav>...</nav></header>
  <main>
    <article>
      <h1>Page topic</h1>
      <p>Primary content...</p>
    </article>
    <section><h2>Secondary section</h2></section>
  </main>
  <footer>...</footer>
</body>
```

---

<a id="few-landmarks"></a>

## Few semantic landmarks

Fewer than three semantic tags found. Replace generic `<div>` wrappers with the appropriate landmark: `<main>` for the primary content, `<header>`, `<nav>`, `<footer>` for site chrome, `<article>` for self-contained content units.

---

<a id="no-h1"></a>

## No <h1> heading

Every page should have a single `<h1>` describing its topic. AI agents and search engines treat the H1 as the strongest topical signal for the page.

```
<main>
  <h1>Customer support knowledge base</h1>
  ...
</main>
```

---

<a id="multiple-h1"></a>

## Multiple <h1> headings

More than one `<h1>` on the page dilutes the topic signal. Demote secondary H1s to `<h2>` so the document outline reflects a clear hierarchy.

---

<a id="empty-h1"></a>

## Empty <h1>

Your `<h1>` tag exists but contains no text, possibly because it's populated by JavaScript or only contains an icon/image. Add meaningful textual content so agents can identify the topic.

```
<!-- BAD -->
<h1><img src="/logo.svg" alt=""></h1>

<!-- GOOD -->
<h1>
  <img src="/logo.svg" alt=""> Acme Corp
</h1>
```

---

<a id="no-noscript"></a>

## Heavy JavaScript without <noscript>

Your page ships more than 15 executable `<script>` tags but provides no `<noscript>` fallback. Agents that don't run JS (and users with JS disabled) will see a blank page.

```
<noscript>
  <p>This site works best with JavaScript enabled.
     For a no-JS overview, see our <a href="/llms.txt">/llms.txt</a>
     or <a href="/for-websites">For websites page</a>.</p>
</noscript>
```

---

<a id="missing-alt"></a>

## Missing alt attributes

Less than 90% of your `<img>` tags include an `alt` attribute. Agents (and assistive tech) rely on alt text to understand images they can't process visually.

- Use a descriptive alt for content images: `alt="Team photo at the 2026 conference"`.
- Use `alt=""` (empty) for decorative images so screen readers and agents skip them cleanly.
- Never omit the attribute entirely.

---

Markdown representation of https://axrush.com/guides/html-rendering
