Using a Cloudfront CDN for Jekyll Without S3

Dec 5, 2016
Joe Buhlig
~5 mins

On occasion I find myself with a sliver of free time. And it seems inevitable that I use it to tinker with this site. Given my latest obsession with page load speeds, this time I directed my attention towards the implementation of a CDN.

This is an idea I stole from Brett Terpstra who has an article about his method of doing the same. My case has some slight differences and if you do a search for how to use Cloudfront with Jekyll, you’ll be hard-pressed to find an explanation of how to do it without using S3 for your hosting. Which is why I felt compelled to share my process with you.

I chose Cloudfront because I’m a fan of AWS (Amazon Web Services) and they are fairly cheap in comparison to the other big names in industry. I’ve worked with AWS enough in the past to trust it. But here’s how I set up Cloudfront to work with Jekyll:

Setting Up Cloudfront

Once you have an account with AWS, open Cloudfront from the management console and create a new web distribution. There are only three settings I changed from the default.

Origin Domain Name - This is your site homepage. I entered joebuhlig.com

Origin ID - This is merely a description you want for the origin. I used joebuhlig.com Resources.

Alternate Domain Names - This becomes the new base URL you’ll use to serve the files from Cloudfront. I was very original and used cdn.joebuhlig.com for this.

With the settings in place and the distribution created, you’ll be given a domain name for your distribution that looks similar to this: abcdefg123456.cloudfront.net We’ll use this to set up our DNS record.

Setting Up DNS

I’m not going into each registrar here, but the idea is to create a CNAME record on your domain with the host as your subdomain (in my case cdn) and the target as the domain name given for your distribution in the previous step (abcdefg123456.cloudfront.net).

You want to give this some time to populate and to allow AWS to do its magic. I left it alone and went to work on converting the URLs within Jekyll to accommodate the new CDN URL: cdn.joebuhlig.com.

Setting Up Jekyll

As a Jekyll user, you are probably familiar with using the Liquid tag {{ site.url }}. That variable is coming from the _config.yml file and we simply want to add cdn_url to it. Mine looks like this:

baseurl: ""
url: "http://joebuhlig.com"
cdn_url: http://cdn.joebuhlig.com

With cdn_url in place, you need to go through your static files and image references and change the base URL from a relative reference or {{ site.url }} to {{ site.cdn_url }} instead. I recommend previewing this before you deploy it just to make sure it’s working correctly and that Cloudfront is fully up and running. You can also test this by navigating to the base cdn_url itself and check if a copy of your site is available. If not, you need to wait for the DNS changes to populate.

Jekyll Development

A problem you’ll quickly run into is doing development of CSS and JS when your assets are being served from a CDN. In my workflow I run two commands: jekyll s --future to test and preview my site and JEKYLL_ENV=production jekyll b for a production build right before deployment. This allows me to use the Liquid variable {{ jekyll.environment }} to test if I am in production, like so:

{% if jekyll.environment == 'production' %}{{ site.cdn_url }}{% endif %}

If you use this in conjunction with the cdn_url, it means that when I’m building for production, the cdn_url is added and empty if it’s for development. I’m left with relative and local URLs in development and the full CDN URL in production.

Cache Busting

The last step here is adding a version number to the static assets that change fairly frequently. There are a number of ways to do this but I prefer the tried-and-true query string: main.css?v=5

Just like the cdn_url, I added version: 1 to my _config.yml file. That allows me to append any asset URL to look like this: /assets/main.css?v={{ site.version}} When used at the same time as the cdn_url, I’m left with this:

{% if jekyll.environment == 'production' %}{{ site.cdn_url }}{% endif %}/assets/css/main.css?v={{ site.version }}

Now, I’m lazy and don’t want to manually increment this version number every time I want to bust the cache so I again stole a page from Brett Terpstra’s book and wrote a rake task to do it for me:

desc "Bump version number"
task :bump do
    content = IO.read('_config.yml')
    content.sub!(/^version: (\d+)$/) {|v|
        ver = $1.next
        puts "At version #{ver}"
        "version: #{ver}"
    }
    File.open('_config.yml','w') do |f|
        f.write content
    end
end

Now all I need to do is run rake bump after I’ve edited static files and it will take care of the rest.