Hosting Hugo Site on CloudFront

Hosting Hugo Site on CloudFront

Recently I decided to start a blog to share what I learn. The variety of options for blog available out there confused me, also saddens me. Wix and any page builders are great; I can focus on creating my content, without having to write any css / html. However, I don’t have any control over the site, and it simply doesn’t look good for a developer. What about Wordpress? No thanks.

I came across Hugo and it is exactly what I need. It has tons of freely available themes created by the community, built-in support for GCP, AWS and Azure. Most importantly, all posts are written in markdown.

This post is meant to be an architectural overview of how to host Hogo site on S3 and CloudFront.


Local development

I started with the this tutorial and used this theme.

  1. For Mac, install hugo with brew. brew install hugo

  2. Verify you have installed hugo. hugo version

  3. Create your site. hugo new site yoursite

    This will createa Hugo site in a folder called yoursite

  4. Install theme.

    cd quickstart
    git init
    git submodule add themes/northendlab-hugo
  5. Add theme to the site configuration.

    echo 'theme = "northendlab-hugo"' >> config.toml

  6. Navigate to themes/northendlab-hugo/exampleSite and copy the data, content and static folders to the the base folder.

  7. Run hugo server -D and navigate to http://localhost:1313/. You will see the same website you see on their demo page

Upload to S3

Hugo has very handy integration with awscli. In the case of AWS, add your s3 bucket ARN to the config.toml file. For example:

URL = "s3://bucket-name?region=xxxx"

Also, you need to configure your awscli by adding your AWS key and secret. Once you have done that, build your site and upload to s3.

hugo -v
hugo -v deploy --maxDeletes -1

Hosting and HTTPS

You can host your website with S3, but in order to use HTTPS, you will need CloudFront. Create a CloudFront distribution and point your website to the distribution with A record and alias. Your CloudFront distribution will appear on the alias dropdown. The cost of running S3 and CloudFront is very low, so don’t worry just go ahead with it.

Continuous Delivery

You can leverage GitHub Action to deploy your website automatically once a new branch is merged into master. Here is a example workflow to help you do that.

name: Build and Deploy

on: push

    name: Build and Deploy
    runs-on: ubuntu-latest
      - uses: actions/checkout@v2
      - name: Install Hugo
        run: |
          tar xvzf ${HUGO_DOWNLOAD} hugo
          mv hugo $HOME/hugo
          HUGO_VERSION: 0.64.0
      - name: Hugo Build
        run: $HOME/hugo -v
      - name: Deploy to S3
        if: github.ref == 'refs/heads/master'
        run: $HOME/hugo -v deploy --maxDeletes -1
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}

You need to store your AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY in the project repo secrets to order for the workflow to work.

CloudFront Default Root Object

When setting up CloudFront, we need to specify Default Root Object to index.html, this will make sure index.html is served by default when trying to access our url.

Install Lambda@Edge Function

After I deployed to CloudFront, I was getting S3 XML error when I tried to access /contact. In order to route properly to /contact/index.html, we need to install this Lambda function here.

Error Page

We can specify which error page to show in response to different HTTP error. This can be done in CloudFront, at the Error Pages of your distribution.

Cache Busting

I will imagine Hugo has some built-in support for cache busting, but I haven’t look into yet. You can do it at CloudFront, simply create a invalidation for all the files.



S3 + Cloudfront with multiple edge locations makes our website highly available. In fact, I think this is more than what we need for a personal blog. Also, sitting behind aws resources, we are very well protected.


Almost nothing. In Canada, Cloudfront charges $0.01 for 10,000 HTTPS requests, and $0.085 for the first 10TB per month data transfered. S3 and Lambda also charge almost nothing.


Protect your aws secret key, and set up a specific group with least privilege access, i.e. only able to manage the S3 bucket we use.