Hosting a static site in AWS for cheaper than a cup of coffee

Table of contents

The Components

These are the main pieces used to build this website. If you’re here, you probably already know what these three components are. If you don’t, here’s a brief overview and linked documentation if you need to read into it further. This is a very cheap, fast, and fairly easy way to host a static website.

Hugo

According to their site:

“Hugo is a fast and modern static site generator written in Go”.

A static site generator is a tool that takes simple text input + markup language, and generates an renderable HTML file. This makes building static content much faster and simpler, because you don’t have to build out content using HTML/CSS syntax. Although if you’re not happy with certain aspects of your site, editing the “theme” or template is as simple as browsing to the CSS or HTML file and tweaking something there if necessary. But for the most part, you’re just writing simple text (like I am now). The exception is when you want to use markup to format your text. I personally use this cheat sheet to reference markup occasionally.

AWS S3

AWS S3 is a service provided by Amazon Web Services (AWS). It’s basically just flat object storage, organized into “buckets” which is the name of the container of data. Its role here is just a place to store our Hugo-generated HTML/CSS and static objects somewhere where CloudFront can find them. It’s perfect for simpler static sites because it’s incredibly cheap (like less than a dollar a month for most sites cheap) and super easy to configure/deploy. You can read more about it here.

CloudFront

CloudFront is a CDN service, native to AWS as well. It’s serving two roles here:

  • CDN to serve our code stored in S3
  • Negotiating and building SSL

A CDN is a Content Delivery Network. It’s basically a service that caches our served site from S3 on servers distributed all over the globe, and those servers act as proxies between your browser and where the code is permanently hosted. So when you browse to this site, instead of browsing directly to the S3 bucket to request a webpage, you’re actually just communicating with CloudFront which is serving you the cached version. Why is this necessary? Well one of the downsides of S3 is it’s fairly slow to load. It also doesn’t support HTTPS if you’re hosting a static site in a bucket. So CloudFront can be used as a way to cache the site on a server that’s much closer to you, and also do all of the SSL building via a configured certificate. You can read more about it here.

Honorable Mentions (Not required, but kinda cool)

Zoho email service

Zoho is one of the few legitimate business suite services that has a completely free tier. Their Forever Free Plan costs nothing as long as you stay under their 5GB/25MB attachment limit, which is still fairly generous. Got cash to burn? They offer a “Mail Lite” plan for a whopping $1 a month, but it comes with a myriad of additional features. Most bloggers and small businesses even, will get by just fine with the free plan. Their mobile app makes it easy to check mail as well.

Route 53 DNS

If you’ve bought your domain already, there’s a good chance you’ve probably already setup or plan to use the domain registrar as your DNS resolver too. I was in that boat when I bought my domain from GoDaddy like many times before. Until I tried Route 53, which is the DNS service hosted on AWS. Using their DNS service made the whole setup much easier. This isn’t free (.50 cents per DNS Zone) but pretty dang close, as long as you’re not getting millions of resolutions for your site a month. Even then, it’s very affordable (less than a dollar a month).

All-in-all, all the above costs about .60 cents to a dollar to host per month (depending on site size and popularity). Yes. Less than a dollar per month for a modern, fast, static website. And the first year in AWS is free. This INCLUDES having a custom email address with your domain.

Step-by-step Guide

These guides from AWS are pretty helpful if you get stuck somewhere to help fill in any gaps.

Hosting a Static Site in S3
Using CloudFront to Serve HTTPS for your S3 bucket

