Web Architecture, Java Ecosystem, Software Craftsmanship

Moving from Wordpress to Hugo

Posted on Jan 29, 2017

Wordpress bugs me. It slows down the authoring process and comes with security issues and maintenance efforts. That’s why I moved to Hugo. Hugo is a static website generator and allows me to write effectively and offline. In this post, I show my reasons for moving from Wordpress to Hugo and point out tips and tricks for the migration.

Moving From Wordpress To Hugo

TL;DR

  • Wordpress slows down authoring and comes with security issues and maintenance efforts.
  • Hugo is a static website generator. It allows me to write offline and to use Markdown. Hugo’s fast preview webserver enables short turn-around times during writing. Besides, the website becomes more secure and requires less maintenance.
  • During the migration from Wordpress to Hugo you have to ensure the stability of your URLs. Moreover, dynamic functionality like comments, search and related posts require some special solutions.

Why is Wordpress Annoying Me?

Authoring with Wordpress

Authoring with Wordpress

  • Authoring issues
    • You have to be online. Always. Every time.
    • It’s slow. The turnaround time (write, save, preview) is very long. This delays the whole incremental writing process.
    • You have to click a lot (for instance to upload image, update image, align an image, set image caption and width, link and format text, insert code snippets). As a developer, this bugs me a lot.
  • Security and Maintenance concerns
    • 40 % of all CMS-based website are powered by Wordpress, which makes it a popular target for hackers. Yes, Wordpress’ automatic update mechanism works great, but you still have to trigger the updates regularly.
    • You have to worry about backups. Yes, plugins like UpdraftPlus are doing a good job, but I never tried to roll back to a backup.

Hugo

Hugo is a static website generator. It takes Markdown files and generates HTML files. So I can write offline and just have to upload the HTML files to my web server when I’m done.

The Authoring Workflow with Hugo

The Authoring Workflow with Hugo

  • Due to the built-in preview webserver (running locally) I can see the results in the browser while I’m writing my Markdown files.
  • Hugo is written in Go and therefore incredibly fast. Generating my whole blog (154 pages, 110 assets and images) takes only 370 ms.
  • You can also create some nice delivery pipelines: If your Markdown files are saved in a Git repository, you can trigger the generation and upload automatically after a commit has been pushed. But for me, the above approach is fine for now.

Benefits

  • Comfortable and fast authoring
    • I don’t have to be online!
    • Being offline significantly speeds up the writing process.
    • Moreover, Hugo’s preview webserver is extremely fast and smart. After saving a file, Hugo detects these changes and generates the new page in less than 200 ms. The browser is refreshed automatically. This way, I immediately see my changes. Authoring becomes a pleasure!
    • Writing with Markdown is great fun. No clicking around in a WYSIWYG editor. Only me and my keyboard matter.
    • I can use my beloved tools (IntelliJ IDEA, Visual Studio Code, Atom) with the familiar shortcuts and self-defined snippets.
  • Almost no requirements regarding your web space. No PHP or MySQL database required. You can even host your website for free on GitHub pages.
  • Performance. Nothing is faster than the delivery of plain static files. No PHP execution and database queries are slowing down the response times.
  • Secure. Your website is made up of simple static files. There is nothing that can be hacked.
  • Low maintenance efforts. No regular updates and backups required.
  • Full and direct control over every part of your website (optimize page speed e.g. by being able to merge all assets, HTML header for SEO or pagination, RSS feed, image locations).

Drawbacks

  • You have to migrate your theme to Hugo’s template language. It takes some time to get familiar with the template syntax. Especially the error messages are rarely helpful.
  • While developers love using the command line (for build and deployment) and Markup languages like Markdown, these things may not be suitable for people without technical affinity. Hence, Hugo won’t work for an ordinary blogger.

Use Cases

Apart from blogs, Hugo can easily replace CMS systems, where simple content is written and displayed. For example, Hugo can be used for knowledge sharing, documentation or news within a company. We at Spreadshirt use it for our internal tech blog, where we share knowledge and best practices.

