Skip to content

Instantly share code, notes, and snippets.

@kmamykin
Last active February 13, 2017 18:23
Show Gist options
  • Save kmamykin/48c41ae19d571c2c99370334d7de56da to your computer and use it in GitHub Desktop.
Save kmamykin/48c41ae19d571c2c99370334d7de56da to your computer and use it in GitHub Desktop.
Site pre-rendering options

Introduction

What is prerendering? Pre-rendering a JavaScript heavy website refers to the process of using a proxy that loads the requested page and runs all JavaScript on the page, and then returning the content of the rendered page as the response to the page's request. The advantages of doing so are twofold:

Performance

Perfomance of the site is greatly improved as the browser is able to render the page without waiting for external scripts to load and external data api requests to finish. In Brickwork client site that means requesting a store page returns HTML that is already rendered with all the data about the store.

After the initial page HTML is rendered from the response, the browser asycronously request JavaScript scripts that load in the background, query the API for data and re-render the page in browser, adding interactivity to the page. Ideally the page pre-rendered on the server/proxy and the page rendered in the user's browser will be exactly the same (given the api data hasn't changed). But even if the api data is different, the user will initially see slightly older data, which will be replaced once page's JavaScript and data load.

SEO

Prerendering is also beneficial for all kinds of bots. The bots will see plain HTML, with JSON-LD and other meta-data present, making the page compatible with a wide range of bots (Slack, Facebook, Twitter, Yandex, Google etc).

While Google bot has the ability to render JavaScript while crawling, one must presume there is some kind of timeout that limits the time Google allows the web-page to render.

Prerendering JS sites

Essentially all tech in this space uses either phantom.js engine (webkit based headless browser) or Chrome engine (headless Chrome engine).

Prerender

Prerendered (https://prerender.io) runs a hosted pre-rendering service and open source projects for the server (pre-rendering proxy) https://github.com/prerender/prerender and various middleware libraries that allow piping web-requests through prerenderer proxy. Prerenderer server internally manages phantom.js workers that load and render urls from requests. Using the server is pretty straight-forward:

  1. Request to http://stores.client.com/en/slug hits the server
  2. Server (through prerenderer middleware) proxies the request to prerenderer server, to http://prerenderer.brickworksoftware.com/?url=http://stores.client.com/en/slug
  3. prerenderer request http://stores.client.com/en/slug (with different headers, not to get to infinite loop)
  4. Server returns HTML that contains lots of JS scripts
  5. prerenderer drives phantom.js to fully render that html and execute all JS scripts that dynamically modify the page.
  6. prerenderer returns the resulting html to the server, which returns it to the browser (or bot)

In Brickworks case this can be architected two ways:

  1. Configure AWS CloudFront distribution to use prerenderer proxy as the origin server. Browser <-> CloudFront <-> prerenderer <-> Rails app. No modification to Rails app required.
  2. Configure Rails app to use pre-renderer through middleware. Browser <-> CloudFront <-> Rails App <-> prerenderer

Prerenderer can be deployed either as AWS Beanstack application, or Heroku appliation (its a simple node.js express app).

Questions to be answered

  • (Done) Does this architecture with pre-rendering actualy work? What are the unknown unknown? Need to implement a very simple prototype with prerenderer on heroku
  • How exactly to plugin prerenderer in the CloudFront <-> app interaction? Can we configure CloudFront to proxy through renderer passing right parameters? Can we use Lambda@Edge to hook into CloudFront request life-cycle?
  • How to host prerenderer to make it scaleable? Beanstock/Heroku/Serverless Lambda?
  • Can pre-renderer be used by Brickwork clients should they decide to host Asiago JS widgets on their site?

CloudFront setup

  • Distribution needs to be configured to pass Host header to the origin, so the rails app knows which client to render.

Making prerenderer render asiago based site

  • Phantom.js does not support WebGL. WebGL is used by SearchMap component which used react-mapbox-gl module. Need to test if WebGL is awailable and conditionally render react-mapbox-gl map.
  • Running prerenderer server locally on OSX fails, ended up running it in docker container (https://nodejs.org/en/docs/guides/nodejs-docker-webapp/)

Making prerenderer run in Docker

  • docker build -t kmamyk/prerender .
  • Add an unused address (e.g. 10.200.10.1) to lo0 interface to be accessed from the container (see https://docs.docker.com/docker-for-mac/networking/) sudo ifconfig lo0 alias 10.200.10.1/24
  • docker run --rm --name prerender -p 49160:3000 --add-host="host.local:10.200.10.1" -it kmamyk/prerender
  • curl "http://localhost:49160/http://host.local:3000/" - use renderer to pre-render http://host.local:3000/
  • docker stop prerender
  • Debugging notes:
    • echo "Hello World" | nc -l 10.200.10.1 4321 makes nc listen on IP and port, and return Hello World to any requests.

Issues to come back to

References

Serverless prerenderer

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment