What this will cover
- Host a static website at S3
- Redirect
www.website.com
towebsite.com
- Website can be an SPA (requiring all requests to return
index.html
) - Free AWS SSL certs
- Deployment with CDN invalidation
- https://stormpath.com/blog/ultimate-guide-deploying-static-site-aws
- https://miketabor.com/host-static-website-using-aws-s3/
- http://www.mycowsworld.com/blog/2013/07/29/setting-up-a-godaddy-domain-name-with-amazon-web-services.html
- https://www.davidbaumgold.com/tutorials/host-static-site-aws-s3-cloudfront/#make-an-s3-bucket
If we're going to be using HTTPS (and AWS Certificates), we need to ensure the domain name isn't set to private because AWS is going to use the three public contacts of the domain name to verify the domain for cert reasons. However, even if we can't control the public-ness of the domain, AWS will additionall (and automatically) send validation emails to each of
administrator@your_domain.io
hostmaster@your_domain.io
postmaster@your_domain.io
webmaster@your_domain.io
admin@your_domain.io
Read more on certs below.
For .io TLDs: http://docs.aws.amazon.com/acm/latest/userguide/troubleshoot-iodomains.html
- Create an S3 bucket named exactly after the domain name, for example
website.com
. - In Properties, click the Static Website section.
- Click Use this bucket to host a website and enter
index.html
into Index Document field. - Don't enter anything else in this form.
- This will create an "endpoint" on the same screen similar to
http://website.com.s3-website-us-east-1.amazonaws.com
.
- Click Use this bucket to host a website and enter
- Then click on Permissions tab, then Bucket Policy. Enter this policy:
{
"Version": "2008-10-17",
"Statement": [
{
"Sid": "AllowPublicRead",
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::BUCKET_NAME/*"
}
]
}
Be sure to replace
BUCKET_NAME
with yours.
Note: Naming the bucket doesn't have to be exactly the domain name. I read that in several articles that it needed to be, but it doesn't. If using wildcard domains with AWS, I've read that we can't have dots in the domain name when using wildcard domains. So just know that you can name the bucket whatever, but using dots does work if not using wildcard domains
Uploading an index.html
should allow us to visit the "endpoint"
- Go to the CloudFront section and click Create Distribution and then create for Web, not RTMP.
- In Origin Domain Name, paste the "endpoint" previously created in S3 (without the
http://
part). - The order of these instructions assume SSL certificates are not setup yet. So don't do anything with settings regarding SSL
- Select "yes" for Compress Objects Automatically.
- In Alternate Domain Names (CNAMEs), put the domain names which correspond to the two buckets. Put each on their own line OR separated by comma.
- In Default Root Object, type
index.html
. - Create. The next screen will show distributions in table form, the one we just made will be "in progress" for a few minutes
The distribution will have a domain name like dpo155j0y52ps.cloudfront.net
. This is important for DNS (see below)
- Click Hosted Zones
- Create a new Zone: Use
website.com
(without sub domain) for zone - This should create NS records such as:
ns-1208.awsdns-23.org.
ns-2016.awsdns-60.co.uk.
ns-642.awsdns-16.net.
ns-243.awsdns-30.com.
- The NS records can be used to point DNS management from other domain registrar to AWS Route 53
- Click Create Record Set to create an
A
record.- This will be the record that points
website.com
to CloudFront. - For the name, enter no value
- Change Alias to Yes
- Paste the CloutFront domain in the Alias field
- Click Create Record Set
- This will be the record that points
- Create another
A
record for thewww
redirect- Follow the same steps for the previous
A
record, but enterwww
for name and use the same CloudFront domain.
- Follow the same steps for the previous
In the AWS Console, go to Certificate Manager and request a cert for domain and all sub domains. We will be required to verify the email of the owner of the domain name. After the verification is done and the cert is "issued", we can go back into CloudFont to edit our distribution for this domain:
- Click the distribution and on the next page (in the General tab), click Edit
- Check the box for Custom SSL Certificate
- Select our cert and save
- When done with the form, click the Behaviors tab and edit the only record that should be there
- Select Redirect HTTP to HTTPS. Click Save
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:
- HTTP Error Code: 404
- TTL: 0
- Custom Error Response: Yes
- Response Page Path:
/index.html
- HTTP Response Code: 200
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:
aws --version
Configure aws-cli:
aws configure --profile PICK_A_PROFILE_NAME
Note that using "priles" 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_NAME
for your name choice (can be anything).
Enter these values:
AWS Access Key ID [None]: [Your Access Key]
AWS Secret Access Key [None]: [Your Secret Access Key]
Default region name [None]: us-east-1
Default output format [None]: json
This will save your entries at ~/.aws/credentials
. Note that you need to enter your correct region for your AWS stuff. I used us-east-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 ~/.aws/config
as:
[default]
output = json
region = us-east-1
Now, since we'll need to do some CloudFront commands which are "experimental", we need to do:
aws configure set preview.cloudfront true
This will result in a record in more records at ~/.aws/config
.
We should be setup now to dest a deployment. Run:
aws s3 sync --acl public-read --profile YOUR_PROFILE_NAME --delete build/ s3://BUCKET_NAME
- Obviously replace
YOUR_PROFILE_NAME
andBUCKET_NAME
with yours. Also this assumes the folder you want to upload isbuild
. - This command will
- Ensure all new files uploaded are public (
--acl public-read
) - Ensure we're using your credentials from your local AWS profile (
--profile YOUR_PROFILE_NAME
) - Remove any existing S3 objects that don't exist locally (
--delete
)
- Ensure all new files uploaded are public (
After deployment is verified and successful, we need to invalidate:
aws cloudfront --profile YOUR_PROFILE_NAME create-invalidation --distribution-id YOUR_DISTRIBUTION_ID --paths '/*'
- Obviously replace
YOUR_PROFILE_NAME
andYOUR_DISTRIBUTION_ID
with yours. Note that your Distribution ID can be found in the CloudFront seciton of AWS console. - If the invalidation worked, you'll be able to see a record of it in the Invalidations tab after clicking on your distribution.
To make it all easier, add to package.json
:
"scripts": {
"deploy": "aws s3 sync --acl public-read --profile XYZ --delete build/ s3://XYX && npm run invalidate",
"invalidate": "aws cloudfront --profile XYZ create-invalidation --distribution-id XYZ --paths '/*'"
},
XYZ
is for all the parts that need to be replaced. Now you can run npm run deploy
which will deploy then invalidate
Cheers!