Tips

Migrating your Wordpress Posts to Markdown

I already used Markdown for my Wordpress posts (yes, that’s possible), so there was no conversion required and I wanted to set up my Hugo project manually anyway.

But you should definitely check out the Wordpress plugin wordpress-to-hugo-exporter. It converts all your posts to Markdown (including the post configuration) and creates a typical Hugo project structure out of your Wordpress installation. The result is a very good starting point for your migration.

Folder Structure

When I started with Hugo it took me some time to figure out an appropriate folder structure. I ended up with the following:

├── content
│   ├── blog
│   │   ├── 2015
│   │   ├── 2016
│   │   └── 2017
│   │       ├── 0124-microservice-guest-lecture-tu-chemnitz
│   │       └── 0129-moving-wordpress-hugo
│   │           ├── Authoring-Workflow-Hugo.svg
│   │           └── moving-wordpress-hugo.md
│   └── page
│       └── legal.md
├── public
├── static
│   ├── img
│   │   └── author-pic.jpg
│   └── .htaccess
├── themes
│   └── edinburgh-hugo-theme
├── config.yaml
└── build-and-deploy.sh

Example Theme

You can find my theme on GitHub under edinburgh-hugo-theme. Use it as an inspiration, but don’t expect that it will work out of the box for you. ;-)

Gulp Integration

Hugo has no built-in integration for assert preprocessing (e.g. SASS compilation, minification or concatenation). But that’s no problem at all. Just call Gulp up front. In my case, it creates the static folder with all assets (css, fonts, img, js). Then I call Hugo.

During development, I start Hugo’s preview server using hugo server in one shell. In another shell, I start Gulp and let it watch for changes in my assets (like the sass files). When I change a sass file, gulp will recreate the CSS file in the static folder. This in turn, will trigger Hugo to create the site and to refresh the browser. This workflow works pretty well.

Again, take a look at my edinburgh-hugo-theme for further details.

Preserve or Redirect URLs

It’s very important to preserve your post’s URLs when migrating from Wordpress to Hugo. Otherwise you will lose all your search engine page rank and all backlinks will break. In Wordpress, I used the following URL schema:

<domain>/my-post-name

As you see, I have no category or date in my URLs. This schema can be easily configured in the site configuration (config.yaml):

permalinks:
    blog: /:slug/

Next, every post has to specify a slug in its configuration:

slug: moving-wordpress-hugo

Another point are the paths for the RSS feed and the taxonomies (categories and tags). Their default paths are different in Wordpress and Hugo:

# Wordpress:
<domain>/feed/
<domain>/category/<name>
<domain>/tag/<name>

# Hugo:
<domain>/index.xml
<domain>/categories/<name>
<domain>/tags/<name>

Since these URLs are not so important for my search engine traffic, I decided to break them and use Hugo’s default URLs for the RSS feed and the taxonomies. This keeps my configuration simple. But I configured redirects in my .htaccess for them:

RewriteRule ^feed$ index.xml [R=301,L]
RewriteRule ^feed/$ index.xml [R=301,L]
RewriteRule ^tag/(.*)/$ tags/$1/ [R=301,L]
RewriteRule ^tag/(.*)$ tags/$1/ [R=301,L]
RewriteRule ^category/(.*)/$ categories/$1/ [R=301,L]
RewriteRule ^category/(.*)$ categories/$1/ [R=301,L]

Finally I recommend the Screaming Frog SEO Spider. With this tool you can create a list of all URLs of your Wordpress site and check if these URLs still exist or get redirect properly on your Hugo site.

Comments

I’m using Disqus for my comments. It’s the easiest choice since Hugo provides nice built-in support for it.

Moreover, you can import your existing Wordpress comments into Disqus. Just install the Disqus Plugin disqus-comment-system in Wordpress and use the export function to get all comments into your Disqus account. It works quite well. The only drawback is that you’ll lose the Gravatars of your comments.

