What it is, How it Works, and Caveats#

"It" being our server-side rendering (sometimes referred to as pre-rendering or dynamic rendering) for JavaScript single-page apps. We also have documentation for capturing screenshots, printing webpages to PDFs, and scraping.

ELI5 Value proposition: make JavaScript single-page apps either or both 1) readable for bots (search indexers and open graph link previews, 2) faster for humans

ELI5 Why this is necessary: because JavaScript single-page apps are, by their nature, JavaScript as opposed to pure HTML. That means the existing world of web crawlers that only parse HTML can't crawl your app. It also takes browsers longer to render ("time to first meaningful paint") a JavaScript than it does to render HTML (e.g. 800ms instead of 200ms). Note: crawlers improve over time so they may eventually parse JavaScript and computers keep getting faster so the render improvements will decline over a long enough time horizon.

A non-exclusive list of synonymous terms to "server-side rendering" used interchangeably below:

  • pre-rendering, prerendering
  • dynamic rendering
  • isomorphic rendering
  • universal rendering

How it works TL;DR: human or bot visits your site, but instead of receiving your original index.html (that references your .js files but otherwise has no HTML content), they receive the server-side rendered version of your JavaScript app, which includes all the HTML, plus original JavaScript. If the client is a web browser (as opposed to a simple HTML-only crawler), that browser parses the server-side rendered HTML and renders/paints the screen (quicker than it would a JavaScript app), then parses and loads the JavaScript single-page app which will naturally (sometimes with an undesirable screen flash) take over the DOM after it finishes loading

Optionally configure whether the serialized HTML + JS is served to humans or bots (or your own list of bot user-agents) or both humans and bots.

Optionally configure whether only the meta tags get serialized ("meta only") so open graph link previews work.

Headless-Render-API.com uses a headless Chrome browser to "server-side render" a JavaScript single page app (SPA). It's a somewhat generic and crude alternative to doing this manually (loading your SPA from a Node.js server and serving the compiled HTML) but it works "good enough" for most apps.

You can also think of "server-side rendering" as simply serializing the DOM into HTML for a JavaScript SPA and serving that from a web server so the browser doesn't have to do this serialization on the first render (so the first page load is faster). The serialized HTML is the same thing you'd see while inspecting an app from Chrome's dev tools.

And this is exactly what headless-render-api.com will do for you: serialize your SPA into HTML and serve it to your users (and/or bots) from most web servers.

note: Headless-Render-API.com was previously known as Prerender.cloud (rebranded 2022-05-01), so please excuse any previous references to prerender.cloud you may see in the docs and libraries.

