Deploy a React application on AWS with CloudFront and S3

Deploy a React application on AWS with CloudFront and S3

A tutorial using the AWS console

Web applications built with React are commonly hosted and served as static files, so if you are building a Single Page Application and you want to host it on AWS you can use the combination of CloudFront and S3.

Introduction

Cloudfront is a Content Distribution Network (CDN), a service that allows you to host your content close to the final users worldwide with low latencies. To know more about CloudFront and CDNs check my article.

S3 is a robust and reliable object storage that will scale on demand and let you only pay for used resources, check it on the AWS page.

Setup React

This setup will work with all static websites but let's focus on React.

So let's start with a simple hello world application created with Create React App https://create-react-app.dev/docs/getting-started

npx create-react-app my-app
cd my-app
npm start

This will create our app and start it, so we can take a look at it in our browser locally on the PC.

Now that we have our sample application ready let's take a look at the structure of the project:

my-app
├── README.md
├── node_modules
├── package.json
├── .gitignore
├── public
│   ├── favicon.ico
│   ├── index.html
│   ├── logo192.png
│   ├── logo512.png
│   ├── manifest.json
│   └── robots.txt
└── src
    ├── App.css
    ├── App.js
    ├── App.test.js
    ├── index.css
    ├── index.js
    ├── logo.svg
    ├── serviceWorker.js
    └── setupTests.js

The structure contains multiple js and media files but those are only the source of our final application, before using it in production we need to build the application and generate an artifact.

We can now kill the React development server we just started and instead we can now build our application:

npm run build

The output of the command will be put in the build directory. All the files inside our build directory are what will be uploaded to our static hosting.

Create S3 resources

With our artifacts ready we can now create an S3 bucket, for this tutorial we will use the AWS console but for your production workloads, I suggest using IaC tools like CDK, Terraform or Cloudformation.

Let's create our bucket:

S3 create bucket

After creating it, the empty bucket is ready to receive our artifacts:

S3 bucket created

We can now upload our artifacts, let's do it with the aws-cli, and follow the blog for a future post where we automate this with CI/CD.

aws s3 sync --delete build s3://react-on-aws-demo-alessandromarinoac

Create CloudFront resources

Let's create now our CloudFront distribution and let's select our bucket as origin, using the console make this process really simple.

To secure the connection between CloudFront and the bucket we need to create or use an existing Origin Access Control Settings.

CloudFront create distribution

If you don't already have one Control Settings let's create it using the button on the right:

CloudFront create control settings

Now with the origin secured, we can start setting up the cache options for our CloudFront distribution, we will for example redirect all HTTP requests to HTTPS.

We can also create a custom cache policy if we need to specify specific TTL or caching options, if not we can use the default CachingOptimized that will help us save some time.

CloudFront create cache behaviour

Let's go ahead and scroll to the general settings section, here we can select a price class if we want to save some money or we can for example set our default root object that will be necessary to make React work properly.

We are setting the root object as index.html

CloudFront distribution creation settings

We can now click on Create distribution and we have the confirmation. The distribution needs some time to be ready (~10min) so have some patience here.

Secure our S3 Bucket

Now as the console advise us to, let's copy the S3 bucket policy from the blue banner and let's go back to our S3 bucket to apply it. We need to navigate to the bucket and then to the Permission tab:

S3 bucket policy

We can now apply the copied S3 Bucket Policy to restrict access to the bucket:

S3 bucket policy for CloudFront

With this policy in place, we are authorizing CloudFront to read from our bucket, without the need of having a public bucket.

Check if it's working

Let's now go back to the CloudFront console and retrieve the distribution domain name to check our app online:

CloudFront distribution domain

If we use the browser to open that link, we can see our app:

CloudFront React app example

So now we have our React application up and running.

Setup Errors Pages

There is still a step we need to take. If we try to navigate to any page other than the root we will get an error:

CloudFront error pages

We need to set Error Pages on CloudFront so instead of getting errors from CloudFront we will let React take care of those.

Let's create two custom error responses, one for 403 Forbidden and one for 404 Not Found , both will have a response code of 200 OK.

CloudFront error pages list

CloudFront 403 error page for React

CloudFront 404 error page for React

With these new settings, we can now navigate to every path of our applications without receiving errors from CloudFront:

CloudFront React app successfully working

To create pages on different paths inside react we will need to use something like https://reactrouter.com/en/main

With react-router configured we can also set up custom 404 pages.