Many Hugo users employ Google Custom Search. But it contains ads (in the free edition). That’s why I’m using a simple HTML form and redirect to Google Search but restrict the search to my site.

<form role="search" method="get" action="https://www.google.com/search">
  <input type="search" placeholder="Search..." value="" name="q" title="Search for:">
  <input type="hidden" name="sitesearch" value="blog.philipphauer.de">
  <button type="submit" value="Search"/>
</form>

You can calculate the related posts by searching for other posts having the same tags. I adopted this approach:

<ul>
{{ $page_link := .Permalink -}}
{{ $tags := .Params.tags -}}
{{ range $page := .Site.Pages -}}
    {{ $has_common_tags := intersect $tags .Params.tags | len | lt 0 -}}
    {{ if and $has_common_tags (ne $page_link $page.Permalink) (lt ($.Scratch.Get "$c") 4) -}}
        {{ $.Scratch.Add "$c" 1 -}}
        <li>
            <a href="{{ $page.Permalink }}">
                <img src="{{ .Params.featuredImage }}" alt="{{ .Title }}"/>
                <p>{{ .Title }}</p>
            </a>
        </li>
    {{- end }}
{{- end }}
</ul>

Image Resizing and Compression

I’m using ImageMagick to resize and compress my images.

convert "image.png" \
    -resize 200 \
    "image-thumbnail.png"

Deployment/Upload

The upload of your static files if very easy if you have ssh access. In this case, rsync is the best choice. It’s extremely fast and easy to use.

Local Apache for Testing the .htaccess

Unfortunately, you can’t test all aspects of your website with Hugo’s preview webserver. This includes SSL/HTTPS, the 404 site, redirects and HTTP headers (like the Expires header). For this, I start a Docker container with an Apache Webserver. My docker-compose.yml looks like this:

version: '2'
services:
  apache:
    image: httpd:2.2.32-alpine
    volumes:
      - ./public:/usr/local/apache2/htdocs
      - ./apache-config/httpd.conf:/usr/local/apache2/conf/httpd.conf
      - ./apache-config/ssl:/usr/local/apache2/ssl
    ports:
      - "80:80"
      - "443:443"

Please note, that you have to change the following in the default httpd.conf:

  • Activate .htaccess files by setting AllowOverride to “All” (under <Directory "/usr/local/apache2/htdocs">).
  • Activate SSL by appending the following at the end:
<IfModule mod_ssl.c>
    Listen 443
</IfModule>
<VirtualHost *:443>
    SSLEngine on
    SSLCertificateFile /usr/local/apache2/ssl/apache.crt
    SSLCertificateKeyFile /usr/local/apache2/ssl/apache.key
    ServerName localhost
    DocumentRoot /usr/local/apache2/htdocs
</VirtualHost>

Now just call:

hugo --baseURL https://localhost
docker-compose up
curl https://localhost --insecure

Authoring with Visual Studio Code

I ended up with Visual Studio Code for authoring my blog posts. Check it out, it’s really nice and is also available for Linux. Besides, you can even tune your Visual Studio Code to make Markdown authoring a real pleasure!

The extension markdown-shortcuts is a must-have! It allows to breeze through formatting Markdown.

I’m using the figure shortcode to insert images with a caption or a certain css class. But the syntax is cumbersome and hard to remember. That’s why I created a snippet for this. I assign the abbreviation fig to it. Now I only have to type fig<Tab> and Visual Studio Code expands the snippet for me. I uploaded my snippets to GitHub Gist. Feel free to copy it!

If you want to copy my whole settings, you can use the extension settings-sync and use my GIST ID bff96da5eb3f5318fc1b64eaf5e4da78.

Tip: Ctrl+Shift+O displays the table of contents of the current markdown file. Moreover, you can jump to a certain section by typing the heading name.

Contribution

I really like to thank my colleague Andreas Linz for pointing me to Hugo!