Here's a flow of what it looks like when fully configured:

  1. random human or bot visits your JavaScript website
  2. your webserver forwards the request to service.headless-render-api.com (this request typically takes ~1.5 seconds, so caching is important, and simpler apps render much faster) - note: "local caching" is done at this step
  3. service.headless-render-api.com receives the request and makes a new request back your webserver
  4. your webserver receives another request but since the user agent matches /prerendercloud/ your webserver decides to serve the original (non server-side rendered) index.html instead of forwarding the request to service.headless-render-api.com
  5. service.headless-render-api.com receives the response from your webserver
  6. service.headless-render-api.com waits for all in-flight XHR/AJAX/WebSockets to finish
  7. service.headless-render-api.com waits for DOM to finish rendering
  8. service.headless-render-api.com serializes the DOM (think: Chrome's dev tools inspect or what "view-source" would show for a non SPA)
  9. if using React v14 or v15, service.headless-render-api.com computes a checksum for the DOM
  10. your webserver receives the new serialized DOM response from service.headless-render-api.com and serves that instead of your original index.html
  11. the browser now renders the server-side generated HTML within a few hundred milliseconds (as opposed to several seconds)
  12. the JavaScript app, meanwhile, is starting/booting and when it's finished, it replaces the server-side rendered DOM with the client-driven DOM - this is the server client transition

Should you bother to server-side render your JavaScript single-page app?#

Reality check, what are we doing here? Over the long run, as computers get faster and cheaper, pre-rendering (server-side rendering) should become unnecessary (I'm surprised it hasn't happened yet - how hard is it for the major services to run their own headless crawling/parsing service). But for now, sadly, they're still necessary because our favorite apps like twitterbot, telegram, etc. still can't parse JavaScript apps and even the mighty googlebot sometimes decides that it won't parse a JavaScript app. So here's how to think about whether you need this.

First, the basics:

  • single-page app means: e.g. React app, Angular, Vue, (i.e. JavaScript app with pushstate URLs)
  • server-side rendering and/or pre-rendering are used interchangeably, they mean the same thing here

Now, consider whether your single-page app has problems that can be fixed by these use-cases:

The 2 primary use-cases of server-side rendering these apps are:

  1. indexability of your site (search engines like googlebot, or twitterbot/telegram link previews not parsing your app)
  2. render performance (appears faster to user viewing app in browser)

If you do nothing, and simply deploy a React app to a CDN like S3/Cloudfront, Cloudflare pages, Netlify, etc, then your app will appear to work fine for normal browser usage but won't correctly show "open graph" link previews when pasting the URL of the app into social or chat apps. The other less obvious issue is indexability and SEO. All you can do here is trial/error: Try not server-side rendering or pre-rendering for a month and see how the search results look. If they look fine, then I would not bother with server-side rendering. (Well, you may still use it for things like user-agents matching twitterbot/slackbot open graph link previews but not googlebot). Then try the next month (no scientific basis for using a month) with server-side rendering enabled (can use this headless-render-api or any other solution that serializes your app to HTML for googlebot).

Render performance is about reducing the time it takes for the "first meaningful paint". If you server-side render, you can get this down to 250ms for a typical React app. Without it, it could be 3x slower (800ms but really depends on how bloated your app is). It may seem trivial to care about sub second differences but when it comes to showing content on a browser screen you can definitely feel the difference. The problem here is whether your app can cleanly rehydrate HTML serialized from a headless browser. React works pretty well but even then you may still see a quick immediate render and then a brief flash while the JavaScript app retakes control over the div. If you can tolerate the flash, so be it. If you're a perfectionist who notices the flash, you won't like it. I personally wouldn't serve server-side rendered content to a human visitor if it caused a flash.

Caveats with this product#

OK, so you've read the above sections, decided you need or want to do some kind of server-side rendering and now deciding whether you should use Headless-Render-API.com for this.

  1. does you SPA framework have a built-in server-side rendering solution? Is it feasibile to implement?
  2. if your SPA framework does not have an SSR solution or it's too difficult to implement then yes, you should consider an option like Headless-Render-API (or any other prerendering service or run headless chromium yourself for server-side rendering)
  3. cost/billing and integration time

Pitfalls to be wary of:

  1. Local caching for cost savings: other pre-rendering services encourage you to forward all traffic through their servers and charging you for it. We encourage you to use local cache so you're not making duplicate requests for pages already rendered (our node.js integration refers to this as middleware cache)
  2. Whitelisting URLs and/or query parameters for cost savings. Worst case scenario for unexpected billing is malicious bots visting hundreds or even thousands of random URLs - this is rare but I've seen it happen on large sites. If you configure all traffic or even just all bot traffic to flow through Headless-Render-API, this is where it gets unexpectly expensive. The solution here is to configure your websever to only forward valid URLs. Our node.js integration (both the middleware and server) have a config for this: whitelistPaths (_whitelist.js file for server) and whitelistQueryParams
  3. State transition (theoretically we have both an automated and a simple manual solution to this, but in practice it can take some time to get working). If you can't get this working quickly, or it just doesn't come out right, then you could just ignore it and only server-side render for bots or even only server-side render (pre-render) the "meta only" so meta tags and open graph still work.
  4. Ad networks will slow down the server-side render and shouldn't be rendered for a server-side rendering use-case (we block a lot of these), but you should also disable any ads or things like Google Maps widgets (anything not part of your SPA framework) by wrapping them in if (!window.prerenderCloudIsServerSideRendering) { // render ads, etc. }