A couple of years ago I switched this blog from a WordPress site hosted on GoDaddy to a statically generated site. The new setup is faster, more secure, and cheaper to operate. And it was easy to do!

Static vs Dynamic websites

WordPress is a very popular blogging platform. You can use it hosted on WordPress.com, or any number of other web providers that offer it. Because it’s open source, you can also host it yourself easily. In my case, I had a virtual server running in GoDaddy where I maintained the installation myself.

Hexo is a static website generator. When you want to publish changes, you run a command that causes it to spit out a bunch of static html, css, and so on that you can then host anywhere you like. These tools can store the raw content any way they like, but Hexo and all the other generators I’ve played with use a directory structure with simple markdown files.

Dynamic websites are generated by software running on the web server. Ignoring any caches or other optimizations, every web request triggers a call to the running application, which in turn makes database requests and invokes any loaded plugins before a page gets sent back to the browser. For medium-sized websites, this isn’t noticeable because the server will be constantly warmed up and ready to respond. For very small websites like mine that go idle, a fresh web request can take more than 30 seconds to respond while everything is initialized. This is not ideal.

Static websites have fantastic performance. The server only has to return the pre-computed content, and the providers that do this at scale do it very well. Even if I only get one hit a month, that page will still load very quickly, likely even faster than a warmed-up WordPress instance. It depends on the hosting platform, but static websites can typically also handle massive traffic spikes without any extra effort, and at a much lower cost. Your small self-hosted WordPress site, by comparison, could fall over when one of your posts goes viral.

Another benefit to static websites is their security. Because it’s just plain files, there is much less attack surface. As long as your account is secure, it should be impossible for an attacker to modify the content. Even if someone did, the content and site generator can be kept separate, so the site can easily be replaced with fresh, clean content. With a WordPress site, you have the running server, the WordPress software, and the database all running in the cloud. They are prone to bugs and attacks like any other cloud infrastructure, and they need to be updated regularly to keep them secure.

Comments and contact pages on a static website

The biggest drawback to static websites is that they are, well, static. There are a few tricks you can use, but some features are much more difficult to implement. For a simple blog like mine, this wasn’t a problem.

Comments are a common blog feature that is trivial for a dynamic site to implement. For this blog, I recently set up utterances. It uses GitHub Issues (in a free public repository) like a database to store comments, and retrieves / renders them on each page with a bit of client-side javascript. This way I don’t need to run hexo generate every time someone posts a comment.

A reader does need a GitHub account to add a comment with this system, but this seems like a reasonable limitation given my audience. There are other static-capable comment systems available, each with different styles and storage mechanisms. There are commercial offerings too, which may or may not be more expensive than using a hosted WordPress site for your blog to begin with.

I don’t have a contact form on this site, but I easily set one up on another static website using getform. For the frequency of messages I expect there, it should also be free.

It gets unfortunately difficult to push much further than this on a static website. Some things can be done with client-side javascript or advanced application firewalls, but these can get complicated and/or expensive pretty quickly. It may be worth it if you want the scalability advantages of a static site, but if you just want a cheap website with a couple of dynamic features, a hosted WordPress site may be your best bet.

HTTPS and custom domains

The main reason I left WordPress on GoDaddy was the desire to move my blog to HTTPS. Nowadays it’s not that hard to get a free certificate from Let’s Encrypt, but the certificates don’t last very long, so it’s best to automate the process. At the time I changed, automating this wasn’t possible with the package I had from GoDaddy. I could have changed to a more expensive virtual machine option or bought a proper certificate. I went with option 3 instead.

HTTPS requires a certificate, and the certificate has to contain the domain name of the website you’re using it for. That means that you need a custom certificate to go with your custom domain name. The good news is that many hosting services can procure certificates on your behalf, and some will do it for free.

GitHub Pages is one of these services, and it was easy to set up. There were a couple of steps necessary so they could validate that I had control of the domain, but now they just take care of it for me. They can also provide redirects from HTTP to HTTPS, and from www to the apex website. GitHub Pages has good documentation if you want to set this up for yourself.

Price of static websites