Setup S3

  • Login to your AWS console and browse to the S3 portion. Create a bucket a bucket with the same name as the domain you’ve purchased (i.e. sabersecurity.io).

  • For the region, it’s generally best to keep this the same throughout your AWS account to avoid confusion/complexity. Just choose one that’s closest to you and stick to that.

  • Under the public access for the bucket, uncheck “Block all public access” (we want to allow our site to be reachable by the internet).

  • Leave the rest and hit create. Your bucket should be provisioned. Now click your bucket and head over to “Properties”.

  • Near the bottom, click edit on “Static website hosting”. Enable this, and set the index document to “index.html” then save.

  • Lastly for your bucket setup, go to the “Permissions” tab and click edit on your Bucket policy. We want to set the policy to allow it to be readable by anyone (replace my site with yours, leave the rest):

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "PublicReadGetObject",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::sabersecurity.io/*"
        }
    ]
}

Request SSL Certificate

  • Browse to Certificate Manager in AWS to request a certificate for your site. This isn’t required but highly recommended if you want anyone to trust your website.

  • Click “Request”. Then public certificate. In the certificate details, for the FQDN make this identical to your site name (i.e. sabersecurity.io). Also add another name with your same domain but “www.” in front. Then click Request.

  • For Validation method, select DNS validation if you own your domain and have access to DNS settings in the registrar (or using Route 53).

    • NOTE: If you have your domain on a different registrar than AWS, but want to use Route 53 for DNS, I recommend moving your designated name servers first before continuing. If you use GoDaddy, follow this guide. Otherwise just search “migrate nameservers from x to Route 53” on Google.
  • A message at the top will appear, if you click on it you can see the status of your cert request. We want to take the CNAME values provided and enter them into our DNS settings on our registrar/Route 53. If using Route 53, just click “Create Records in Route 53” and it will do this for you (this is why I recommend just using Route 53).

  • Once the CNAME records are added to your DNS settings, it will take a few min usually to verify your ownership of that domain, and the certificate be approved.

Setup CloudFront CDN

  • Browse to CloudFront in the AWS console. Click “Create Distribution”.

  • Fill out the following fields, leave the rest blank/default, then click Create:

    • Origin domain: Go back to your S3 bucket properties in a seperate tab, at the bottom where we enabeld static hosting copy the url to your site. It should look like http://sabersecurity.io.s3-website-us-east-1.amazonaws.com. Paste this url into the Origin domain field.

    • Viewer protocol policy: Redirect HTTP to HTTPS.

    • Alternate domain name (CNAME): input the same two CNAME records you used for your certificate request.

    • Custom SSL certificate: If your certificate request has been approved, you can click the drop-down and select it.

    • Default root object: index.html

  • You should now have a CloudFront distribution generated. It can take 20 or so minutes for it to be enabled.

  • Once enabled, click on the distribution ID, and copy the distribution domain. We want to create two A records in our domain’s DNS settings with this domain pointing to our purchased one. They should look something like this:

    https://imgur.com/a/cPvAmPh

    • NOTE: If using Route 53, you will have to make this an “Alias”, because usually A records are used for domain -> IP translation.

Create and upload your site

  • At this point, your cloud infrastructure should be up and running, and is just waiting for something to serve to browsers. You can test it by saving a file as index.html with the following contents and uploading it to your S3 bucket:
<!DOCTYPE html>
<html>
    <head>
        <title>Test</title>
    </head>
    <body>
        <p>Your site is working!</p>
    </body>
</html>
  • Once the file is uploaded, browse to your website URL and it should render the text in the code above. If it doesn’t work, you may have to do some additional troubleshooting and double checking the linked AWS articles above.

  • If it’s working, and you’re planning on using Hugo, you can now browse to the Themes page and pick out a theme that suits your website purpose.

  • I can’t do a better job than Hugo already has at documenting their simple setup process, so just follow that here (it’s super easy): https://gohugo.io/getting-started/quick-start/

  • Once your site is setup, and you’ve run Hugo -D to build it and generate the “Public” folder in your site directory, browse back to S3 and upload all the contents of that Public folder (not the folder itself). This should include the key index.html file that your CloudFront distribution is looking for.

    • NOTE: If you ever make changes to your S3 contents, you will have to invalidate your CloudFront cache to serve the new content. This is because CloudFront only caches every 24hrs or so, and will not check your S3 bucket for changes for a while. To do that follow these steps. You can invalidate 1000 objects a month before you start gettings charged for it (even then it’s super cheap).
  • Once uploaded, it make take a second for everything to get cached and ready to go. But now you should be able to browse to your site URL and see your brand new shiny site in all its glory!

If you have any questions, or something is out-of-date here, feel free to email ivan@sabersecurity.io

Posted 11/10/2022