Building a Blog with Jekyll Static HTML

Posted on 27 Mar 2020 by Ray Heffer

But… Wordpress!

Without a doubt, Wordpress is the most popular and versatile blogging platform available today. It is used by both individual bloggers and large organizations alike. According to WordCamp, over 75 million sites are running on Wordpress around the world. One of the fundamental problems with this popular blogging platform, or more precisely, content management system (CMS), is keeping it secure. With over 50,000 plugins and the ease of installation, it is no wonder it has become the platform of choice by millions of websites. Unfortunately due to it’s popularity, it will continue to be pwned on a daily basis.

Wordpress can be a secure platform if deployed and managed properly. However, there will continue to be zero-day exploits that exist in many of the plug-ins used by the millions of Wordpress sites around the world. Whether Wordpress itself is secure is only part of the story. If you run a Wordpress blog, how often is the underlying server OS patched? What about the plugins, and Wordpress itself? While an unpatched server or vulnerable plug-ins is one such attack vector, there are numerous others including brute forcing XML-RPC, and exposed wp-config.php files on GitHub.

Poor implementations of the underlying server architecture is also to blame. Wordpress requires a database, and there is no way around that. Often MySQL is installed on the webserver along with everything else. This is a LAMP stack after all! But, one of the issues with this is how MySQL is managed, using add-ons like phpMyAdmin or cPanel. As I write this, cPanel has over 360 known vulnerabilities and phpMyAdmin has over 250. Don’t get me wrong, tools like this make it easy to deploy you own Linux server and build your website from scratch, but it is a wornderland for hackers.

Those plugins though? These can be used by hackers to gain access to important files such as wp-config.php, using vulnerabilities. This is why it is so important to patch your Wordpress server and associated plug-ins, but even patching won’t protect you from a zero-day attack. These are the exploits we don’t know about, so patching is potentially useless against zero-day attacks. For example, in 2015, an exploit was found in the Slider Revolution (revslider) plug-in, that allowed attackers to access wp-config.php, among other critical files on the web server, by manipulating the URL (action=revslider_show_image&img=../wp-config.php) to gain access.

The answer to securing Wordpress lies in the adoption of multiple layers of security.

  1. Use multi-factor authentication for /wp-admin
  2. Use .htaccess to restrict admin access to a single or range of trusted IP addresses
  3. Eliminating unnecessary plugins
  4. Don’t use phpMyAdmin or cPanel!
  5. Keep Wordpress and plugins up to date
  6. Hardening the host OS on which it is running. That means, no FTP, use SSH key based authentication, SELinux, just to name a few.
  7. Adopt some common sense.
  8. If you are using Git, don’t include secret keys in your commits!

I provide guidance on hardening a Wordpress server in my previous blog and ebook on building a secure Wordpress server with CentOS 7.

Why I chose Jekyll

As much as I love (and hate) Wordpress, I spend much of my day focused on IT security. It would be pretty reckless of me if I didn’t heed my own advice. So around a year ago I decided to migrate my entire blog from Wordpress, which had been running since 2010, to a static site. Security aside, static sites are fast. Hit that refresh button now, and you’ll see how fast this page loads. Using GTmetrix, I tested the load speed of my home page, and it renders in less than a second with a straight A score. Images will slow the page load speed down, but remember this is a blog, not a bare-bones search site. Regardless, a page load speed of 0.8s is nothing to be sniffed at.

Migrating from Wordpress

Yes, about that. It’s a royal pain in the scrotum. If you only have a dozen or so blog posts, then I’d suggest you just do this manually, but if like me you have hundreds of blog posts then there are some options. The WP Gatsby Markdown Exporter plugin is one option. Whatever method you choose, believe me when I say that Markdown will be your friend!


What if I told you that I use Markdown for my note taking on a daily basis? It’s not like editing HTML, although Jekyll allows for that too. Markdown is actually a really easy way to write content. In fact, when I ran my website on Wordpress, I used the Markdown feature of Jetpack. I am writing this blog write now using Markdown. I use an app called Joplin to take notes. For the first few days while I was getting used to Markdown, I used this site:

