Building a CMS (Content Management System)-backed website involves finding a solution to one of the two hardest problems in computer science¹: cache invalidation. (The other hard problem being naming… and off-by-one errors).
Many websites use content-delivery networks (CDNs) to cache content geographically close to their audience. Using a CDN improves website performance (due to lower network latencies) and also reduces the amount of resources needed to serve the website. But it introduces a problem — how can you deliver new content when everything is cached?
This article looks at 3 approaches to solve this problem. Each approach does solve the problem, but there are trade-offs in terms of complexity, cost, and customer experience.
Why build a CMS-backed website in the first place?
The main reason is to allow non technical people (e.g. journalists, content editors) to create content for webpages, without forcing them to write code. CMSs also provide other benefits, such as having a publishing workflow (to ensure only approved-content is published). As a content-driven website grows, the need for a CMS increases.
Approach 1 — Server Side Rendering (SSR)
In this context, SSR means “running a computer program which can return a webpage (HTML) in response to a HTTP request”. That computer program may be setup so that it is *always* running (like a web server), or it can be configured so that it just runs when a HTTP request is received (like a Lambda function). In both cases, the process generates the HTML response dynamically, rather than sending a pre-generated HTML response.
Serving web pages
As the diagram² below shows, when a HTTP request is received by the CDN, the CDN checks if the content it needs is already in its cache. If not, it decides whether the request is for a static asset (which can be sent back quickly), or for some other content which is provided by the web server (or Lambda).
Content coming from the web server takes much more time to retrieve because the SSR program has to build the response in real-time, fetching information from the CMS and/or other data services, then converting that into a HTML string.
Once the CDN receives the response from the web server, it may add it to its cache (so that subsequent requests are much faster). Then it sends the response to the client.
Typically the website is rebuilt every time there is a content or code change. When building the site, static assets are generally stored separately from the SSR code to improve performance (by allowing requests for static assets to be aggressively cached). Configuring the build process and the CDN to support this separation is more complex compared to the other approaches.³
Once the site has been rebuilt, the CDN’s cache is invalidated (cleared) so that the new content can be served. The invalidation process takes a few minutes on AWS CloudFront, but other CDNs (like Netlify) may be faster.
Benefits of SSR
- Smaller build time — pages are not pre-generated and stored.
- Flexibility — SSR allows custom HTML responses for each request. This might be required when the website does not allow direct browser-to-API requests, as these requests could be performed by the web server on behalf of the client. (Though I have to say, this would be a rare situation and the reason for such a limitation seems questionable).
- Efficiency — non-requested content is never generated.
Disadvantages of SSR
- Longer response times — SSR is much slower than serving static content or content from a CDN’s cache. The flexibility comes at the cost of speed (milliseconds for static content vs seconds for SSR).
- User experience — Initial requests for content after cache invalidation are slow (seconds). This is really important and is discussed more in the last section.
- Infrastructure complexity — there is additional complexity in configuring a CDN and Build pipeline to separate & serve the static assets and the SSR code. Also, the web server requires access to the CMS and other data services at runtime.
- More coupled — for example, if the CMS has degraded performance, it affects the serving infrastructure (via the SSR program on the web server which calls the CMS).
- Whole cache is invalidated — whenever any change is made to the content, the entire CDN cache is invalidated. This is wasteful since most of the time only a few pages would actually have changed.
Approach 2 — Static site generation (SSG) with full rebuild
Static site generation is the process of rendering all (or most) of the pages for a website before any HTTP requests are received. The generation process normally occurs as part of a build process.
Serving web pages
When a HTTP request is received, the CDN checks if the content is already in its cache. If not, it fetches the content from the static assets.
Once the CDN receives the static asset, it would add it to its cache (so that subsequent requests are much faster). Then it sends the response to the client.
Note the difference in the number of components involved in this approach compared to the SSR approach.
In contrast to serving web pages, the build process contains more complexity, but not much more than in the SSR approach.
What about dynamic routes?
The SSR approach natively supports dynamic routes (e.g. URLs with parameters in them, such as `/product/123343–343`) by virtue of it being a running program. It can make API requests on behalf of the client and send the responses back.
Dynamic routes are supported by SSG tools by sending the API requests directly from the client. This is commonly called Single Page Application (SPA) mode or client-side rendering (CSR)⁴. This approach is only needed for pages that are not pre-generated by the build step.
Benefits of SSG
- Serving Simplicity — the serving process is much simpler than SSR.
- Speed — since the site has been converted into static assets, it loads very quickly (sub-second) in all conditions as there is no computation step (in contrast to SSR, which is fast only when the content is in the CDN cache).
- Less coupled — if the build process is slow, it does not affect the serving infrastructure. Similarly, if the serving infrastructure is slow, it does not affect the build infrastructure.
Disadvantages of SSG
- Build speed — pre-generating the webpages takes 1-N seconds per-page (where N is dependent on the number of API calls required to fetch the data for the page). Most SSG tools support building pages concurrently to reduce the build time. But build times using SSG would be a few minutes slower than SSR builds.
- Infrastructure complexity — the build infrastructure requires access to the CMS and the other data services.
- Whole cache is invalidated — whenever any change is made to the content, the whole website is rebuilt and the CDN cache is invalidated. This is wasteful since most of the time only a few pages would actually have changed.
Approach 3 — Static site generation (SSG) + incremental rebuild
This approach is the same as the previous approach (SSG), but with an additional build pipeline to which just rebuilds changed-content (rather than the whole website).
In this approach, when the content-change event is received from the CMS (which contains the name of the CMS-page that has changed), a page-build pipeline is started instead of the full-build pipeline. The page-build pipeline determines which page(s) need to be rebuilt, and just builds them. This takes a lot less time to complete because there are fewer steps to execute (e.g. no unit testing) and fewer pages to build during any particular execution. Lastly, the CDN’s cache is invalidated only for the changed-pages.
Benefits of SSG (incremental)
The same as Approach 2, plus:
- Build speed — rebuilding just the changed-pages is much faster than building the entire site (e.g. 2 minutes vs 10 minutes). Since only the content has changed for the page build pipeline, there’s no need to run unit or functional tests again.
- Site speed — a full cache-invalidation happens less frequently, meaning that the site remains very fast even when new content is displayed.
- Lower cost —there are no requests for CMS content at runtime.
Disadvantages of SSG (incremental)
- Infrastructure complexity — the build infrastructure requires access to the CMS and the other data services. Also, this approach needs two different build pipelines.
- Cache invalidation complexity — a mapping is required between CMS content changes and website-paths. This requires more configuration than simply invalidating all website-paths.
Real world comparison
I have been fortunate to have been involved in building commercial websites — sites that serve 10,000+ visitors per hour — using both SSR and SSG approaches. No approach is perfect. But in my experience, building & running a SSR site involves much more effort and complexity than an equivalent SSG approach, and has a big drawback.
As noted earlier, the big drawback to SSR occurs immediately after the site is redeployed and the CDN’s cache is invalidated. The SSR program on the web server instantly becomes very busy, and must try to deliver a HTML response before the CDN or an API gateway times out. This places the SSR process at the mercy of the CMS and API response times. Even if individual APIs are fast, sometimes you will need to call APIs in-sequence, which can still violate the timeout. And if you increase the timeout, you directly impact the customer experience. It’s a big deal.
On the other hand, SSG is able to mitigate API response-time issues because the build process can take as long as it needs (within reason) to finish building then deploying the site. Approach 3 improves on the basic SSG approach by allowing an incremental build to occur, which reduces build time (for CMS changes) and avoids invalidating the entire CDN cache for every change.
Finally, as the diagrams show, the SSG approach is much simpler in terms of serving-infrastructure. Clients really like the fact that the site is less vulnerable to degradation due to fluctuations in traffic. They also enjoy the cost-savings of avoiding the need for an always-running web server to service requests.
 https://martinfowler.com/bliki/TwoHardThings.html, original quote by Phil Karlton.
 The diagrams do not show any browser-to-API requests which would normally take place, since these requests are unaffected by any of these approaches. Just something to keep in mind.
 For example, the build process needs to deploy files to the static-assets location (e.g. S3), then deploy code to the web server. Then, the CDN requires configuration to tell it when to use the static assets, and when to use the web server.
 Client-side rendering (CSR) is not discussed here because most commercial websites rely on some-form of server-rendering for SEO (search engine optimisation).