What does it take to move a blog from Wordpress to a static site? I started asking myself this question last year. When I originally chose Wordpress back in 2011, it was due to the ease of getting started, low cost, and open community. All of these qualities are still true of Wordpress but I grew increasingly disappointed with slow page-load times and wanted a lighter-weight, more hands-on blogging framework.

I decided to move my blog to Jekyll, a static site generator written in Ruby that can convert Markdown to HTML (one of my main requirements). Other popular static site generation frameworks include Ghost, Middleman, and Pelican. For hosting I considered AWS S3 for the files and Route 53 for DNS, but ultimately went with Github.io for hosting and Hover for DNS (more on this below).

To summarize, here were my requirements:

  • keep all the post content from my Wordpress blog
  • permalinks to existing posts to remain the same (mattdickenson.com/year/month/day/title)
  • allow for extra pages (such as /about) outside of the main blog
  • decent monospace font + syntax highlighting for code

Moving content

Moving your data from Wordpress to Jekyll is very easy. First, choose “Download export file” from the “Tools” section of your Wordpress dashboard. Then, install Jeykyll import tools:

$ gem install jekyll-import
$ gem install hpricot
$ gem install open_uri_redirections

Instructions for importing the file you downloaded from Wordpress are here. If you chose to fetch images (the default), this can take a while. To avoid this, use the "no_fetch_images" => true option if you don’t care about keeping images from your old posts.

Next, set up a Github repo for your site. If you plan to use Github.io for hosting then you should call the repo “your_username.github.io”. Clone the repo locally, run $ jekyll new . inside it to initialize your Jekyll site, and copy in the imported data from the previous step. You may wish to choose a theme for your site at this point as well, but you can also do it later. If you want to use the same permalink settings as Wordpress, open up the config.yml file and edit the following line:

permalink:    /:year/:month/:day/:title/

If you were using a custom domain on Wordpress that you want to keep, you will want to transfer it. I chose Hover because that is where I have some other sites already registered (and they sponsor many of my favorite podcasts) but any popular domain registrar should be able to do that for you.

To transfer, start on your registrar of choice to verify that they can do the transfer. Then you will have to unlock the domain on Wordpress (in the admin console) and request an authorization code). With that code you can purchase the domain on Hover. Next you will have to accept the transfer on Wordpress when they send you an email or wait five days for it to happen automatically. If you had whois privacy enabled before you will probably want to add that back; with Hover this will be done for you automatically. Then, add a file called “CNAME” to the root of your git repo with only the domain name you want to use. This will tell Github.io which domain name to use. This step is one of the more time consuming ones, but it’s relatively painless if you choose a good registrar.


The four main “gotchas” with my move from Wordpress to Jekyll were embedded Youtube videos, image filenames, LaTeX math syntax, and post tags. If none of these are an issue for you feel free to skip this section.

Embedded Youtube Videos

Wordpress makes Youtube embeds very easy, but once you move your site you will want another solution. At first I thought that this Jekyll plugin would work–and it would if you are self-hosting your site–but it is not supported by Github.io. I ended up going with this CoffeeScript solution. To use CoffeeScript on your blog, install jekyll-coffeescript locally and include a gems list in your config file like this:

 - jekyll-coffeescript

At this point it became a find-and-replace job to convert the Wordpress embed syntax over to the one expected by this CoffeeScript Solution. Using regular expression searching in Sublime Text, here were the searches I used (the overall number could probably be reduced by improving the regular expressions themselves, but at the time I was doing this I was in a hurry):

\[youtube https*:\/\/www.youtube.com\/watch\?.*v=([\w-]*).*\]

In every case the replacement you want to use is {% youtube $1 %}.

Image filenames

Your mileage may vary, but when I exported my images from Wordpress there were query params appended to the filenames (e.g. image.png?w=300). This caused Jekyll to not be able to find the files in the assets folder, so I ran the following shell script which iterates over the files and removed the ? and everything after it from the filenames:

for i in *
    mv "$i" "`echo $i | sed 's/\?.*//'`"

LaTeX Math Syntax

A lot of posts from my time in grad school included equations written in LaTeX which Wordpress allows you to embed with $latex ... $. This has a different semantic meaning in Markdown, so I switched to using Mathjax (more info here). My particular errors were due to liberal use of double-curly brackets for nesting in LaTeX which conflicts with Liquid syntax.

For inline math, you’ll want to find \$\$(.)\$\$ or \$\$([^$]*)\$\$ and replace with \\($1\\). For non-inline math, find \$latex ([\w\s]*)\$ or \$latex ([^$]*)\$ and replace with \$\$$1\$\$.

Post Tags

Another nice feature of Wordpress that I wanted to preserve was the ability to find all posts with a given tag. Wordpress uses a MySQL database to facilitate this but in Jekyll we have to take a different tact.

I generated a /tags page with anchor links for the tag name that lists each post with that tag. In Liquid syntax:

<ul class="post-list">
  {% for tag in site.tags %}
    {% assign t = tag | first %}
    {% assign posts = tag | last %}
    {% assign name = t | downcase | remove: ' ' %}
    <a href="#{{name}}" name="{{name}}">{{ t }}</a>
      {% for post in posts %}
        {% if post.tags contains t %}
          <a href="{{ post.url }}">{{ post.title }}</a>
        {% endif %}
      {% endfor %}
  {% endfor %}

Once you have this page set up, you need to replace all of your Wordpress links with these anchors (find yoursite.com\/tag\/(\w+)\/ and replace with /tags/#$1).

Post Comments

One thing I did not cover here was transferring comments over. I had 405 posts at the time of transfer, and only 70 comments–30 of which were me. To me it was not worth moving them over, but if you want to keep your blog’s comments you can use Disqus.

The switch

Two other Wordpress features you may want to keep are analytics and an RSS feed. In Jekyll 3.0 RSS now works out of the box so there is no extra work. For analytics I chose to use Google analytics, which requires setting up your site with them and adding a script tag to your page template.

The final step when you are ready to switch to your static site is to switch the DNS record. You will need to add the CNAME file now if you did not do that above. Then, add an A record as described here. I also added A records to the two Github IPs through Wordpress. Once you have done all this you can check whois to see if the nameservers have switched over or dig yoursite.com +nostats +nocomments +nocmd to see whether it is still being served by Wordpress. If you run into trouble you may find this post helpful.


As you can see, there are some semi-technical steps involved in converting an existing blog over to Jekyll. Overall none of them are too tedious or difficult, though. Personally I have found that having quicker pageloads and a lighter-weight framework that allows me to write posts in Markdown and play around with new pages locally before deploying them to be worth the trade-off. The only monetary cost involved was registration at Hover, which is comparable to Wordpress registration.

I hope that you found this post helpful. If you are interested in converting your site, here are some other pages that helped me along the way:

Now if I want to hack on my site I can do so in Ruby, JS/CoffeeScript, or Sass. I’m finally blogging like a hacker.