Jekyll adds further functionality by allowing for the insertion of images for example. The Unicorn at the top of this post, which is from the 36th Chaos Communication Congress (36C3), was inserted by using this line:


To add some styling to the image using the Bootstrap CSS framework (see below), I can add a class using {:class="img-fluid rounded pb-3"}. It then becomes ![](/assets/images/36c3unicorn.jpg){:class="img-fluid rounded pb-3"} and Jekyll converts this to HTML.

Bootstrap CSS

Because this theme uses the Bootstrap 4 CSS framework, the classes for img-fluid, rounded, and pb-3, are all documented here. It’s fairly straight forward when you get used to it. img-fluid for example, means images are responsive (they never become larger than their parent elements). rounded rounds the corners very slightly, and pb-3 adds padding to the bottom of the image with a size of 3 (pb-3).

If you are not into web development then you really don’t need to worry about Bootstrap. All it really does is ensure that the website uses a responsive design, so it will work on almost any browser (sorry Internet Explorer 9), and mobile devices too.

Existence - A Jekyll Theme for Bloggers

While there are plenty of themes for Jekyll available, not many of them were good enough to be direct replacements for a Wordpress blog. I wanted a traditional layout with a sidebar, blog content in the middle, pages, author name, tags, and even a comments section. To solve the problem I created this theme called Existence.

For a walkthrough of the theme and its features, check out this video I put together:

Static sites are going to be faster than sites being loaded from a database, regardless of caching and other page speed hacks. Furthermore, using Liquid, which is an open source template language, I have been able to do cool things like only load JavaScript that is needed for each HTML file. For example, if you enable an embedded tweet on a blog post then it needs to load Twitter’s JavaScript for embedding tweets. If you don’t specify a Tweet ID, then don’t generate the page with that JavaScript.

If you run a blog, what is your main objective for your site? Is it to share information, or use as a communication platform? If it’s the latter, then Wordpress certainly offers plugins for embedding Tweets, forms, comments, and other dynamic content, but is that really necessary? By the way, static sites don’t need to eliminate all forms of dynamic content.

Getting Started with Jekyll

I’m not going to rewrite what’s already out there, and installing Jekyll is documented here. As I show you in the video above, you need to install some prerequisites, but here are the basic steps.

  1. Install Ruby 2.4.0 or above (see these guides)
  2. Install Jekyll with gem install jekyll bundler
  3. Download my Existence theme on GitHub, CD to the directory, and run bundle exec jekyll serve
  4. Navigate to http://localhost:4000 and you’ll see the site running on your local machine

Whether you use my theme or another one doesn’t really matter. If you wanted to start from scratch, rather than downloading a theme like mine, you’d just start with an empty directory and run jekyll new <PATH>. By default, this will create a new site with the default Minima theme. It’s very basic, but good if you are interested in developing your own theme and need something to start with.

Once you have Jekyll installed, along with a theme to get started, you are probably wondering how to create blog posts, pages, and start building your own website on Jekyll. So far, all we have is Jekyll running on our local machine. If you are new to Jekyll, my recommendation is to explore how the site is structured and experiment.

Note: Gems are packages for the Ruby programming language, which Jekyll uses.

What Jekyll Does in the Background

This is just an insight into how Jekyll works and constructs the static HTML. Don’t worry if I dive too deep in this section, it’s just to give you a taste of what is going on behind the scenes. If you use my Existence theme, you’ll see some of this in action but you don’t need to be a programmer to use it!

One of the most powerful features of Jekyll is the front matter. On each blog post I create, it has the following front matter at the top of each file. This allows Jekyll to pull in a few required parameters such as the title and permalink (URL for the post), but it also allows my theme to do some other fancy stuff like generate the embedded Tweet at the bottom of the page. Here is the frontmatter used for this blog post:

title: Building a Blog with Jekyll Static HTML
description: "Ray Heffer VCDX #122. How to build a static blog using Jekyll, replacing Wordpress."
layout: post
author: Ray Heffer
categories: []
tags: [code,devops,lamp,tutorial]
excerpt_separator: <!--more-->
permalink: /building-a-jekyll-blog-website/
tweetid: 1243603604830932992

