From 0 to HTTPS-enabled Portfolio in 15 Minutes with Hugo, S3, and CloudFront

No, really, we’re going to set up an HTTPS-enabled portfolio, or blog, or other content-oriented site, from scratch, in 15 minutes, and we’re going to do it all for free. Ready?


  • A neat domain

    Who doesn’t have a cool domain just sitting there, not serving up anything fun and exciting? If you’re one of those people, head over to a reputable registrar like Namecheap and grab yourself a domain.

  • A credit card

    Signing up for AWS requires a credit card, even though you’ll be on the Free Tier for a year. Continue reading below.

  • Git and npm

    Every web developer should have these installed.

Step 1: Sign up for Amazon Web Services (AWS)

We’ll be using S3 and CloudFront, which are both AWS services. AWS has a generous Free Tier which covers both of these services for an entire year. After that, you’ll be paying just pennies per month, or perhaps a few dollars if your site sees a lot of traffic. See Amazon S3 Pricing and Amazon CloudFront Pricing.

Step 2: Install and configure AWS CLI

The AWS command line interface simplifies a lot of tasks. Let’s install it now.

To configure the AWS CLI, we need to create an IAM user with the permission to interact with our S3 buckets. You can do this through the Security Credentials link under your name in the navigation area. Go to Users, create a user, and in the Permissions tab, attach the AmazonS3FullAccess policy.

Once the user is created, we can configure the AWS CLI profile that we’ll be using for our deployments:

aws configure --profile agop

I’m using agop as my profile name. Enter the keys that you were provided with when the IAM user was created.

Step 3: Create an S3 bucket to store your website files

Create a bucket for your files:

aws s3 mb s3://agop-me --profile agop

I’m using agop-me as the bucket identifier. Then, create and upload a simple file for testing:

echo "WIP" > index.html

aws s3 cp index.html s3://agop-me/index.html --profile agop

You can also perform these steps through the web-based AWS Console interface.

Step 4: Create a CloudFront distribution to act as your server

In the AWS Console, open the CloudFront service. Then, click Create Distribution, and click Get Started under Web Distribution. This is going to be the longest step, so pay close attention.

Click into the Origin Domain Name field, then select your S3 bucket from the dropdown list. Leave Origin Path empty, and keep the automatically generated Origin ID (looks like S3-agop-me for me).

Set Restrict Bucket Access to Yes, choose Create a New Identity, and name your identity (I named mine access-identity-agop-me). Make sure to select Yes, Update Bucket Policy. By using these settings, only your CloudFront distribution will be able to read from your S3 bucket.

Proceeding onto Default Cache Behavior Settings, choose Redirect HTTP to HTTPS for Viewer Protocol Policy. Leave the Allowed HTTP Methods as GET, HEAD. Leave Forward Headers as None.

For Object Caching, choose Customize. Set Minimum TTL to 0, Maximum TTL to 300, and Default TTL to 300. This tells CloudFront to cache our files for no more than 5 minutes at a time. These settings are a good default for content that changes often. If your content changes less often, feel free to set the TTL to 15 minutes, 30 minutes, or even several hours. (In a future post, we’ll cover adding additional cache behaviors to cache CSS and JS files for much longer periods of time.)

Leave Forward Query Strings as No, Smooth Streaming as No, Restrict Viewer Access as No, and Compress Objects Automatically as Yes.

Moving onto Distribution Settings, choose Use Only US and Europe to keep your costs to a minimum once your Free Tier runs out. Leave the AWS WAF Web ACL as None.

For Alternate Domain Names, enter your domain. For example, I entered The SSL Certificate field should have two options, and the Custom SSL Certificate option should be grayed out. Leave it as Default CloudFront Certificate for now, but click the Request an ACM certificate button.