Because GitHub Pages is free (for public repositories, at the time of this writing), the only thing I’m paying for is the domain name and the DNS service, both of which I get from AWS. The .ca domain costs $13 USD per year, and the Route53 hosted zone costs me $0.90 USD per month (the minimum possible since I don’t exceed the initial usage limits). This is much less than I was paying GoDaddy for a virtual server.

There are a few equivalents to GitHub Pages that are also free. There are also cheaper options than AWS for domain registration and DNS.

If you don’t want the hassle of setting this all up, there are lots of hosted WordPress solutions around, and other blogging/website platforms too. Some of these are even free, but the free ones each have their own limitations. I haven’t searched exhaustively, but I don’t know of any free options that support custom domains and control over advertising.

Creating, editing, and publishing posts

It can take a bit of effort to get a static site generator set up and configured with a nice theme, but things get easier once you have it all working. You still need to run a few commands in a terminal, which will be too much for some people. Even the mildly technically challenged should be able to keep themselves going with a few basic commands written down. This is what I use:

When I want to start a new post, I use the hexo new command:

1
hexo new draft 'This Blog: Hexo-generated static site hosted on GitHub Pages'

This command generates a markdown file in the draft folder with some initial “front-matter”. I can then edit the content in any text editor I wish. These days I use VS Code. Markdown is a very simple mostly-text format for making pages that is very easy to learn. Front-matter is a bit of data about the page that go at the top of the document such as the title, publish date, tags, categories, and so on. You can see an example of what this looks like in the source for the Hexo documentation.

If I want to see how a page will look (for example: if I’m using some tricky markdown syntax), I use hexo server --drafts. This hosts a website on my computer that looks exactly like the blog will when I publish it. I also sometimes use commands like hexo list tag --draft to make sure I’m using the same tags across multiple posts.

Once I’m happy with a post, I run the hexo publish command:

1
hexo publish 'This Blog: Hexo-generated static site hosted on GitHub Pages'

The main thing this does is move the file and its attachments from the drafts folder into the posts folder. It also renames the file and updates the front-matter with the published date and time. You could do this manually, but the command is quick and reliable. Then I need to re-generate the static content for publishing:

1
hexo generate

This goes through all the content files and configuration and generates everything needed for the website. The new post gets its own folder and html file, but it will also cause changes to the front page and a number of peripheral pages. The theme I’m using supports paging, so it also pushes the post at the bottom of the front page back to page 2, the bottom post from page 2 to page 3, and so on. There are also the tag, category, and archive pages that get updated similarly. It might seem like a lot, but I assure you, the software doesn’t mind.

After this, it’s a simple matter of uploading the content to my hosting provider. In the case of GitHub Pages, this means copying it to the website repository, commiting, and then pushing the branch. Nowadays I use a PowerShell script to generate, copy, commit, and push in a single step.

I use Git and GitHub to manage my hexo folders because I am very comfortable with these tools. This isn’t necessary though. You could just as easily store your hexo files on a cloud drive. You could store it on your local hard drive too, but I highly recommend using somewhere that has automatic backups and some form of change history.

Because I’m using drafts for incomplete posts, I commit and push any changes I’ve made regularly. The drafts aren’t included in the generated site, so I can keep a few sitting around while publishing other posts or changes. I originally went a little bonkers with a branch-per-post but found it made my process unnecessarily complicated.

You can simplify publishing your site further. Some static page providers can automatically run the generate step for you. I chose not to do this for my blog because I didn’t want my drafts and their edit history to be visible in the public repository (as a public repository is required to use Pages for free). There are other options that don’t have this limitation.

Converting from WordPress to Hexo

Most of the work of migrating was achieved using a WordPress export file and the Hexo migration tool:

1
hexo migrate wordpress wordpress-export.xml --paragraph-fix --import-image original

It took a bit of fiddling after this to get everything working perfectly. I had some issues with my code snippets (I was using a gist plugin in WordPress), and some of the attachment references didn’t work with my theme. The nice thing about working with markdown files is that it was really easy to search for patterns once I found them. With Git and branches, I could also quickly test fixes and revert them if they didn’t work.

I didn’t have a lot of content when I made the switch, but it only took me a few hours.

Summary

Static websites can be cheaper, perform better, and require less maintenance than self-hosted options. They have their limitations, but for a simple blog like mine, it was an easy choice that I’m still happy with.