What is prerendering?
The problem
A single-page application (SPA) ships a near-empty HTML document and builds the page in the browser with JavaScript:
<body>
<div id="root"></div>
<script src="/assets/app.js"></script>
</body>A real browser runs app.js, fetches data, and renders the content. Many crawlers and scrapers do not. When they request the page they see an empty <div id="root"></div> - no headings, no text, no links, no meta tags.
This affects:
- Search engines that do not execute JavaScript, or execute it on a delay.
- Social media scrapers (Facebook, X/Twitter, LinkedIn, Slack, WhatsApp, Discord) that read
<meta property="og:*">tags but never run scripts, so link previews come out blank. - Other automated consumers - monitoring, archiving, link checkers.
The solution: prerendering
Prerendering (also called dynamic rendering) puts a renderer between your app and the crawler:
- A bot requests
https://your-app.com/products/42. - Your reverse proxy detects the bot and forwards the request to the prerender service instead of your static files.
- The prerender service loads the page in headless Chromium, waits for the client-side rendering to finish, and captures the resulting HTML.
- That fully-rendered HTML is returned to the bot.
Human visitors are unaffected - they still get the normal SPA and its client-side navigation.
┌──────────────────────────┐
Bot request ─────────▶│ Reverse proxy │
│ (is this a crawler?) │
└───────┬──────────┬───────┘
yes │ │ no
▼ ▼
┌──────────────────┐ ┌──────────────┐
│ headless-prerender│ │ static files │
│ headless Chrome │ │ / your CDN │
└────────┬─────────┘ └──────────────┘
│
fully-rendered HTMLIs dynamic rendering still recommended?
Google now renders JavaScript for most pages and describes dynamic rendering as a workaround rather than a long-term recommendation. It remains useful when:
- Your content must be crawlable by engines and bots that do not run JavaScript (many do not).
- You need reliable social link previews immediately after publishing.
- Server-side rendering (SSR) is not practical for your stack right now.
If you can adopt SSR or static generation, that is usually the better long-term answer. headless-prerender exists for the cases where you cannot.
How it differs from SSR
| SSR | Prerendering | |
|---|---|---|
| Where HTML is built | Your server, per request | A headless browser, on demand |
| App code changes | Yes - app must run server-side | None |
| Serves | Every visitor | Usually only bots |
| Freshness | Real-time | Real-time, or cached |