Philipp Hauer's Blog

Engineering Management, Java Ecosystem, Kotlin, Sociology of Software Development

Moving from Wordpress to Hugo

Posted on Jan 29, 2017. Updated on Apr 11, 2019

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


  • 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 and search 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 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.


  • 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).


  • 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.


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
│   │       ├── 01-microservice-guest-lecture-tu-chemnitz
│   │       └── 02-moving-wordpress-hugo
│   │           ├── Authoring-Workflow-Hugo.svg
│   │           └──
│   └── page
│       └──
├── public
├── static
│   ├── img
│   │   └── author-pic.jpg
│   └── .htaccess
├── themes
│   └── edinburgh-hugo-theme
├── config.yaml

Update: Hugo can now derive the date and the slug from the file name with a certain font matter configuration. No need to add them in the beginning of each post.

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 Asset Management

Update: Since version 0.43, Hugo supports Pipelines which can be used to compile SASS, minify CSS or concat CSS and JavaScript. This way, we don’t need other build tools like Gulp. This is a great feature and works like a charm.

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:


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

    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:

# Hugo:

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.


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="">
  <input type="search" placeholder="Search..." value="" name="q" title="Search for:">
  <input type="hidden" name="sitesearch" value="">
  <button type="submit" value="Search"/>

Update: Since version 0.27, Hugo provides a powerful solution for related content out of the box.

<div class="related-posts">
  <p class="h3-like">Related Posts</p>
  {{ $related := .Site.RegularPages.Related . | first 4 }}
  {{ with $related }}
    {{ range . }}
      <a href="{{ .Permalink }}">
        <img src="{{ .Params.featuredImage }}" alt="{{ .Title }}"/>
        <p>{{ .Title }}</p>
    {{ end }}
  {{ end }}


  threshold: 80
  includeNewer: true
  toLower: false
  - name : "tags"
    weight: 150
  - name: "categories"
    weight: 80
  - name : "date"
    weight: 10
    pattern: "2006"

Image Resizing and Compression

I’m using ImageMagick’s convert to resize and compress my images and OptiPNG to optimize the image size of PNGs.

convert "image.png" -resize 300 "image-thumbnail.png"
optipng "image-thumbnail.png" -quiet

For converting SVG to PNG I achieved the best results with the command line of inkscape.

inkscape --without-gui --export-png "image-small.png" --export-background "#ffffff" --export-width "200" "image.svg" > /dev/null


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'
    image: httpd:2.2.32-alpine
      - ./public:/usr/local/apache2/htdocs
      - ./apache-config/httpd.conf:/usr/local/apache2/conf/httpd.conf
      - ./apache-config/ssl:/usr/local/apache2/ssl
      - "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
<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

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.


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