As I host a couple of Microservices with some React JS frontends I like hassle free deployment. But every time I needed a new deployment for a project I didn’t remember all the nifty little details to get my React app up and running on Amazon S3 + with Cloudfront as a edge optimized CDN. So this time I will document a suitable setup.
What this will cover
index.htmlinto Index Document field.
Be sure to replace
index.html should allow us to visit the “endpoint”
http://part). Note: that when you click on this field it will act like a dropdown with options to your existing buckets. But for me it often populates with S3 bucket names without the needed
s3-website.regionpart. So I think you can’t just select one of those dropdown items but have to enter it manually.
www.banaan.org. The field is called “Alternative Domain Names” because AWS will have an aws-specific domain name for the CDN, but you don’t want to use that so you’ll want to put in your custom domains and then use Route 53 (next section) to point domains to the CDN.
The distribution will have a domain name like
dpo666j0y51ps.cloudfront.net. This is important for DNS (see below). So copy it somehwere.
These DNS instructions assume your DNS is hosted at AWS. This does not mean you have to buy a domain at AWS, it just means that when you buy a domain at somewhere like Google or GoDaddy, over there you need to point NS records to AWS to allow AWS to manage the parts of the DNS record. But first, at AWS is where you create the “Hosted Zone” which is where you create the NS values to eventually give to Google or GoDaddy, etc. I don’t know how any of this is different if you buy your domain at AWS (But then again I never buy domains at the same place I host)
banaan.orgwithout sub domain) for zone. Note that each domain name will get one zone, sub domains all belong to the same zone.
[some-random-number].couldfront.net. You can get this by clicking your CloudFront distribution and in the General tab there is a “Domain Name” label.
Arecord for the
Arecord, but enter
wwwfor name and use the same CloudFront domain. But note this is because we want
banaan.orgto point to the same bucket (and therefore the same CloudFront domain). I suppose you would make a whole new bucket and a whole new CloudFront distrubution (with a new CF domain) if you wanted a second project at
app.banaan.org. This might be common if you app is a React app that is completely separate code from your “home page” website which might be from a static site generator or something.
In the AWS Console, go to Certificate Manager and request a wildcart cert for the domain and all sub domains. We will be required to verify certificate via email or DNS.
If you choose to verify via DNS, AWS will ask you to add some CNAME records to your Route 53 DNS, but the nice thing is that there is a shortcut button to do so (for each domain and sub domain) from within the Certificate Manager page.
After the verification is done and the cert is “issued”, we can go back into CloudFont to edit our distribution for this domain:
If the website is an SPA, then we need to make sure all requests to the server (S3 in this case) return something even if no file exists. This is becuase SPAs like React (with React Router) need the
index.html page for every requests, then things like “not found” pages are handled in the front-end.
Go to CloudFront and click the distribution you want to apply these SPA settings to. Click the Error Pages tab and add a new error page. Fill the form with these fields:
For deployment, we need to consider that files in the CloudFront CDN are not meant to change. If we were to upload new files to S3, they would not be deployed to the CDN’s edge servers and therefore would not update the website. Read More.
To invalidate files on the CDN we’ll need to use CloudFront’s invalidations feature: Read More.
In the AWS console, in the CloudFront management of a distribution, there is a tab for Invalidations. We could manually create an invalidation (with the value of
/*) to invalidate all S3 files. Note that invalidation records here are one-time invalidations and every time we deploy new files, we will need to make a new invalidation.
To deploy with invalidations, we will need to install AWS-CLI first. We also assume you have an IAM user from AWS with an Access Key and Secret Access Key.
To test installation, do:
Note that using “profiles” to configure AWS-CLI is probably best since you might want to use the CLI to manage multiple AWS accounts at some point. Be sure to swap out
PICK_A_PROFILE_NAMEfor your name choice (can be anything).
Enter these values:
This will save your entries at
~/.aws/credentials. Note that you need to enter your correct region for your AWS stuff. I used
eu-central-1, but make sure to use the correct one for you. Also note that you can have responses in
text instead of
json if you want
You can ommit the last two questions for region and format if you want to set up a default for your computer (that all profiles will use). The default profile is located at
~/.aws/config. If you omit the region and format from your profile, be sure they exist in your
Now, since we’ll need to do some CloudFront commands which are “experimental”, we need to do:
This will result in more records at
We should be setup now to test a deployment. Run:
BUCKET_NAME with yours. Also this assumes the folder you want to
This command will
After deployment is verified and successful, we need to invalidate:
YOUR_DISTRIBUTION_IDwith yours. Note that your Distribution ID can be found in the CloudFront section of AWS console.
To make it all easier, add this to
REPLACE_ME is for all the parts that need to be replaced. Now you can run
npm run deploy which will deploy your
build to S3 and then invalidate the CloudFront cache so visitors will see the update.
If you host some other assets in you S3 bucket that you don’t want to overwrite or delete when you deploy a new app you
--exclude folder like this:
Optionally you could also exclude them from the invalidation command as they don’t have to be invalidated if they didn’t change.
Enjoy your new app!