When you run bundle exec jekyll serve, Jekyll will pull this information into the statically generated site. So the title will be used for the heading at the top of the blog post, the description is pulled into the metadata description tag for search engine optimization, and so on. The Existence theme, will also use the tags to find the corresponding file in the /tags directory. In the above example, code, and tutorial, have a corresponding file in /tags, which contains the following:

layout: tag
title: "Tag: code"
tag: code
permalink: /tags/code/

This mimics the same functionality as Wordpress. If you click on a tag, it loads the file (with the above front matter) with the tag layout page (/_layouts/tag.html), which contains some Liquid code. The layout (tag.html) simply contains the themes HTML formatting to list all of the posts that match that tag. I did this using Liquid code:

{% for post in site.tags[page.tag] %}

Twitter (Keep the conversation going) Function

The way I wanted this to work, is if the blog author includes a Tweet ID in the front matter (E.g. 1243343593777123329), then it will embed the Tweet for my blog post at the bottom of the page. If no Tweet ID is added, it will say “Comments are closed for this post.”. If my post layout (/_layouts/post.html), I used an IF statement using Liquid ( {% if page.tweetid %} ):

    <!-- Open container -->
    <div class="container"> 
            <div class="postbox">
                {% if page.tweetid %}
                <p class="lead text-uppercase">Keep the conversation going on Twitter!</p>
                <div id="tweet" tweetID="{{ page.tweetid }}"></div>
                <script sync src=""></script>
                <a class="btn btn-rh" href="{{ tweeturl }}">Reply with Twitter</a>
                {% else %}
                <p class="lead text-uppercase">Comments are closed for this post.</p>
                {% endif %}
    <!-- Close container -->

The Right Tools for the Job

First, install Visual Studio Code. It really is the best code editor, and it has so many useful plugins I use it everyday for coding, markdown notes, opening large text files, and writing these blog posts. Second, install Git. If you use a Mac, then Git will be installed along with Xcode. When using my Windows PC, I use Git Bash for my command line since it allows for Linux commands.

Back to VS Code. You don’t need a whole bunch of plugins, but these are the ones I use:

  1. HTML Snippets by Mohamed Abusaid
  2. Markdown All in One by Yu Zhang
  3. Mardown PDF by yzane
  4. Prettier - Code formatter by Esben Petersen
  5. Spell Right by Bartosz Antosik

Press Ctrl+Shift+X to bring up the Extensions pane (or click on the little Extensions button on the left). Just search for these and any other plugins you’d find useful, install them and you are good to go!

Before going any further, open a command prompt and make sure you have everything installed correctly by checking versions:

git --version
ruby -v
gem -v
jekyll -v

Using Git and CI/CD to Manage Your Jekyll Blog

Using Git to make commits and deploy my site is the single most attractive reason I like to use Jekyll. That said, when you issue the bundle exec jekyll serve command, Jekyll will output the static site to the _site directory. There is nothing stopping you from uploading the contents of this directory to a web server, or even Amazon S3, since they’re all static HTML files.

However, if you take that approach, updating your website is going to be time consuming and cumbersome. This is where using Git from the command line, and a CI/CD approach (Continuous Integration / Continuous Deployment) will not only make updating your blog quick and easy, but you now have a practical reason to use Git!

Well, I’ll let you into a little secret. This isn’t really CI/CD, it’s just me sat in my home office making commits and pushing changes to GitHub. So it is the CD portion, but if I were doing Continuous Integration, then I would have automated the build and testing of my code each time I make a commit. As this is my blog, I have a single live branch.

