A static website is a great choice for content that doesn’t change often, and isn’t dynamic or unique to the user. Blogs, documentation, and cooking recipe sites are examples of static website use cases.

A banking or social media app would require authentication, dynamic data, etc - better suited for a web application, or Single Page Application (SPA).

How I made this blog

I wanted to keep it simple and avoid heavy blog tools like WordPress.

Some research for blog platforms took me to the Hacker News thread Ask HN: Best Blogging Platform, where Hugo was recommended. Hugo is a static site generator with a great ecosystem of themes. I chose the harbor theme, which has an appealing minimalist style.

The output of Hugo is a static site, consisting of several html/css/js files, and other common website artifacts like font files, icons, and images. A great hosting option for a static website is AWS S3. This walkthrough from the AWS docs gives detailed steps to set up the AWS resources for static website hosting.

This second walkthrough from the AWS docs sets up CDN and https.

Several AWS resources are involved:

Technical Details

While following the above walkthroughs, I ran into a few quirks or issues, detailed below.

Nameserver mismatch between Hosted Zone and Domain

When I created an SSL certificate in AWS Certificate Manager, I added some CNAME records to my Hosted Zone, to prove ownership. I noticed that the Certificate status didn’t update to “Issued” for several days. I realized that, when I had created a Hosted Zone for my Domain, managed in AWS Route 53, there was a mismatch of nameservers. The Hosted Zone referenced one set of Nameservers, and the Domain referenced a different set. I updated the Domain (which I had ignored for several years) to reference the same set of Nameservers as the Hosted Zone.

Here are some details from the AWS docs: Adding or changing name servers and glue records for a domain

CORS issue between www subdomain and domain

I noticed that, when visiting the site using the www subdomain (ie, www.bfostdev.net), some static files like CSS, served from the bfostdev.net domain, were blocked by a (lack of) CORS policy. I fixed this by updating the CORS policy on the domain (bfostdev.net) S3 bucket, allowing GET requests from the origin https://www.bfostdev.net, with the policy JSON shown below:

[
    {
        "AllowedHeaders": [],
        "AllowedMethods": [
            "GET"
        ],
        "AllowedOrigins": [
            "https://www.bfostdev.net"
        ],
        "ExposeHeaders": []
    }
]

Deploying changes

For now, I’m pushing changes semi-manually to production. This involves 3 commands, which:

hugo -D
aws s3 sync --delete ./public s3://$BUCKET_NAME
aws cloudfront create-invalidation --distribution-id $DISTRIBUTION_ID --paths /*

Reference Material

By the time I finished, I had accrued many open browser tabs - the sign of honest work! Here are some references that I didn’t mention above. This is for my benefit as much as anything else.

https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/CreatingHostedZone.html

https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/dns-configuring.html

https://docs.aws.amazon.com/AmazonS3/latest/userguide/website-hosting-cloudfront-walkthrough.html

https://docs.aws.amazon.com/AmazonS3/latest/userguide/IndexDocumentSupport.html

https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Invalidation.html#invalidating-objects-api

https://docs.aws.amazon.com/AmazonS3/latest/userguide/website-hosting-cloudfront-walkthrough.html

https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/dns-configuring.html

https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/CreatingHostedZone.html

https://aws.amazon.com/blogs/networking-and-content-delivery/implementing-default-directory-indexes-in-amazon-s3-backed-amazon-cloudfront-origins-using-cloudfront-functions/

https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-restricting-access-to-s3.html