> ## 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.

# CSR/SSR

> Compares the page rendered with and without JavaScript to tell whether its content is delivered by the server or built in the browser.

<Info>
  **Performance Auditor** — renders the audited page in a headless Chromium twice, with JavaScript enabled and disabled, and compares the size of the two documents to tell server-side rendering (SSR) from client-side rendering (CSR).
</Info>

<CardGroup cols={2}>
  <Card title="Discoverability" icon="magnifying-glass">
    Content that only exists after JavaScript runs is invisible to crawlers, AI assistants and link previews that do not execute scripts.
  </Card>

  <Card title="Perceived speed" icon="gauge-high">
    A server-rendered page shows its content as soon as the HTML arrives; a client-rendered one shows nothing until its scripts download, parse and run.
  </Card>
</CardGroup>

Modern front-end frameworks can deliver a page in two ways. With server-side rendering (or static generation) the HTML sent by the server already contains the content, and JavaScript only adds interactivity. With client-side rendering the server sends an almost empty shell and the browser builds the page from scripts and API calls. The second approach costs time on every visit and hides the content from anything that does not run JavaScript. This auditor measures how much of the page depends on JavaScript by rendering it both ways and comparing the results.

## How it works

1. **Render with JavaScript** — loads the audited URL as 37AuditsBot in a headless Chromium with JavaScript enabled, waits for the network to go idle plus a short grace period, and keeps the rendered HTML. A blocked navigation (HTTP 403, HTTP 429 or a Cloudflare challenge) ends the audit with `590`; no response, or an HTTP status of 400 or more, ends it with `500`. A browser error, after one relaunch on a crash, ends it with `501`.
2. **Render without JavaScript** — loads the same URL in a second context with JavaScript disabled and keeps that HTML too.
3. **Compare** — computes the size difference ratio `|sizeWithJs − sizeWithoutJs| / max(sizeWithJs, sizeWithoutJs)` (two empty documents count as no difference).
4. **Rules** — a ratio above 20% warns (`300`): the content is built in the browser. A ratio of 20% or less is `SUCCESS` (`200`): the content is delivered by the server. The message of either outcome gives both sizes and the ratio.

## 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, `500–599` error.

| CheckCode | Status | When it fires | Recommendation | Why it matters |
| - | - | - | - | - |
| <a id="37A-CsrSsrAuditor-300" />`37A-CsrSsrAuditor-300` | `WARNING` | The documents rendered with and without JavaScript differ in size by more than 20%. | Serve the content of `{url}` in the HTML itself (server-side rendering, static generation or pre-rendering in your framework) so the page delivered without JavaScript carries more than `{sizeWithoutJs}` characters of the `{sizeWithJs}` characters rendered with it and the difference drops below 20.0%. | Crawlers, AI assistants and social previews that do not run JavaScript see a nearly empty page, so client-rendered content is indexed late or not at all, and every visitor waits for the scripts to download and run before seeing anything. |
| <a id="37A-CsrSsrAuditor-200" />`37A-CsrSsrAuditor-200` | `SUCCESS` | The documents rendered with and without JavaScript differ in size by 20% or less. Message gives both sizes and the ratio, for example `{url} renders 48,210 characters with JavaScript and 47,902 characters without, a 0.6% difference within the 20.0% threshold, so its content appears to be server-side rendered (SSR).` | — | — |
| <a id="37A-CsrSsrAuditor-500" />`37A-CsrSsrAuditor-500` | `ERROR` | The JavaScript-enabled navigation got no response from the browser, or answered with an HTTP status of 400 or more (other than a block). | Make sure `{url}` answers with HTTP 2xx, then run the audit again. Last error: `{error}`. | — |
| <a id="37A-CsrSsrAuditor-501" />`37A-CsrSsrAuditor-501` | `ERROR` | The headless browser failed to render the page, 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-CsrSsrAuditor-590" />`37A-CsrSsrAuditor-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 content is delivered by the server | Size difference ratio of 20% or less between the renders with and without JavaScript (`200`). |
| **WARNING** | The content is built in the browser | Size difference ratio above 20% (`300`). |
| **ERROR** | The auditor could not render the page | No response or HTTP status of 400 or more on the JavaScript-enabled render (`500`); browser render failure (`501`); site blocked 37AuditsBot (`590`). |

Every `SUCCESS` and `WARNING` check carries the measured values in `data`: `sizeWithJs`, `sizeWithoutJs`, `sizeDifference` (characters), `ratio`, `threshold` (`0.2`) and `isCsr`.

## Risks and considerations

### Why rendering strategy matters

* Search engines do render JavaScript, but later and with a smaller budget than plain HTML; client-rendered pages are often indexed days after server-rendered ones, or with missing content when a script fails.
* Many AI crawlers, link unfurlers (chat apps, social networks) and accessibility tools read the raw HTML only. A client-rendered page gives them an empty shell.
* Visitors on slow networks or devices wait for every script before the first content appears, which raises Largest Contentful Paint and bounce rates.

### A size heuristic, not a framework detector

* The auditor compares the size of the two rendered documents, nothing else. A page whose scripts add a cookie banner, a chat widget or large tracking markup can cross the 20% threshold while its main content is server-rendered; a client-rendered page that replaces a large placeholder with content of a similar size can stay under it.
* The threshold is a fixed constant (20%) and is not configurable.
* Both renders wait for the network to go idle plus 1.5 seconds; content loaded after that, on scroll or on interaction, is not counted.

### Changes from the previous version of this auditor

* A page whose JavaScript-enabled navigation answers with an HTTP status of 400 or more is no longer rendered and compared as if it were the site: the audit ends with `ERROR` `500`.
* A browser failure that survives the one relaunch is reported as `ERROR` `501` with that code, instead of an error check without a code.
* The verdict thresholds are unchanged: a size difference above 20% still warns, 20% or less still succeeds.

### What this auditor does not check

* Whether the server-rendered HTML is also correct, complete or hydrated without errors.
* Pages beyond the audited URL; a site can mix strategies per route.
* Which framework or rendering mode (SSR, SSG, ISR, streaming) the site uses.


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