HowTo: Deploying Hugo on S3 and Cloudfront

I started using Hugo in January and ported 7 years of WP posts over the last month. Getting Hugo deployed on S3 and Cloudfront with the proper redirects, error handling, and SSL took up more of my weekend than I’d like to admit. I wrote it all up, and did it purely with the AWS CLI so you can automate it if you like.

I hope other people find it helpful: AWS Hugo Hosting, HowTo

Thanks everyone that’s worked to build Hugo, I love it!



Great write up, thanks for sharing it with us.
Did you also run into the problem of the css file not having the correct MIME type? I looked at your upload scripts and could not find such a thing.

I have to manually change the css file’s MIME type to text/css in a different command after I upload everything.

For deployment to S3 with Gzip and custom headers (Cache-Control, Content-Type (mime) etc.) with regexp matching, have a look at my little tool:

I guess I want https for my sites as well, so I’ll look into Cloudfront – thanks or the writeup.

awscli properly detected the MIME types on the CSS files. I just curl’d them and get

Content-Type: text/css

Note: I’m not pre-gzipping, because CloudFront now gzips the files as they pass through automatically. If you gzip them, then you need to set content encoding and MIME headers.

I’m using a recent version: aws-cli/1.10.8

That’s nice, I have been using s3cmd and it does not do that, I need to switch. Thanks for the information.

@parsiya @joseph.lust @bep @ArjenSchwarz

Glad I found this rather than creating a new comment. Excuse my backend-tech ignorance on this one.

I’m using Hugo to create a documentation site for internal purposes only at my nonprofit. The site is coming along great, but I’m wondering if there is a way to still host the site via Amazon (I believe our internal IS has a robust AWS package) but blacklist every IP outside of our internal network (thereby requiring VPN login for my colleagues to access the site). My original plan was to have this be a public site, but some of the higher-ups are of the mind that this is high-value intellectual property (it’s not, and nobody would care, and I always defer to open).

The ideal workflow is almost done: Local changes in colleagues text editor > Push to our private GitHub for Enterprise Repo > Wercker file watches for change and builds Hugo site (thanks again @ArjenSchwarz for all your help!) > Wercker pushes to Amazon hosting

I’ve got everything except the emboldened last step. Any help would be greatly appreciated!

Hey @rdwatters, it’s certainly possible to do keep the hosting on AWS.

S3 allows you to limit access by IP address, which is fairly trivial to set up. As it’s only going to be accessible internally there isn’t really any point to serving the site through Cloudfront. Let me know if that’s a requirement after all, but as that’s designed for speeding up global distribution I doubt you’ll need it. The only restriction is that the files obviously can’t be made public, but that’s not a problem when syncing it from Wercker.

It’s morning here right now, and I’ve got a couple of things I need to do first, but afterwards I can provide the steps you need to take for this.

@ArjenSchwarz You are freakin’ awesome. If my organization wasn’t so tight-wadded when it came to spending $, I’d pay you for your help. Seriously. Thanks!

(Thanks to @bep too, of course, since he answers my questions almost daily on this Discourse.)

Agree. His contributions on automation/deployment is solid gold.

Thanks for the two very detailed posts !

Speaking of price, I noticed on GitHub you said you wanted to host the blog for 1$ a year. But I wonder, is it really a possibility, with CloudFront on top of S3 ?

Is it possible to pay less than a small host with Amazon ? When I try to evaluate the price, I always have the feeling the cost of bandwidth would explose really quickly.

Anyway, it’s an interesting choice and I love the idea to host static files this way !

@rdwatters, @bep thanks for the kind words. The way I see it though is that we’re all working together to create something better while having fun :slightly_smiling:

Anyway, the promised writeup. I did it as a post on my blog to make it easier for anyone with the same question in the future.

Nice writeup. A little tip: In the “Error Document” field in S3, it makes sense to put in “404.html”. This is, I think, the only error type you’ll experience with S3.

Good point, I’ll add that to the post.

@bep @ArjenSchwarz Again, I can’t thank you enough for all your help. I think you’re both overseas, right? Maybe I can send you both some Chicago beer.

1 Like

Appreciate the thought, but the cost of getting it to Norway, with taxes and all, is not worth it. Lets grab a beer at the first HugoCon :slightly_smiling:


I have to agree with @bep here @rdwatters, it’s a kind offer but not needed. In my case it might even be worse as I’m in Australia which while it might not have all the same taxes as Norway it’s also a tiny bit further away :wink:.

@bep @ArjenSchwarz fair enough. I’ll definitely by the first round (or first three) for you guys at the first Hugofest.

1 Like

@rdwatters - @ArjenSchwarz’s solution is definitely an easy one to implement. However, it’s a bit limiting in that there’s a heavy maintenance burden if your VPN server changes IP or you need to add more services/sites to your lineup.

You could also consider using a private hosted zone in route 53 wherein the DNS only resolves if VPN’d in. Food for thought.

I see at the end of your blog post you didn’t automate the majority of the work you did. If you’re interested in the ability to spin up/down your site (and potentially many more just like it) with one command, check out my project. You’ll not only get infrastructure to host the code automated, but continuous deployment on every git push. Let me know if it’s helpful! At the very least, hopefully it can inspire you to automate some of your workflow!

Taking all the blog articles about setting up a static site on S3 with CloudFront and alike as input, I created a set of Terraform scripts that sets all this up for you. The scripts can be found here:

Hope this helps others in getting a site set up fast and easy. If you need modifications, feel free to submit pull requests!


I just wanted to say thanks for posting this.

I need to reply to this thread / leave this not here so people don’t fall into the same traps that I did.

Let me first define what i call the “index.html” behavior. Any time the web-server automatically adds “index.html” to the request. E.G. url to hXXp://some-s3-hosted-website/thing is actually being re-written on the fly to hXXp://some-s3-hosted-website/thing/index.html

Now, let’s talk about cloudfront.
When you use coudfront with S3, you have two options:

  1. the “use S3 just like every other webserver” option. When you use this option, you can still access the files inside of your S3 bucket. If you were using cloudfront to force users to HTTPS, they can just go to the http version of the URL that points directly to your bucket / bypassing cloudfront altogether.

In this setup, the index.html behavior is present

  1. the “lock people out of the S3 bucket, they must go through cloudfront!” option. When you set up cloudfront to use an S3 Origin instead of “regular web” origin, you can set up IAM policies that say which cloudfront users are allowed to access which files. Additionally, you can also prevent anybody from accessing the bucket content; forcing them to go through cloudfront.

However, in this setup, there is no index.html behavior.

See here:



(sorry, the form wont let me post more than 2 links…)