Host a static website on AWS with S3 and CloudFront

Photo by Wolfgang Hasselmann / Unsplash
Photo by Wolfgang Hasselmann / Unsplash

AWS provides many services to deploy a web application. These applications are grouped into two categories:

  • Frontend applications usually consist of static files generated from a Single Page Application library such as React or Vue
  • Backend applications that run on a server, a container, or a Lambda function and interact with a database or other backend applications following an Event-driven architecture.

Although you can also host static files using backend AWS services, it can be beneficial to use dedicated services such as CloudFront and S3.

AWS CloudFront is a content delivery network (CDN). It integrates with other AWS services to give developers an easy way to distribute content to end users with low latency, high data transfer speeds, and no minimum usage commitments.

Prerequisites

To follow this post, you must have the following things:

  • An AWS account, a free tier is enough
  • Node.js 18 or higher - Download link
  • The Node Package Manager (NPM)

Set up a React project with Vite

To have a static website to host on AWS S3 and CloudFront, let's set up a React project with Vite and then generate the static files.

Run the command below to scaffold a new React project using the Vite CLI:


npm create vite@latest teco-app --template react-ts

The CLI asks two questions, the first to select a framework and the second to choose a variant; choose "React" for the former and "Typescript" for the latter.

"teco-app" is the project's folder name, so change it if you want.

Create a new React project using the Vite CLI.
Create a new React project using the Vite CLI.

The project is generated; run the commands below to install the dependencies and run the project:


cd teco-app
npm install
npm run dev

Navigate to http://localhost:5173/, and you will see the page below:

The React application running locally.
The React application running locally.

Generate the static bundle of the website

The project runs locally, and we want to deploy the static website on AWS S3 and CloudFront. Let's generate the static files by running the command below:


npm run build

This command will generate a "dist" folder with all the static files.

Bundle files of the React application generated after the build.
Bundle files of the React application generated after the build.

To verify the static files will run the website as expected, you can use the Node.js package serve to start a web server locally to serve a static folder. Run the command below:


npx serve dist -p 5001

Navigate to http//:localhost:5001, and you will see the web page:

Static files running locally through a web server.
Static files running locally through a web server.

The static files of the website work as expected; we can proceed to host them on AWS S3.

Create the S3 bucket on AWS

Log into your AWS account, search for the AWS services named S3, or click on this link. Click on the "Create bucket" button.

On the page displayed,

  • Type a bucket name; it must be unique globally and not just in your AWS account.
  • Select the region where to host the S3 bucket.
  • In the Object Ownership section, leave it as is.
  • In the section Block Public Access settings for this bucket, untick the checkbox Block all public access and tick the checkbox labeled I acknowledge that the current settings might result in this bucket and the objects within becoming public.
Allow access to the S3 bucket from anywhere.
Allow access to the S3 bucket from anywhere.
  • In the section "Bucket Versioning", enable the bucket version if you need; I will leave it disabled.
  • In the section "Tags", add one or more if needed.
  • In the section "Default encryption", leave the Encryption key type as is, but disable the Bucket key (should find why)
  • Skip advanced settings
  • Click on the button Create bucket
The S3 bucket has been created successfully.
The S3 bucket has been created successfully.

You can see the bucket name created in the bucket list.

Upload the file to the S3 bucket

Click on the bucket created previously, and on the page displayed, click on the "Upload":

Start files upload to the S3 bucket.
Start files upload to the S3 bucket.

Upload the "dist" folder containing the static files. Click on the Upload button to submit.

Summary of folders and files to upload to the S3 bucket.
Summary of folders and files to upload to the S3 bucket.

Wait for the upload to complete, and verify the files are on the S3 bucket.

Upload files to the S3 bucket using the AWS CLI

You can also use the AWS CLI to upload the file to the S3 bucket; this is very helpful when you use a script to deploy your website or inside an automated CI/CD pipeline.

The syntax to copy a folder from the computer to an S3 bucket is the following:


aws s3 cp <folder> s3://<bucket-name> --recursive

The recursive option allows copying subfolders. The command to upload the "dist" folder to the S3 bucket "teco-web-app" is the following:


aws s3 cp dist s3://teco-web-app --recursive

Here is the output you will get

Copy files from the local computer to the S3 bucket.
Copy files from the local computer to the S3 bucket.

If you don't have the AWS CLI on your computer, follow this link to install it.

Enable static website hosting on the S3 bucket

In your bucket, click on the bucket properties tab; scroll to the bottom till the section Static website hostingclick on the "Edit" button.

Edit the S3 bucket static website hosting settings.
Edit the S3 bucket static website hosting settings.

Change the Static website hosting to Enable; leave the Hosting type to Host a static website.

In the text input with the label "Index document", type "index.html", which represents the entry file of the website.

New values for the static website hosting settings.
New values for the static website hosting settings.

