How to build and deploy a beautiful static site with AWS S3, Route53, and CloudFront 🔥

Nicholas Vincent-Hill


This is a step-by-step guide to creating a responsive static billboard/portfolio site, deploying it to the web with AWS, and serving it securely with HTTPS.

This guide is designed for beginner and intermediate-level software engineers and web developers looking for an easy way to create a personal site for cheaper than paid-services like SquareSpace or other site-builders. Basic technical requirements include a knowledge of HTML/CSS/JS, npm, and Git (the advanced site creation process uses Gatsby.js which requires knowledge of React.js and GraphQL).

The only economic requirements are AWS Route53 hosted zone fees ($0.50/month) and the cost of acquiring a domain (I rented for $25 for five years from The estimated time commitment is four to five hours depending on individual skill and experience.

This guide will enable you, the developer, to control all aspects of the design, development, and deployment of your site.

Step 1 — Create your personal site (the easy way or the hard way)

Here’s the easy way- go here and pick out a fully responsive, super customizable, and 100% free (under the Creative Commons) HTML/CSS/JS template. Download it and customize/build on it until you love it!

Call npm init in the root directory of your chosen project. Make sure to git init, add all non-public information to .gitignore, and write great commit messages.

Here’s the hard way- use Gatsby.js to build a blazing fast static site and PWA from scratch. This isn’t a Gatsby.js tutorial (plenty of great tutorials can be found here) and requires React.js and GraphQL knowledge. A number of starter templates can be found here (I used the “strata” HTML5UP template as a jumping off point for

Test and develop your website until you are happy with it and want to share it with the world. We will be building a continuous development pipeline with either Grunt or the gatsby-plugin-s3 plugin later so future revisions/deployment will be quick and easy.

Step 2 — Get a domain

Sites like or offer very compelling prices for domains. I waited for a promotion and got for $25 for five years. Alternatively buy a .dev domain from Google.

Step 3 — Create an AWS account

Create an AWS account. This step is fairly straightforward. New accounts get free services for a year — outlined here. This requires you to have an active credit card on file with AWS. Be aware of the security implications and do not accidentally push your AWS credentials to the public web.

Step 4 — Configure AWS S3 buckets

Amazon Simple Storage Service (Amazon S3) is an object storage service that offers industry-leading scalability, data availability, security, and performance. This means customers can use it to store and protect any amount of data for a range of use cases, such as websites, mobile applications, backup and restore, archive, enterprise applications, IoT devices, and big data analytics.

AWS S3 is where your static site will live; there are a few things to do in order to configure this correctly.

First create a bucket; AWS S3 and Route53 only work correctly if your bucket name and domain name match precisely — name your bucket mysite.domain (i.e. Make sure to set permissions correctly; the public should be able to read from the bucket but not write to it.

Make sure to give public read access to your bucket

Next navigate to “Properties” and configure AWS S3 to statically serve index.html (see below).

Create a bucket (also public read access enabled) named www.mysite.domain ( in my case) and redirect to the main bucket address (see below).

Next, create aws-keys.json in the root directory of your project:

Here’s a guide to finding your AWS access key ID and Secret key.

Remember to add aws-keys.json to your .gitignore! One of my students accidentally pushed their AWS credentials to Github, was programmatically attacked, and incurred ~$1.3k in charges in a few hours.

Don’t let this happen to you!

Step 5 — Upload your site to AWS S3 with Grunt or gatsby-plugin-s3

Grunt is a JavaScript task runner, a tool used to automatically perform frequent tasks such as minification, compilation, unit testing, and linting. It uses a command-line interface to run custom tasks defined in a file.

If you choose the easy way to create your site, you can use Grunt to upload your completed site files to your AWS S3 bucket.

npm install grunt grunt-aws-s3 --save-dev

Create a Gruntfile.js:

Add this to the scripts object of your package.json.

"deploy": "grunt deploy"

Now whenever you want to deploy a new production version of your site just execute npm run deploy. This will only POST/PUT files to AWS S3 that have changed — reducing the number of requests you need to pay for (new AWS accounts get 2,000 free PUT requests/month for the first year).

Alternatively — if you built your site with Gatsby.js, forget Grunt and just use gatsby-plugin-s3 for static site deployment. I wrote a npm script npm run ship which builds the production bundle and uploads it to AWS S3 - gatsby build && gatsby-plugin-s3 deploy. The code for my Gatsby site can be found here.

Step 6 — Create a distribution with AWS CloudFront

Amazon CloudFront is a fast content delivery network (CDN) service that securely delivers data, videos, applications, and APIs to customers globally with low latency, high transfer speeds, all within a developer-friendly environment.

Create a new CloudFront distribution (loosely following these AWS docs). Make sure to specify Alternate Domain Names (CNAMEs) correctly and request a custom SSL certificate to enable HTTPS.

Also specify the Default Root Object as index.html.

Remember to specify Default Root Object

Click Create Origin to link your S3 bucket to your CloudFront Distribution. Specify Origin Domain Name as Do not restrict bucket access.

CloudFront Distribution Origin

Create a new behavior and set Viewer Protocol Policy to “Redirect HTTP to HTTPS.”

CloudFront Distribution Behaviors

You can also customize error responses — I redirect 404 errors to a custom 404.html page.

CloudFront Error Pages

Step 7 — Redirect traffic to your new CDN with AWS Route53

Amazon Route 53 is a highly available and scalable cloud Domain Name System (DNS) web service. It is designed to give developers and businesses an extremely reliable and cost effective way to route end users to Internet applications by translating names like into the numeric IP addresses like that computers use to connect to each other.

Now we need to connect your domain name (mysite.domain) to your CloudFront distribution so that when a user makes a request to your domain they receive your index.html and the rest of your static site (by hitting your newly created CDN).

Open the AWS Route53 console and create a new Hosted Zone. Here’s some helpful AWS docs to get you started.

Make sure to update the name servers for the domain you bought. Use the method provided by the registrar for the domain you purchased to change the name servers for the domain (use the four Route53 name servers found in the NS — Name server record set).

Create a new record set and set the alias target to your AWS CloudFront distribution (see below). Make sure to name it yoursite.domain.

Alias your CloudFront distribution

Changes to domain name servers take time to propagate so make sure to wait a few minutes before attempting to access your site via your new domain.

Step 8 — Bask in the glory of your newly deployed site! (or troubleshoot problems)

You did it! Enjoy your beautiful new portfolio site deployed to the world wide web. Check out my site I built and deployed using this tech stack; I wired up the contact form with AWS Lambda using this guide.

Enjoy your new beautiful portfolio site!