This will take you into the Amazon Certificate Manager, which allows you to grab a free SSL certificate for your domain instantly. Just enter your domain (again, I entered, then hit Review and request. ACM will send a verification email to a bunch of different email addresses ending in your domain, including webmaster@, admin@, postmaster@, and a few more. Make sure that you can receive at least one of these emails! If you don’t have an email service attached to your domain, most registrars (Namecheap included) allow you to set up email forwarding for free. If you run through this step before setting up at least one of these email addresses, just go back into ACM, select your pending certificate, and choose Resend verification email in the actions menu.

While you’re waiting for the verification email, go ahead and complete the distribution creation process. For Custom SSL Client Support, pick Only Clients that Support Server Name Indication (SNI) (this is supported by all major browsers). For Default Root Object, enter index.html. Leave Logging and the other settings at their defaults (mostly Off), then create the distribution.

Once created, you should see a domain like - that’s your CloudFront domain. Copy that, then go into your domain settings in your registrar’s control panel, and add a CNAME record for your domain (the root of your domain is usually indicated by an @ symbol) with that value followed by a period, like - the period at the end is important. Save your domain settings.

Now, head back into CloudFront. By now, you should have received and accepted the verification email for your SSL certificate. Click your distribution, edit it, and the Custom SSL Certificate option should no longer be grayed out. Select it, then pick your newly issued SSL certificate from the dropdown below. Save the distribution.

At this point, CloudFront and your domain are fully set up. They’ll probably take a couple of minutes to be ready, so proceed onto the next steps. In the meantime, keep checking both your CloudFront domain (e.g. and your custom domain (e.g. Since we uploaded a test index.html that says WIP, you should see WIP on your screen sooner or later.

Step 5: Install Hugo

Hugo is an extremely fast static site generator written in Go. It’s fairly similar to Jekyll and other static site generators. I like it because it doesn’t have any dependencies and it’s very easy to get started with.

Grab the installation file from Hugo’s GitHub Releases page, then install it.

Step 6: Set up your site with Hugo

The hugo new site <site-name> command creates a directory with a baseline configuration file for your site:

hugo new site agop
cd agop

I’m using agop as the site name.

Step 7: Install a Hugo theme

Head over to Hugo Themes and pick a theme. I like Beautiful Hugo, so I’m going to install that. Installing a Hugo theme is as easy as cloning its repository into your themes directory:

mkdir themes
cd themes

git clone beautifulhugo

I told git to clone it into the beautifulhugo subdirectory of themes.

Step 8: Set up your Hugo configuration

Hugo already gave you a basic configuration file, but there’s a lot more you can do (see Configuring Hugo). Most Hugo themes come with an example site which includes a configuration file that you can use. Go ahead and copy that file to your local config.toml, then change it as desired.

The most important changes you should make are:

  • Change baseurl to your site’s URL, like
  • Add uglyurls = true. This will generate pages ending in .html instead of directories with index.html files. AWS CloudFront does not support the latter. (There’s a way around that by using the S3 bucket as a web server, but we’ll get into that in a future post.)

Most of the other options will work fine with their defaults.

Step 9: Set up your first post

Let’s create your first post:

hugo new post/

Hugo will create content/post/ and automatically add some frontmatter (like when the post was created). Feel free to add some markdown text below the frontmatter.

Step 10: Set up npm

Set up a package.json file:

npm init

Then, edit it to include some helpful scripts:

"scripts": {
	"server": "hugo server -w -v",
	"build": "hugo -v",
	"deploy": "aws s3 sync public/ s3://agop-me/ --delete --profile agop"

npm run server runs Hugo’s built-in server, allowing you to test your site at http://localhost:1313. It also watches for changes and automatically reloads the page when necessary (due to -w). Note that Hugo’s built-in server does not populate the public/ directory by default (instead, it builds and stores the pages in memory).

npm run build builds your site and places the deployment-ready files in public/.

npm run deploy takes all of the files in public/ and automatically uploads them to your S3 bucket. It also deletes any outdated or unnecessary files in the bucket (due to --delete).

Step 11: Test your site!

As seen in the previous step, run the Hugo server:

npm run server

Open up localhost:1313 and you should see your new site! Make some changes to your content and watch your site reload automatically.

Step 12: Build your site and deploy to your S3 bucket

Simply run:

npm run build
npm run deploy

And you’re all set! Go ahead, visit your site. In my case, it’s

How to update your site

Working on your site is easy. Run npm run server to review your changes locally, then run npm run build and npm run deploy to build and deploy to production.

You may wish to empty the contents of your public/ directory before running the build to get rid of any outdated files.


Comment below!

comments powered by Disqus