Static sites are the best. They’re fast, they’re simple, and they’re practically free to host. Since late January 2016, Amazon Web Services (AWS) has become one of the best static web hosting providers of all time due to their release of AWS Certificate Manager (ACM).
Using AWS, you can now:
- Deploy your static site into an Amazon S3 bucket and host it for almost no cost.
- Make your website incredibly fast by using CloudFront to proxy and cache your web traffic for almost no cost.
- Generate free, zero-effort SSL certificates for your websites that auto-renew without any effort whatsoever on your part.
- Easily automate deployments of your website to ensure things are always fast, cheap, and scalable.
This guide will walk you through everything you need to know to successfully run a simple, scalable, secure, and cheap static site on AWS.
Before we continue, you need to have an AWS account. If you don’t already have one, go sign up now.
You’ll also need to install the official AWS command line tool. You can do this using pip, the Python package manager:
$ pip install awscli
We’ll use the AWS CLI tool extensively later on to do things like deploy your website, invalidate your website cache, and a number of other things. It makes working with AWS a lot simpler.
Once you’ve signed into AWS, you need to generate an IAM account for yourself. This is a user account inside your main AWS account that can access various AWS services.
To create a new IAM account, follow these steps:
- Visit the IAM users page.
- Click the Create a new User button.
- Enter a username for your new account in the first input box, leave all other options alone, then click the Create button.
You’ll now see a confirmation page telling you that your new account has been created.
This page contains your user security credentials. These are your private API keys that are required to work with AWS.
Be sure to save these somewhere (we’ll need them later), but most importantly: KEEP THEM SAFE! Don’t publish these keys to Github, email them to anyone, or put them anywhere on the public internet. If you do this, someone will use your keys to do all sorts of nasty things on AWS that will cost YOU money.
Next, you need to give your newly created IAM user some access permissions. To do this, first return to the IAM users page and click on your newly created user account.
Now that you’re on the user page, do the following:
- Click the Permissions tab.
- Click the Attach Policy button. This will give you the ability to assign permissions to this user account.
- The first listed policy you should see is called AdministratorAccess. Select this policy only, then click the Attach Policy button.
This creates an administrator account that you can use to do work with any AWS services.
Now that we’ve got our user account setup, let’s configure the AWS CLI tool so we can use it to simplify some tasks later on.
Open your terminal and run the following command:
$ aws configure
This will open up an interactive prompt which will collect your AWS credentials you created in the previous step, and save them into your home folder so you don’t need to remember them for each command in the future.
Put your credentials in as requested, and leave all the default settings alone.
Now that you’ve got all the tools and accounts setup, let’s move onto the more interesting stuff!
Amazon S3 is where you’ll be storing your static site files: your html, css, js, images, videos, etc. It’s one of the cheapest and most popular file storage services on the internet.
S3 requires you to create a bucket, which acts like a directory for storing files. This is where you’ll store your static site files.
You’ll create one bucket for each website you want to host. S3 charges a little over 3 cents per month for storing each GB of data and 4/10ths of a cent for each 10,000 requests. S3 also charges 9 cents per GB to transfer data out to the public internet (+1 free GB of transfer per month).
Let’s say you run a rather large website that consists of roughly 1GB of text, images, CSS, etc. Let’s also say that this website gets one million page views per month.
If we were exclusively using S3 to host this website, we’d be looking at a monthly hosting cost of roughly 88 cents per month (this factors in 6GB of data transfer). Good luck finding a cheaper web host for a site with such high traffic!
Anyhow: back to creating a bucket.
Run the following command in your terminal to create a new bucket for your website:
$ aws s3 mb s3://www.mywebsite.com
$ aws s3 website s3://www.mywebsite.com --index-document index.html --error-document error.html
This will create your new bucket for your website (make sure you specify the full domain name for your website during this step), and tell S3 to treat your bucket like a static website, rendering index.html when a user visits the root URL, /, and showing the error.html file when a user tries to visit a page that does not exist.
The next thing you need to do is deploy your static files into your new bucket. When this step is done, you’ll have a functional website that you can test.
To deploy your website, run the following command in your terminal:
$ aws s3 sync --acl public-read --sse --delete /path/to/static/site/ s3://www.mywebsite.com
The above command will ensure that all the files inside of the /path/to/static/site/ folder are copied into your bucket. The –acl public-read option tells Amazon to ensure all the files you upload are publicly readable. The –sse options tells Amazon to store these files encrypted at rest. The –delete flag tells Amazon to delete any files from your bucket that don’t exist in your local folder.
Once this operation has finished, your site should be live, and you will be able to view it by visiting: http://www.mywebsite.com.s3-website-us-east-1.amazonaws.com
Just swap www.mywebsite.com for the domain name of your site.
By this point, you should have a working website that is hosted purely by Amazon S3. Now, if you wanted to, you could set up a DNS CNAME record right now and your website would be live — but that wouldn’t be very fun.
In the next section, we’ll speed your website up by using Amazon CloudFront.
The next thing we need to do is setup a CloudFront origin. CloudFront is a CDN service that Amazon provides.
The way this works is that when users visit your website, they will actually be visiting a cached copy of your website that CloudFront stores in various datacenters all over the world.
So, for instance, if a user visits your website from the west coast of the US, they will actually be getting a copy of your website from a server located in the west coast somewhere nearby. This geographically close mirror of your site will make your website much quicker to load than would otherwise be possible using Amazon S3 alone.
CloudFront can also compress your web pages to minimize bandwidth transfer, and terminate SSL so you can easily secure your site.
To start, visit the CloudFront dashboard and click the Create Distribution button, followed by the Get Started button in the Web section (make sure not to select the RTMP one).
Next, you need to:
- Put www.mywebsite.com.s3-website-us-east-1.amazonaws.com into the Origin Domain Name box. Don’t forget to substitute www.mywebsite.com with your S3 bucket name.
- Under the Viewer Protocol Policy section, select Redirect HTTP to HTTPS (because we plan on serving this site over SSL).
- Under Object Caching, click Customize then input 31536000 for all of the 3 input boxes: Minimum TTL, Maximum TTL, and Default TTL.
- Under the Compress Objects Automatically section, select the Yes bubble. This will turn CloudFront compression on for us automatically.
- Under the Alternate Domain Names section, input your real domain name. For instance: www.mywebsite.com
- Under the Default Root Object section, input index.html into the box. This tells CloudFront to render your index.html by default when a user visits your website directly.
- Under the SSL Certificate section, select the Custom SSL Certificate button. This is where we’ll generate a new SSL Certificate.
- Next, click the Request or Import a Certificate with ACM button (this will open a new window).
- Enter your domain name (www.mywebsite.com), then click the Review and Request button. Then confirm the request.
- Now, go check your email that is registered for that domain (whoever the admin of the domain is should receive an email), and click the verification link Amazon sends you. This lets Amazon know you are the owner of the domain and generates a new SSL cert for your website.
- Now, go back to the CloudFront configuration page you were on, and click the little refresh icon in the Custom SSL Certificate section. You can now select your SSL certificate from the drop down menu.
- Finally: click the Create Distribution button at the very bottom of the page!
Now that you’ve created your CloudFront origin, you will need to wait roughly 15 minutes for the origin to be provisioned in all the AWS datacenters around the world. If you take a look at the CloudFront dashboard page, you will see a State field that will read Enabled when the provisioning process has finished.
Once your origin is fully provisioned, you can view your website through the CloudFront CDN by clicking on your Origin in the CloudFront dashboard and copying out the Domain Name address that is listed on the page. This is your CloudFront origin public address. If you copy it into your browser you should see your website.
For me, this ended up being: https://d1p1m6ca3melg2.cloudfront.net/
Now that you’ve gotten everything set up and deployed, you need to get your actual domain name to render your CloudFront site.
To do this, you’ll need that CloudFront origin Domain Name you got after creating your origin.
Visit your DNS provider (I’m a huge fan of DNSimple), and add a CNAME record for your domain that points to your CloudFront origin.
For instance, if you wanted your website to be www.mywebsite.com, you would add a CNAME record on mywebsite.com that points: www -> xxx.cloudfront.net
This CNAME record is what will ensure that when users visit www.mywebsite.com, the CloudFront origin is what services that request from the nearest geographical location.
Once you’ve added this CNAME record, it may take up to one hour before your website starts working properly.
Once your DNS has been updated, visiting your website will initiate a request to CloudFront to display your website. You can also take note that if you visit your website over HTTP (eg: http://www.mywebsite.com), you will be automatically redirected to HTTPS (eg: https://www.mywebsite.com). This is a nice feature that CloudFront handles for us.
You now have a live website, served via a global CDN, that is fully secured via SSL for almost no cost =)
Now that everything is live and working, let’s talk about the process of automating your website deploys.
For people building websites that change frequently (blogs, company websites, etc.) — automating the deployment of your website is a huge timesaver.
The way I recommend you automate deploys is to use the Amazon CLI tool from earlier to:
- Sync the latest copy of your website files into your S3 bucket.
- Invalidate the existing CloudFront cache, ensuring that the new website files you just uploaded are used in the future. This ensures users visiting your site won’t see an older version of the site.
If you’re using an automated deployment tool like Travis CI, this process might look something like this:
$ pip install awscli
$ aws s3 sync --acl public-read --sse --delete /path/to/static/site/ s3://www.mywebsite.com
$ aws configure set preview.cloudfront true
$ aws cloudfront create-invalidation --distribution-id xxx --paths '/*'
This is the same deployment command as earlier. The new bits here are the CloudFront invalidation pieces.
The first CloudFront command is required as currently (at the time of writing) the AWS CLI tool has CloudFront features in beta, which must be enabled.
After the CloudFront beta has been enabled on the CLI, the final command will instruct CloudFront to purge its cache of all your cached web pages. Be sure to fill in the appropriate Distribution ID field here (and remove the xxx bit). You can find this value on the origin page after you visit the CloudFront dashboard.
NOTE: The CloudFront invalidation process will take a bit of time to process. It will happen asynchronously. You may need to wait around 15 minutes before the latest changes on your site appear.
If you’d like to see a full example of this in action (on a real static site), check out the Github repository for one of my pet projects, ipify: https://github.com/rdegges/ipify-www Specifically, take note of the .travis.yml file which contains the TravisCI automated deployment steps.
In this project, each time I push a change to the master branch, TravisCI will deploy my new changes to Amazon S3, as well as invalidate my CloudFront cache.
You may also be wondering how it is that I’m deploying an open source project on AWS from a public service like TravisCI — the answer is that I can hide my AWS API keys as encrypted environment variables using Travis. For more information on how you can do this, see the TravisCI encryption docs. Many other continuous integration / delivery services have similar functionality.
Finally, if you are deploying your websites using a public service like TravisCI, you will most likely want to create a new IAM user for the project, and instead of granting it administrator access like we did earlier, only grant it specific permissions to write to S3, and invalidate CloudFront caches. You can do this using the same method we did above, but only selecting the minimal permissions you need.
I hope you found this guide useful. Now, for a shameless plug.
If you’re interested in web technologies, and services that make your life as a developer easier, you should definitely give our service Stormpath a try.
Stormpath is an API service that stores user accounts for your web, mobile, and API services, and handles stuff like authentication, authorization, social login, single sign-on, multi-tenancy, etc. If it has to do with user accounts and user security: we make it easier.
Go check it out, sign up, and build something cool! I use it for all my personal projects and really enjoy it.