I strongly urge everyone to learn Git, regardless of whether you want to use it to manage your Jekyll blog. Learn Git Branching ( is a really nice interactive learning platform for Git. Make some coffee, and head over there now!

As for using Git to deploy your Jekyll site, there are some basics you should know to get you up and running.


If you work in the IT industry it is hard to escape CI/CD practices. While writing this blog post I noticed that the Existence theme didn’t pull in the meta description tag from blog posts, however it’s a simple fix. I just needed to make a small change in the theme template layout file, but if I were using Agile software development sprints (10 day cycles), my fix would go through testing and QA, and be later approved for release. Instead, I implemented the change, pushed it to GitHub, and viola! In other words, I can release changes along the way rather than waiting until the end of a sprint. This is Continuous Delivery.

Much of this is way beyond the scope of this blog post, but I wanted to give you a little background to why Git is so useful.


As I mentioned earlier, you could host the static HTML files on a traditional web server but that really defeats the point of using Jekyll. Instead, I use Git to commit my changes then push the site to my repository (GitHub). Here is a typical workflow:

  1. Create a blog post by creating a Markdown (.md) file in /_posts
  2. Use git add to add the changed files to the index (tracked)
  3. Use git commit to commit the changes locally
  4. Use git push origin master to push the commit to GitHub from my master branch
  5. Sip some coffee and watch the change go live within a few seconds

GitHub Pages

GitHub has a feature called GitHub Pages, which is free, and allows you to use your repo as a static website. This works well, and you can take advantage of GitHub Pages CDN (Content Delivery Network), so no matter where you visitors are located, content is delivered from a local CDN point of presence close to them. GitHub Pages also allows you to add an SSL certificate for HTTPS, again with no cost.


Another option is to use Netlify, which is what I use and it’s still free. You still push your commits to GitHub, but instead of using GitHub Pages, you can use a private repo and use Netlify to serve the content for your website. One of the added benefits Netlify brings is the ability to use forms with their Form Handling feature. So if you have a contact form, you don’t need a backend server to run the server-side code. Netlify do this for you.

With security in mind, one of the issues I see with Netlify is they don’t offer multi-factor authentication. That said, you can authenticate with your GitHub credentials which does support 2FA. Since you have to give Netlify access to your website private repo anyway, this is not a major concern for me. However, I would love to see them add 2FA in the future.

Basic Git Commands

Here are the Git commands you’ll use most often, except for git init to initialize the repository, and git remote add which is only done once to add your remote repository. In essence, you’ll be doing a git status to see what files have changed, and what needs to be commited. You’ll use git commit to commit your changes, such as a new blog post, and git push to push them to the live site.

Git init

Initialize the local repository.

git init

Git remote

Since we are pushing our changes to a GitHub remote repository, we need to tell your Git configuration where our remote repo is (the live site). You simply do this by adding a remote origin. In my example below, I use a private repo This will prompt you to login to GitHub, and it’s a one-time action. Next time you can skip this step.

git remote add origin<username>/REPONAME

Git status

Next, run git status to check the status of your working tree. At the least, you’ll have one branch (Master), until you get used to working with multiple branches (E.g. Staging and Production).

git status

Git add

When you run git status you’ll see your modified and untracked files. Before you commit your changes, you need to specify which files you want to add. If you want to add all changed files (stages all changes), use git add -A, or you can add individual files as shown in the example below.

git add _posts/

Git commit

Once you have made all the changes locally, and everything is looking good in Jekyll, you can commit the changes. Using git commit with the -m switch allows you to add your comments without it opening vi or whatever text editor you have configured.

git commit -m "Added blog post on using Jekyll"

Git push

You can go on with this cycle, adding commits, until you are ready to push the changes to the live site. Again, I won’t go into the specifics on running multiple branches, but you can do that if you want to have a staging version of your site, if master is the live site.

git push origin master

Final Thoughts

After almost 10 years using Wordpress and hosting my site on a traditional LAMP web server, I’ve successfully been using Jekyll with Netlify for over a year now. I’ve had no issues at all, and even moved my site around from GitHub Pages, S3, and Netlify, with zero downtime. With no database, cPanel, PHPMyAdmin, FTP, SSH, or anything like that, the attack surface has been significantly reduced.

The biggest hurdle is migrating all of your existing blog posts to Markdown. However, once you have done this, whether using a plugin or doing so manually, learning Markdown will only take a few days. I write all of my notes in Markdown every day, and VS Code makes it even easier by allowing you to use keyboard shortcuts like CTRL + B for bold, and so on.

If you get stuck, or have any questions feel free to contact me on Twitter. Now hit that refresh button a few times and see how fast this page loads :)