How to Best Engineer Your Website II

November 01, 2018

In a previous article we’ve shown how you can use an S3 bucket to host your static website or web application without the need of a server. Now that’s not quite enough to have an SEO friendly and a very reliable website to not depend on availability zones, support caching and adjust routing right. Beside this, to grow your SEO ranking and have your content trusted, you will have to have an SSL setup so that connection would be secured. We’re gonna be using AWS CloudFront and Lambda@Edge functions to easily handle this problems for you.

To setup CloudFront you’re gonna have to create a new CloudFront Distribution with SSL with the origin pointing to your S3 Bucket:

Scr 1

Create Origin from Distribution > Origin > Create Origin:

Scr 2

Now we’re gonna setup main Behavior for your distribution (*)

Scr 3

The proposed architecture is pretty much gonna look like the following, with the 2 Lambda@Edge functions associated to the existing behavior:

Scr 4

First let’s talk about Cache Control. As you may already know CloudFront caches your static content for 24 hours, so if you update your files relatively often you may end up not getting them displayed right away, so you will have to manually invalidate cache on CloudFront which might be an option as well.

Lambda A is specifically doing cache invalidation programmatically without you having to worry about any cache control for content that must revalidate.

'use strict';
exports.handler = (event, context, callback) => {
  const request = event.Records[0].cf.request;
  const response = event.Records[0].cf.response;
  const headers = response.headers;

  if (request.uri.startsWith('/static/')) {
    headers['cache-control'] = [
        key: 'Cache-Control',
        value: 'public, max-age=31536000, immutable'
  } else {
    headers['cache-control'] = [
        key: 'Cache-Control',
        value: 'public, max-age=0, must-revalidate'

  callback(null, response);

Lambda B will take care of eventual old URL redirections, or in our case to a specific index.html from the S3 bucket folders structure. GatsbyJS static site structure is creating an index.html for every page that needs to render. Eg. /about-us/index.html

exports.handler = (event, context, callback) => {
  const request = event.Records[0].cf.request;
  const uri = request.uri;

  // Redirect some popular search results to their new pages
  const redirects = [
    { test: /^\/contact\/?$/g, targetURI: '/contact-us/' },
    { test: /^\/about\/?$/g, targetURI: '/about-us/' }
  const redirect = redirects.find(r => uri.match(r.test));

  if (redirect) {
    const response = {
      status: '301',
      statusDescription: 'Moved Permanently',
      headers: {
        location: [
            key: 'Location',
            value: '' + redirect.targetURI
    callback(null, response);
    if (uri.endsWith('/')) {
      request.uri += 'index.html';
    } else if (!uri.includes('.')) {
      request.uri += '/index.html';
    callback(null, request);

Handle "404 Not found" in CloudFront for GatsbyJS or any React routed app

You will notice that CloudFront does not automatically route your application to a 404.html if the resource cannot be found within its cache, respectively within S3 bucket. This will have to be set manually into its configuration

CloudFront > Your Distribution > Error Pages > Create Custom Error Response

Scr 5

You will have to make sure the HTTP Response code is 200 and that is pointing to index.html, so that your Gatsby webapp (that uses react router) will be hit and display the 404.html We would recommend to do it for 400 and 403 as well.

Hitting your API from Origin and avoiding CORS in the same time

1) Create a new Origin to point to your LoadBalancer or API

2) Setup a new Behavior on /api/

CloudFront allows you to setup Behaviors for your distribution depending on the PATH, so instead of * for Path Pattern you may have /api/ and origin to your upper created origin.

Scr 6

Final Thoughts

If you ever have to design an architecture that involves separation of back-end from the front-end layer, and they only connect through an API, you may wanna consider using an agnostic serverless architecture. In the previous article we’ve explained all the performance and scalability advantages of using this approach. Deploying them separately will also give you more control over using different versions of front-end and back-end on the top of speed and reliability.


Outsourcing - Why, When and Where?

February 20, 2019
Reading time: 5 Min.

Multi-tenant architecture in React Native

January 31, 2019
Reading time: 7 Min.

From understanding to creating Progressive Web Apps

January 26, 2019
Reading time: 13 Min.