Overview

For work and personal reasons I wanted to know more about the world of static-site generators so I decided to explore what was out there and how they compared to one another. From my perspective, a static-site generator offers the best possible web experience because it can deliver the fastest page load times for the following reasons:

  1. There is zero time waiting for backend code to execute.
  2. Since all the pages are pre-generated a CDN can be simply put in front of your static site to serve the page from the nearest edge node.
  3. A very high cache control max age can be set which allows the browser to load more files/assets from its local cache as opposed to making a round-trip to web server.

For the static site generator evaluation I had 4 requirements:

  1. Proper asset minification and cache busting (i.e., including a hash in the filename).
  2. Automated deployment to AWS S3.
  3. Automated CloudFront invalidation on deployment (ideally only invalidating what has changed).
  4. A plethora of clean, device-friendly, and browser-friendly themes.

With those requirements in mind I set off to see who were the big names in the static site generator world. Although there are quite a few I settled on experimenting with Lektor, Hugo, Pelican, and Jekyll.

Lektor

While I think the idea of having a static-site generator that has a local CMS is brilliant this project is still very much in its infancy. This is evident from the lack of community support in the form of themes, plugins, and documentation. While I was able to find automated deployment to S3 and CloudFront invalidation Lektor’s lack of themes and proper asset handling did not meet all my requirements.

Hugo

Hugo is another new player to that static site generator world and its relatively large user community made it seem like another great option. I really enjoyed playing with Hugo because it was incredibly fast to compile your site and its live reload feature (it automatically injects a piece of javascript that refreshes the page when a change is made) made my workflow extremely fast. However, I still had issues with themes being very brittle and not displaying properly on small mobile devices, it has a dependency on Gulp to do asset minification and cache busting, and has no way to automatically invalidate CloudFront when a change is pushed to S3. The last thing that really irked me about Hugo is that it generated sloppy unformatted HTML that contained massive amounts of whitespace due to how the Go templating language functions. In Go 1.6 they did add the ability to remove whitespace by prefixing or postfixing the variable with a dash (e.g., {{- myVariable }} would remove whitespace on the left-hand side). I realize I could also use a tool like html-tidy to clean up the generated HTML, but I really wanted this feature as part of the static site generator and not a hack.

Pelican

Pelican was another clear choice in my opinion because it has been around for quite a long time and it is written in one of my favorite programming languages: Python! I knew right off the bat that the asset management for Pelican would be superb because I have used Python’s webassets for other projects. I then discovered that if you install s3cmd you can push your generated site to S3 with the Makefile that Pelican also generates. However, there is no support for invalidating CloudFront at this time, but I did see some interest in the form of GitHub issues.

Jekyll

The daddy of all static site generators is clearly Jekyll so I had very high hopes that Jekyll could deliver everything I wanted and sure enough it did not disappoint. Proper asset minification and cache busting with jekyll-assets, automated deployment to S3 and invalidation (including invalidating only the files which have changed) using s3_website, many really well done themes that support every major device perfectly, and a built-in way to pretty format the output HTML using jekyll-tidy. Clearly Jekyll met all of my requirements and through in a bonus feature with the pretty HTML formatting. There were some minor configuration stumbling blocks along the way, but eventually I was able to figure out how to get all of these pieces playing together nicely. For reference and to help those that may come to this page and want to achieve the same setup as I have I’ll go over some of my configuration settings.

Gemfile

If you’re using bundle as the Jekyll documentation recommends in order to add the additional features to have pretty HTML, asset minification, cache busting, S3 deployment, and CloudFront invalidation add the following lines to your Gemfile and run bundle install:

gem "jekyll-assets"
gem "jekyll-tidy"
gem "s3_website"

_config.yml

In order to enable these features on your site add the following lines to your _config.yml file:

# Include the additional gems
gems:
  - jekyll-feed
  - jekyll-assets
  - jekyll-tidy

# Exclude these files from the static site generator
exclude:
  - Gemfile
  - Gemfile.lock
  - s3_website.yml

# Instantiate jekyll-assets
assets:
    compress:
        css: true # compress and minify the CSS
    digest: true # Add the cachebust hash to the CSS filename

There are some additional steps for getting jekyll-assets to work which include organizing your assets as described in the README and adding the liquid tag {% css main %} in your template where you want your minified and cache busted CSS include to appear.

s3_website.yml

Last, but not least is configuring s3_website using the s3_website.yml file to upload your site to S3 and invalidate your CloudFront distribution. Although the majority of how to configure s3_website is obvious the only non-obvious parts are as follows:

# cloudfront_wildcard_invalidation: true
cloudfront_invalidate_root: true

exclude_from_upload:
  - assets/*json

In order to only invalidate what has changed on the site you need to comment out the line with cloudfront_wildcard_invalidation: true and uncomment the line with cloudfront_invalidate_root: true. Also including assets/*json under the exclude_from_upload section will stop s3_website from uploading intermediate sprockets files to S3.

Conclusion

If it wasn’t obvious by now in the end I decided to go with Jekyll. Although I am not as familiar with Ruby as I am with the other languages that the other static site generators used it was immediately clear to me that Jekyll had everything I needed and is the most mature static site generator available right now.