Host websites with high availability and low latency for less than 1$/month, SSL included

I’ve been deploying static websites the wrong way all of my life. My procedure used to look like this:

  • Choose a server (EC2, Digitalocean) in a region (US, Europe, …)
  • Set up some nginx / apache configuration
  • Maybe add SSL through letsencrypt

I’ve always been aware that there are several drawbacks to this:

  • The smallest EC2 / digital ocean instances cost ~5$ which is quite a lot if you have many websites.
  • To make this cheaper, you start putting everything on one server (e.g. one ec2 small instance for 20$ / month), but if this instance crashes ALL your websites are down. You’ll have a very fragile availability.
  • By selecting a server in a specific region, the other regions have high latency
  • Adding SSL is kind of a pain, even if it’s free through letsencrypt
  • It’s not scalable
  • You need to mess with complicated config files from nginx / apache and need to ssh into your instance.

I’ve just never been aware that there’s a much better and easier solution!

The solution is called … (drumroll) … : AWS CloudFront.

Or to be less product specific, the solution is called: Put a CDN in front of some highly available data store.

Here’s what that means:

Your users will access the content through servers in the region closest to them.

This eliminates all of the previously mentioned drawbacks:

  • It’s cheap: AWS charges less than 1$/month for this setup
  • It’s got high availability: S3 has 99.9999999% uptime (I’ve randomly added 9’s)
  • It’s got low latency: Content is served through the data center closest to you.
  • You get a free SSL certificate from AWS that you can easily, no config file hacking.
  • AWS takes care of scaling for you
  • It’s simple to set up & deploy!

How to set up AWS CloudFront

AWS CloudFront is pretty simple to set up, but there are a few pitfalls, so here’s a tutorial with pictures to guide you.

Step 1: Host website with AWS CloudFront

Go to https://console.aws.amazon.com/quickstart-website/home; you also have that link on your dashboard.

Next, fill out the wizard:

and hit create. Your website needs to have an index.html file for this to work.

Tada. You have a working hosted website at some crazy url.

Step 2: Setup domain

Hit “Buy domain” on the website dashboard. This is the right button even when you’ve already bought you’re website through Route 53.

If you already have a domain, you can simply select it here:

Step 3: Set up SSL

Back on the website’s main page, select “Manage settings in Amazon CloudFront”:

There, click edit:

and select Custom SSL Certificate:

If you don’t have one yet, you can request a new one. It’s easy to set up, but you’ll need to have a working email address for the domain. E.g. when you want to set up example.com, you need e.g. admin@example.com to be working. I’m not going too much into detail here, since that’s a topic on it’s own.

Once the certificate has been issued and connected, SSL works:

Step 4: Pretty URLs and Subdirectories

By default, the files are hosted as they are: example.com/articles/my-article.html. Now how can you get rid of the html extension? You can either just remove the html extension or have a folder example.com/articles/my-article/index.html.

Unfortunately, there’s also one setting that needs to be changed for this to work. Head over to s3 > website bucket > properties > static website hosting and copy the “Endpoint” URL:

Then in Cloudfront > Origins

select the right website and hit edit, then replace the “Origin Domain Name” with what you’ve copied from the S3 bucket (without the http://).

Congrats, you’re all set up!

Step 5: Deployment

It’s a bit annoying to always have to upload a zip if you’re more the command line type of guy. Fortunately, it’s still possible to upload through command line with AWS cli:

aws s3 cp dist s3://<bucket-name>/ --recursive

You’ll need to set up IAM for this to work. Create a new user, give him S3 access, and hit:

aws configure

once the aws command line cli is installed.

Additional Tips & Tricks

There are a few gotchas you’ll have to be aware of when setting up CloudFront.

Forwarding the Naked (Apex) Domain to the WWW domain

In order to achieve a forward from https://example.com to https://www.example.com you’ll have to use a really weird workaround: Create a new S3 bucket with the name example.com. It HAS to be example.com, you can’t give the bucket any other name. Then in properties > static website hosting redirect all requests:

Finally, in Route53 resolve example.com (or here tsmean.com) to this S3 bucket:

If the S3 bucket name IS EQUAL TO THE RECORD NAME, the S3 website bucket should show up when you select Alias Target. That’s it, that way requests to the naked (apex) domain can be forwarded to a www domain.

Cache Invalidation

If you’ve followed the “one server deployment paradigm” so far, you’re probably used to seeing your changes visible immediately after deployment. This is not the case when using AWS CloudFront. Since it’s a distributed system with servers at edge locations that cache your assets, those caches have to be invalidated first. AWShas two articles on this, first http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Invalidation.html and second http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/ReplacingObjectsSameName.html.

They advise you against using their built-in cache invalidation mechanism since it becomes expensive above a certain number of requests. However, this free limit is 1000 requests per month as of August 2017. That’s more than enough for most people since you can declare wildcard paths and those are counted only once, even if thousands of requests are made! So you could just specify “*” as invalidation and your cache is cleared with one request:

I personally prefer this to versioned object / directory names since I find it easier.

Conclusion

Compared to AWS CloudFront, deploying to single instances just feels hacky and wrong now. It’s simpler, cheaper and safer with AWS CloudFront. It’s the solution I’ve always wanted, but didn’t know existed. Hope this tutorial helps someone!

Leave a Reply

Your email address will not be published.