Click on the "Save changes" button at the bottom right of the page.

Create the CloudFront distribution

Search for the CloudFront service and click on it.

The page displayed shows the list of existing CloudFront distributions and a button to create a new distribution at the top right; click on it.

View the list of CloudFront distributions.
View the list of CloudFront distributions.

The page displayed shows the form to create the distribution.

In the text input labeled "Origin domain", type the origin domain related to our S3 bucket; it has the following format: <bucket-name>.s3-<aws-bucket-region>.amazonaws.com.

If your bucket name is "teco-web-app" and the bucket region is "eu-west-3", the value of the origin domain is teco-web-app.s3-eu-west-3.amazonaws.com.

Leave an empty value for the text input labeled "Origin path".

Change the origin name; give a name that makes it easier to find among the distributions list. E.g.: teco-web-app-distro

Define the CloudFront distribution origin domain and name.
Define the CloudFront distribution origin domain and name.

In the "Origin access" section, tick the radio button with the label "Origin access control settings (recommended)" to restrict bucket access to CloudFront only.

A section will appear to select an origin access control; click on the button at the right to create a new one.

Define the CloudFront distribution origin access.
Define the CloudFront distribution origin access.

A modal will appear with fields having predefined values. You can change the name, add a description, but do not change the value for the Signing behavior.

Create a new access control setting.
Create a new access control setting.

Leave everything as is and click the button to create the access control.

An alert will notify you that we must add a policy to allow CloudFront distribution to access the S3 bucket files; we will do it later.

The CloudFront distribution origin access is defined.
The CloudFront distribution origin access is defined.

In the section "Default cache behavior", find the label "Viewer protocol policy" and change the value to "Redirect HTTP to HTTPS".

In the section "Web Application Firewall (WAF)", tick the option "Do not enable security protections".

Leave the rest of the settings and click the "Create Distribution" button.

The distribution is created; copy the ARN and keep it somewhere because we will need it in the next section.

The CloudFront distribution is successfully created.
The CloudFront distribution is successfully created.

Allow the CloudFront distribution to access the S3 bucket

Go to your bucket and click on the "permissions" tab.

On the page displayed, scroll down to the section "Bucket policy" and click on the edit button. In the Text editor, paste the policy below:


{
    "Version": "2008-10-17",
    "Id": "PolicyForCloudFrontPrivateContent",
    "Statement": [
        {
            "Sid": "AllowCloudFrontServicePrincipal",
            "Effect": "Allow",
            "Principal": {
                "Service": "cloudfront.amazonaws.com"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::<bucket-name>/*",
            "Condition": {
                "StringEquals": {
                    "AWS:SourceArn": "<distribution-arn>"
                }
            }
        }
    ]
}

Replace the <bucket-name> and the <distribution-arn>, respectively, with the bucket name and CloudFront distribution ARN we created previously.

Define the policy allowing CloudFront distribution to access the S3 bucket files.
Define the policy allowing CloudFront distribution to access the S3 bucket files.

Click the "Save changes" button; the CloudFront distribution can now get files in the S3 bucket.

Set the entry point of the CloudFront Distribution

From the CloudFront distribution, we should define the file in the S3 bucket that must be loaded when we hit the / path of the CloudFront distribution URL.

Go to the CloudFront distribution created; in the "Settings" section, click on the "Edit" button.

Edit CloudFront distribution settings.
Edit CloudFront distribution settings.

Locate the text input with the label "Default root object" and type the value "index.html".

Set the default root object for the CloudFront distribution.
Set the default root object for the CloudFront distribution.

Click the "Save changes" button and wait for the changes to apply. You can verify the progress of the CloudFront changes by looking at the field with the label "Last modified". When the value is "Deploying...", the application of the changes is in progress; when done, the last modification date will be displayed instead.

The CloudFront distribution changes application is in progress.
The CloudFront distribution changes application is in progress.

Test it

Once the CloudFront distribution changes are applied, copy the distribution URL, open a browser tab, paste it, and navigate to it.

The static website is accessible from the CloudFront URL.
The static website is accessible from the CloudFront URL.

Wrap up

Hosting a static website on AWS using S3 and CloudFront is a tedious task that we broke down in this post; here are the main steps to host a static website:

  • Create a S3 bucket
  • Upload the static files to the S3 bucket
  • Enable static website hosting on the S3 bucket
  • Create the CloudFront distribution
  • Link the CloudFront distribution to the S3 bucket and authorize the former access to S3.

The website is accessible from the CloudFront subdomain, which is not user-friendly. The following tutorial will show how to set the custom domain to CloudFront distribution and generate an SSL certificate.

Are you interested in seeing how to achieve this using the AWS CDK? Check out this post I wrote about using the AWS CDK to host a static website.

Follow me on Twitter or subscribe to my newsletter to avoid missing the upcoming posts and the tips and tricks I occasionally share.