We will do S3, Cloudfront(CDN), custom domain, SSL, and www
redirection
EDIT: I think you can safely refer to this post instead since the author seemed to really know what he/she was doing. Anything that isn’t covered there, you can try it here. 🌞
Create an IAM user
If you haven’t already got a separate IAM user with restricted permissions, we will create one and give her permissions to perform tasks on S3.
Check the ‘AWS Management Console access’ box too if you want this user to be able to login and manage their restricted version of ‘AWS Management Console’ via a url that looks like https://71291827982.signin.aws.amazon.com/console. So I suppose that’s the power of IAM — it lets you create and manage multiple users, each with differently level of access/permissions. Howver, from the standpoint of a lone side project, this is an overloaded feature which I failed to grasp until I watched a really dumbed-down Youtube video about it…But hey we just created a IAM user and using root’s access and secret keys is a huge taboo, so we are really getting off on the right foot! Anyway, let’s move on.
Now, keep clicking next/accept the defaults until you come to this page:
And note down this user’s Access key and Secret access key because we are going to need them later when configuring ‘aws-cli’. But nevermind all that for now. Let’s move on.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:PutObjectAcl",
"s3:GetObject",
"s3:GetObjectAcl",
"s3:DeleteObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::app.react.com",
"arn:aws:s3:::app.react.com/*"
]
}
]
}
So the ‘policy’ above is essentially saying: This IAM user has permission to upload and download files and ‘ACL’(don’t remove these! It’d break the upload! i.e. Permission Denied error. I learnt that the hard way..), and delete files, and well ‘ListBucket’, on your bucket, in this case app.react.com
(rename it to yours!) and its nested files(i.e. /*).
And finally,
And that’s it with this IAM stuff. Next, we will create S3 bucket so we can upload our react/front-end stuff to it later!
Create a S3 bucket for static website hosting
Btw here is a rare straightforward tutorial from AWS that don’t suck you into infinite rabbit hole. But I like pictures, so here we go:
Admittedly, there might be a way where I didn’t have to turn OFF all blocking of public access here but currently I have no idea. Will update if/when I do :)
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadAccess",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::app.react.com/*"
}
]
}
Change the red underlined bit app.react.com
to the name of your bucket name.
And now we are done setting up our S3 bucket.
Before we move on to Cloudfront to serve this bucket from CDN, let’s see how we will deploy/upload our dist/build folder’s files to this bucket from our local machine 🚀
Upload static assets to a S3 bucket
aws-cli
We are going to need a tool called aws-cli
. You just need to stick to the official tutorial below and you are done with that.
https://docs.aws.amazon.com/cli/latest/userguide/install-linux.html
Configure aws-cli
Dig out the access and secrets keys that we obtained before when creating the IAM user, and plug them in when you run aws configure
:
$ aws configure
AWS Access Key ID [None]: AKIAIOSFODNN7EXAMPLE
AWS Secret Access Key [None]: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
Default region name [None]: us-west-2
Default output format [None]: json
Create a npm script for deployment
In your package.json
‘s ‘scripts’ property, add the following script to automate:
- Building/bundling your web app into a
build
folder. - Delete all existing files in the S3 bucket that will contain our fresh static assets.
- Upload all the fresh stuff in the
build
folder to a S3 bucket calledapp.react.com
and allow public to read the files.
"deploy-client": "npm run build && aws s3 sync --delete build/ s3://app.react.com --acl public-read",
So now every time you want to deploy your front-end, you will just do
npm run deploy-client
🎊 🎈 ✊
Setup Cloudfront(CDN) to serve your bucket
Before we start, you can check out this tutorial from AWS regarding this matter. But if you want to follow by pictures, then read on :)
OK now we are going to setup a Cloudfront ‘distribution’ to serve our web app’s bucket’s contents! Navigate to the ‘Cloudfront’ service and do all this:
[CORRECTION]: In the ‘Object Caching’ field above, just go with the default ‘Use Origin Cache Headers’ so Cloudfront will respect the cache-control
header you can set, possibly in the npm deploy script:
"upload-assets": "aws s3 sync ./client/dist/ s3://app.react.com --delete --exclude 'index.html' --metadata-directive REPLACE --cache-control public,max-age=31557600,s-maxage=86400","upload-index-html": "aws s3 cp ./client/dist/index.html s3://app.react.com/index.html --metadata-directive REPLACE --cache-control no-cache,no-store,must-revalidate",
See here if you wondered why we entered endpoint string for the ‘Origin domain name’ field when we could have selected our bucket in the autocomplete dropdown.
We will largely ignore the ‘Distribution Settings’ for now. We will explore that abit later to setup custom domain pointing to our Cloudfront — rather than d192189729823.cloudfront.net
to access our app, we do app.example.com
.
And click the ‘Create Distribution’ button! It will take 5–15mins to be deployed to all edges, and once it’s done, you will have a URL like d092830278239872.cloudfront.net
serving your bucket from the endpoint given to the ‘Origin domain name’ field above.
Setup Custom Domain and SSL/HTTPS
And click ‘Request or Import a Certificate with ACM’ button to let AWS generates a SSL cert for us.
Clicking the button will bring us to the ‘ACM’ service…
Then you will see something like this:
Collapsing one of the row will show you this:
Now you need to add a CNAME record in your domain name registrar like Namecheap, Hover, GoDaddy, or in Lightsail and Digital Ocean if you have moved your nameservers over there.
Once you have added the record, it will take a quick while to successfully validate upon which the status will show ‘Issued’.
OK now you can go back to the ‘Edit distribution’ page and do this:
Finally, one last thing to do to get over with this chore is head back to your domain name registrar(Namecheap etc.) and add another CNAME record that maps your custom domain to the cloudfront domain that fronted your web app’s bucket. The cloudfront’s domain name you can find it here:
OK I lied, we should do something to handle when users land in our www.app.example.com
site in which case we will redirect them to our app.example.com
. The benefit is purportedly search engine won’t list your www
site too.
OK let’s do it.
You will create another S3 bucket with a name like www.app.example.com
. We are not going to do anything with this bucket but
- Turn off all the public access blocks
- Redirect this bucket to our
app.react.com
bucket!
Then you will create another Cloudfront distribution for this www
bucket by following the same steps we did for a distribution before.
And create a CNAME record for this www.app.react.com
domain too.
Add security headers to your app
You know like those headers provided by the popular ‘helmet’ library but now you have to do it in another AWS service called Lambda@Edge that integrates with Cloudfront that is now serving your stuff.
Below is a straightforward official tutorial:
Final words
If you are just building yet another 90% of the apps out there, avoid AWS by considering other options first like Digital Ocean, in particularly, Render which costs much more and significantly less bandwidth offered, but it’s a lifesaver for those building 90% of the apps, in which case though, paradoxically, you should consider AWS! Because it saves you money, the free tier provides generous runway for the potential doom and gloom, you get to learn AWS(hello recruiter!), you got nothing to lose assuming you hadn’t quit your day job(right?), and the world is not missing anything too! But anyway, with the benefit of hindsight, I would say this post has reduced the gap between AWS and the one-click deployment platforms for those currently, for whatever reason, ended up in AWS .🙏