There are quite a few posts out there that try to explain how to set up S3 and CloudFront to serve up your single page Angular2 app. However, they are incomplete, lead you into more configuration than is necessary and/or don’t do what I think is best practice.
This HOWTO will take you step-by-step through the following:
- Domain registration via Route53
- FREE SSL certificate creation via Certificate Manager (ACM)
- Naked/apex domain support
- Multi-domain redirection without servers
- Gzip’ing and uploading your Angular2 app to S3 WITHOUT the need for static website configuration
- Serving your app out of CloudFront CDN with support for: SSL, root apex, SSL upgrading, Angular2 routing and 1st load sub-directory URLs, far future content expiration (for high cache hit ratio), HTTP/2, and multiple domains
In the end you will have a lightning fast SECURE page, with fortune 500 reliability and durability – all for cheaper than you will believe.
Step 1: Register your domain(s) and create SSL cert via ACM
I highly recommend using AWS Route53 to register your domains. It’s $10/year for .com’s with no bullshit price changes or coupon hunting. Super easy to use and gives you lots of easy integration with other AWS services like CloudFront, EC2, ELB, S3 etc.
For this example we pretend I registered rynop.org and rynop.com
Next go to the Amazon Certificate Manager (ACM) console page and request a certificate. Put in your root apex(es) (aka naked domain) and all your subdomains. ACM SSL certs are FREE and they auto-renew. It’s similar to letsencrypt but zero overhead. Ex:
rynop.org, *.rynop.org, rynop.com, *.rynop.com
You will get an email to your domain admin contact email addresses to approve.
Step 2: Create S3 bucket and upload hello world HTML page
Create an s3 bucket (Ex:
http://www.rynop.com). Create a file named
index.html with the following contents. When you upload the file make sure to give it public read permissions.
<!doctype html><html lang=en><head><meta charset=utf-8><title>rynop.com</title></head><body>Hello World</body></html>
Note: you will notice I did NOT setup static website hosting on my S3 bucket. It is not needed as CloudFront will handle it all for you.
Step 3: Create and set up CloudFront distribution
Create a new CloudFront distribution, specify the “web” delivery method.
Use these settings (ones I do not mention, leave the default value):
- Origin Domain Name: (this should auto-complete to your s3 bucket) ex:
- Viewer Protocol Policy:
Redirect HTTP to HTTPS
- Allowed HTTP Methods:
GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE(we want to do CORS)
- Forward Headers: Both
- Query String Forwarding and Caching: Don’t choose
Noneif you use query string in your Angular app
- Compress Objects Automatically:
Yes(It won’t re-gzip content)
- Alternate Domain Names:
http://www.rynop.com. I omitted the root apex domains as we are going to setup another CloudFront distro in to redirect those to
http://www.rynop.com. Don’t do naked domains over HTTPS. Google this if you care why.
- SSL Certificate:
Custom SSL Certificate. Chose the cert you created in ACM
- Default Root Object:
index.html. This is your Angular2 app entry point
Create Distribution. Then immediately go back and edit your CF distro. Choose the
Error Pages Tab and press
Create Custom Error Response. Fill in the fields like this for BOTH 403 and 404 errors:
This will now support when a URL change is emulated via pushState or directly navigated to when you type in your browser address bar.
Step 4: Point your DNS to CloudFront distribution
Route53 makes this dead simple. Create a new record set (ex:
http://www.rynop.com), type of “A”, alias “yes” and find your CloudFront distro DNS name. If it doesn’t auto-populate you can get the DNS name from the CloudFront web UI. It will be something like
Do this for all your www sub-domains (if you have more than one).
Step 5: Setup redirects to https://www.yourdomain.com
We want http(s)://rynop.org, http(s)://rynop.com, and http(s)://www.rynop.org to re-direct to https://www.rynop.com
To do this we will leverage S3 static website hosting redirects to another hostname and another CloudFront distro to handle the SSL.
Create one S3 bucket, it can be named anything but I recommend making it your root apex domain name, ex: rynop
On the bucket properties page, there is an Endpoint domain for the bucket. Copy this to your clipboard. Its in the format
Now go make a 2nd CloudFront distribution of type “web”. For the
Origin Domain Name paste the Endpoint domain from your clipboard. Leave
Origin Pathblank. Set
Viewer Protocol Policy to
Redirect HTTP to HTTPS . For
Custom SSL Certificate chose the cert you created in ACM. Leave
Default Root Object blank.
No go back into Route53, and create “A” record set of type “alias” for each domain that needs to be re-directed. Select the S3 bucket from the “alias target” dropdown. It may take a while to populate. Ex: I created A record set aliases for
Step 6: Verify SSL and re-direction
You can now validate the re-direct by running
curl -v "http://rynop.com"
You should get a 301 re-direct.
You can then put any of the domains you configured in your web browser and you should see the helo world html page.
Step 7: Compress and upload your Angular2 app
- Gzips your built assets (
- Upload all assets to s3 with the HTTP headers to allow CloudFront edge caching:
aws --profile rynop s3 cp . s3://www.rynop.com/ --recursive --include "*" --acl public-read --cache-control public,max-age=31536000,no-transform
These cache-control header values tell browsers and proxy servers it is OK to cache without modification, and CloudFront should cache for
index.htmlin CloudFront (you need to do this for all your assets, not just index.html):
aws cloudfront create-invalidation --distribution-id S11A16G5KZMEQD --paths /index.html
I highly recommend gzipping your assets BEFORE uploading to s3. CloudFront supports runtime gzipping however CloudFront will not gzip if its busy. All the consumers of your website have supported gzip compression for the last 15 years so you don’t have to worry about the Very: header.
You now have a single page app, who’s static assets have full HTTP/2 support, 11 nines of durability, infinite scale, free SSL, cached at the edge all over the world with zero servers to maintain and no ongoing renewals. You are ONLY charged for bandwidth and HTTP requests out of CloudFront (and the infrequent calls to your S3 origin).
Lots of setup here I know, but remember this is one time only and you are getting some world class features for almost nothing.
If you would like the webdev’s dream setup, I highly recommend checking out the Serverless framework for your backend (disclosure: I’m one of the original co-creators). It is open source and a completely kick ass way to create and manage an AWS backend that leverages Lambda and API gateway.