When working with SPAs (like React or Angular apps), hosting them via serverless solutions like a CDN is a great way to ensure high scalability at a low cost. At Ensolvers, we have several apps that are served this way via AWS infrastructure using CloudFront. How we do this? By following the rules below.
So, let's assume we have an app written in React, which is bundled and served in https://mydomain.com/home - so, basically, we have a /home path in our app. Then, when a consumer tries to access this URL, the following happens:
That sounds like a great solution, but the inherent high availability and low cost comes with some drawbacks. Let's talk about one: SEO Optimization.
As we've said previously we are serving the same /index.html content for any path requested if that content is not found. That means: the same meta-content is served for all pages in the app. For SEO we want each particular path/page to have their own <title> and OpenGraph <meta> tags, among other features. How do we address this issue?
There is no unique solution for this. We've implemented several solutions depending on the requirements of each customer. Most of the solutions imply to generate an optimized version of the contents of the index.html page in different ways.
If we have a well-known, reduced set of pages that we need to be SEO-optimized, we can run a script that takes the index.html and applies the corresponding transformations. We do this quite simply by using Jsoup Java library for DOM manipulation. These scripts consist of a basic app that is run after the bundling but before the app uploads and generates the optimized version for a particular route. For instance,
will take the file located in index.html and will generate the optimized version called /home, by reading a specific optimization DSL spec file designed by us and called optimizations.json. This file contains the HTML tags that need to be added/modified.
Page post-processing and/or pre-rendering is a pretty common technique in content management apps. In this case, we apply the same code that we used in Solution 1, but dynamically when some domain object changes and, as a result, we need to optimize some particular path in which it is shown. It consists in:
This approach requires a bit of simplification in the index.html structure to avoid re-generating all pages when the index.html content or structure changes, but that can be described in a different thread.
AWS CloudFront provides a very powerful feature that is the ability to introduce dynamic code via Lambda@Edge. While this has some limits (for instance, function size has to be no more than 1 MB), it's very useful to introduce lightweight behavior.
In this case, we've implemented two approaches so far:
In this note we've described different ways in which we can implement SEO while preserving all the advantages of serverless frontend app serving. The techniques here described (used in different projects at Ensolvers) can be applied to other fields as well, like request queueing, caching optimization, etc.