Host React Apps using S3 & CloudFront

Soumya Ranjan Sahoo
Towards AWS
Published in
5 min readApr 3, 2022

--

Ok ! so this article is all about hosting your React apps in S3 bucket and using CloudFront for faster delivery.

SITUATION:

You might have noticed that now these days we have URLs like

www.example.com/flights#domestic?stdt=0507&endt=0709

www.example.com/recharge#dth?stdt=052021&endt=072022

After breaking API into Microservices now we need to break the frontend to micro frontends so that every component will be independent of each other and developers can work parallelly in multiple components.

Note: This is not micro frontend architecture. I am just using the terminology here. Micro frontend is a different concept.

In the above example, let’s say we have three apps: Root-APP, Flights and Recharge. Root app is the landing/home app for the website from where user can go to other pages. In flights app we have different pages like /domestic, /international and in Recharge app we have pages like /dth, /phone, /electric etc.

When user go to www.example.com the root-app will be loaded and when user clicks on Flights icon, the request will be navigated to Flights app.

TASK:

We need to deploy all the apps in such a way, for user its one application (example.com) but behind the picture it’s three apps deployed and integrated.

ACTION:

Create the react apps

>> npx create-react-app root-app

>> npx create-react-app flights

>> npx create-react-app recharge

Add a parameter in package.json as below for flights and recharge apps only:

“homepage”: “/flights/”

OR

“homepage”: “/recharge/”

** flights and recharge should be same as the folder names inside S3. Will be explained after sometime.

Create the builds

>> npm run build

Make sure the file references in build/index.html are like:

/favicon.ico → /flights/favicon.ico

logo192.png → /flights/logo192.png

manifest.json → /flights/manifest.json

/flights/static/css/main.060ca332.chunk.css

/flights/static/js/2.3f3c4ba4.chunk.js

/flights/static/js/main.79a1fc1f.chunk.js

Now you will have the build directories in root-app, flights and recharge apps.

Upload apps in to S3

  1. Create S3 bucket and make static website hosting as enable. It’s not very important to create the S3 bucket with name same as your domain name here as you will be redirecting the request using cloud front but still its a good practice.
  2. Enable OAI for CloudFront so that only CloudFront can only access S3 bucket content.
  3. In the root folder upload all the build folder content (not the build directory) of root-app
  4. Create two folders flights and recharge for the two apps we have.
  5. Upload the build folder contents of the apps in their respective folders
S3 bucket view with application build

Create SSL Certificate for your domain

1. Create a certificate in ACM for a custom domain : example.com. This will make your domain as SSL certified.

2. Click on “Create records in Route 53” to create an CNAME entry in Route53.

CloudFront Configurations

In this deployment type, cloud front is acting as a router. Means when you go to specific page like /flights, it is CloudFronts responsibility to land the request on flights react app.

Here we have two concepts to understand.

  1. Origin: This represents the source location (S3) where the file(index.html) is located
  2. Behavior: This maps the URL path pattern to the correct origin. Means /flights will be mapped to the Origin which represents the source location of Flights react app.

Using path pattern in behavior CloudFront redirects the request to the correct origin which is the location of the application build files.

Steps …

  1. Create a CloudFront distribution
  2. Add example.com in Alternate domain names
  3. Mention “index.html” in Default root object
CloudFront Distribution
  1. Add Origins in CloudFront
  2. Origin name: anything you like to identify it.
  3. Origin domain: S3 bucket source. Select from auto complete. Here all the origins domain will be same as you are hosting all the apps in one S3 bucket. Please mention the correct S3 in case of using multiple bucket for hosting.
CloudFront Origin
  1. Add Behaviors in CloudFront
  2. Path pattern: this will be the URL path and make sure that you have the folder same as this path in your S3 and host your react app inside the folder. (Ex: flights/*)
  3. Origin or origin group: select the origin you have created before for respective application.
CloudFront Behaviors

Create a CloudFront Function

Now as you have deployed the root app in the root location of S3. But the web page will be only available at www.example.com/index.html. You need to mention the index.html at the end which doesn’t look very good. Hence to avoid index.html at end and consider it by default we will create a CloudFront Function. Now when you hit www.example.com, it will be considered as www.example.com/index.html behind the picture.

  1. How to create a function: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/functions-tutorial.html
  2. The function code: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/example-function-add-index.html
  3. Publish the function
  4. Associate the function with the distribution
  5. Event Type: Viewer Request
  6. Cache Behavior: Default (*)

RESULT:

Now your application is hosted in S3 and you can access it by using cloudfront “Distribution domain name” or the “Alternate domain names”.

When you hit

www.example.com → navigates to root application

www.example.com/flights → navigates to flights application

www.example.com/recharge → navigates to recharge application.

Notes:

  1. Each time you make changes in code and redeploy, make sure to invalidate the cloud front with /* pattern otherwise users will not get the updated application. What happens is, CloudFront creates cache in the Edge locations near to user and uses the content from the edge location while responding. Hence when we invalidate with /*, CloudFront will clears the cache from all edge locations and next time when user hits the app it will go to the source(S3) for the content. In case you are making changes only in flights app then invalidate with /flights will be enough as well.
  2. In case you want to use more nested URL pattern like /flights/domestic/xyz … This will throw error from S3 as access denied or Unauthorized. What is does is, it tries to find an index.html file in the /flights/domestic/xyz folder location of S3 bucket. So you need to use HashRouter instead of BrowserRouter from react-router-dom for URL navigation and your URL should look like /flights#domestic?stdt=0507&endt=0709. You need to use # for the nested URL paths instead of /.
  3. The folder names inside S3(flights, recharge) for individual apps, path patterns (flights/*, recharge/*) in CloudFront behaviors and the “homepage” value in package.json file (/flights/, /recharge/) should match.

I hope this helps and you will be able to deploy your multiple react apps in S3. Happy developing …

--

--

Solution architect and full stack developer, like to write tech stuff …