If you are using a static site builder like Jekyll, Amazon S3 is one of the cheapest hosting solutions around. To speed up delivery time for a little extra cost, I recommend using Cloudfront in front of S3.
A few months ago I decided to serve my static assets from S3, but yesterday I decided to take the full jump. Here is what I did to make the move.
This full guide will be most useful for static sites on existing hosting solutions that you are moving to AWS while using outside DNS services. This guide uses s3_website, Amazon S3, Amazon Cloudfront, and a non-Amazon DNS service.
If you are setting up a site from scratch, use Amazon’s guide instead.
0. Back up your stuff
The steps below worked for me, but I can’t guarantee everything will work for you. Back up your stuff so you can roll back. You’ve been warned.
Really, before you make a mistake and mess a bunch of stuff up, make sure you have a back up of everything:
- Your local Jekyll site
- The content on your existing webserver
- Screenshots of your current DNS settings
1. Set up your AWS account
Set up an account at Amazon Web Services.
2. Download and configure s3_website
s3_website is a very useful tool for automatically setting up buckets on S3 and a Cloudfront distribution to host a static site. It also helps easily deploy updates to your static site with a few commands.
Follow the tool’s instructions on Github.
s3_website cfg create. This generates a configuration file called s3_website.yml.
- You’ll need to make an AWS user for the tool with access keys and set your configurations in the
s3_website cfg apply. This will configure your bucket to function as an S3 website. If the bucket does not exist, the command will create it for you. When you run this, it will ask you whether you want to deliver your website via CloudFront. If you answer yes, the command will create a CloudFront distribution for you.
Handling non-Jekyll content
If you have content on your webserver that doesn’t exist in your Jekyll site, you’ll need to set some extra configurations for those things in the
s3_website.yml. For example, I serve a bunch of PDFs that are separate from my actual Jekyll instance. If you do, too, make sure you take advantage of the
ignore_on_server section of the configuration. Make sure to uncomment it (remove the
# in front of it) and list the folder paths from the root that you want the tool to ignore on the server. Otherwise folders not on this list and not in your Jekyll site will be removed.
I added these:
ignore_on_server: - justanswer - illum
Reminder: Don’t use tabs for formatting in YAML files. Use spaces.
3. Deploy your website using s3_website
Run a new build on your Jekyll site:
Then, push up to the AWS environment:
Don’t worry, even though the
s3_website.yml gets added to your
_site folder, it won’t get uploaded to S3. The tool skips it. I triple checked this because I didn’t want to publish my access keys online.
Wait 15 or 20 minutes for your site to propagate across Cloudfront (the Cloudfront dashboard shows the propagation status.) Once it does, check your Cloudfront domain (or public S3 domain if you aren’t using Cloudfront). The s3_website tool gives you both domains during your initial setup, or you can always get them from your AWS console at any time. For example, my Cloudfront domain for this site is
d27zm8z2abfvhn.cloudfront.net and my S3 domain is
Check the domains and make sure everything seems to be live and working correctly.
4. Add your own domain to Cloudfront or S3
First, add your domain, including the www, as a CNAME on your Cloudfront distribution. This allows Cloudfront to serve requests to this domain once it is point there. This is the easy part.
Pointing your domain at Cloudfront without using Route 53 as your DNS provider
I have a bunch of DNS records on cagrimmett.com, so I didn’t want to migrate my DNS over to Amazon’s Route 53. That means I had an hour of trial and error (as well as a consultation from my friend Eric Davis) figuring out how to get things to work. Also, the typical A record pointing won’t work because the IP addresses to Amazon’s servers change regularly.
The solution we settled on is: CNAME the
www subdomain to Cloudfront and forward all
cagrimmett.com requests to
To CNAME your
www subdomain to Cloudfront, just log in to your DNS service and add a new CNAME record for www (or change the existing one if you have it) and point it to your Cloudfront domain, which is the one that looks like this: d27zm8z2abfvhn.cloudfront.net.
You have two options for forwarding non-www requests to the www subdomain:
A) If you aren’t using your domain for anything except this site, you can use your DNS service’s domain forwarding tool if it has one. Set it up to forward
http://www.domain.com. Note: If you are using your domain for anything else or if any subdomains point to the @ record, don’t use this option. I tried this and took down my other sites, which are served from subdomains of cagrimmett.com.
B) Use Apache or nginx to rewrite requests. This means that you still need your original hosting, but it will only be used to rewrite your domain requests. This isn’t an issue for me because I still need my hosting to serve cooklikechuck.com, amandakate.me, and my dev and staging areas for WordPress development. This option isn’t ideal, and definitely not what I would have designed from the beginning, but I don’t want to burn everything down and start over. So Apache it is. Here is what my rewrite rule looks like in my
RewriteEngine On RewriteRule ^(.*) http://cagrimmett.com/$1 [QSA,NC,NE,R,L]
This takes any non-www requests and rewrites them to the www subdomain, preserving the whole query string, making sure existing links to your content around the web don’t break.
Wait an hour for all changes to take affect and you should be good to go!
5. Deploying updates
Since we used s3_website, deploying updates and new posts to your site will be super easy:
Navigate to your Jekyll site folder and build your Jekyll site:
Then push up to the AWS environment:
A note on SSL
I opted to not use SSL for my site right now because there are a ton of extra steps and extra cost involved. I’ll probably revisit this later when Google starts penalizing non-secure sites, but for the time being I’m sticking with plain http. I’m not collecting any user-submitted data.