Blog-Relaunch mit Hugo

Fast sieben Jahre lang hat jetzt mein Lieblings-CMS WordPress die Seiten dieses kleinen Blogs bereitgestellt. Vorher hatte ich von 2003 bis 2006 ebenfalls schon ein WordPress-Blog, allerdings unter einer anderen Domain.

Da man ja hin und wieder über den Tellerrand schauen soll und statische Site-Generatoren spätestens seit Jekyll auch niemandem mehr unbekannt sein dürfte, habe ich mich einfach mal nach einem modernen Site-Generator zum Bloggen umgeschaut.

Hugo erfreut sich größter Beliebtheit und kann vom einfachen Blog bis hin zu komplex aufgebauten Sites so ziemlich alles erzeugen, was sich irgendwie in statischer Form ablegen lässt.

Beim Herumspielen mit Hugo kam mir dann die Idee einfach testweise mein WordPress-Blog zu migrieren. Das funktionierte recht einfach, es kam eins zum anderen und nun ist hier alles neu.

Worauf ich alles achten musste, habe ich in diesem Artikel zusammengefasst.

Posts von WordPress übernehmen

Mein Blog hat ein sehr einfache URL-Struktur für Beiträge: www.marcusjaschen.de/blog/<JAHR>/<POST-NAME>.

Diese Struktur lässt sich sehr einfach in Hugo abbilden. Normalerweise bildet Hugo die vorgefundene Dateisystemstruktur direkt auf URLs ab, ich konnte also mit folgendem Aufbau alls bisherigen Links beibehalten:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
/content
  /blog
    /2014
      devicons.md
      findguidelines.md
      ...
    /2015
    ...
    /2021
      staemme-bohlen-saegen-kettensaege-saegewerk.md

Ich habe den bisherigen Permalink-Teil der Blogposts einfach als Dateinamen für die Markdown-Dateien übernommen, eine explizite Definition der URL für jeden einzelnen Post im Front Matter entfällt so.

Das Markup der Posts habe ich in HTML gelassen, als Dateiendung aber .md gewählt. HTML ist auch gültiges Markdown, sodass hier keine Probleme auftreten.

Lediglich der [caption] Shortcode aus WordPress funktioniert nicht mit Hugo, ich habe diesen durch ein <figure>-Element mit <figcaption> für die Bildunterschrift ersetzt:

1
2
3
4
5
preg_replace(
    '/\[caption.*](<a.*<\/a>)(.*)\[\/caption]/iuU',
    '<figure>$1<figcaption>$2</figcaption></figure>',
    $content
);

Die eigentliche Konvertierung habe ich mit einem schnell gehackten PHP-Skript erledigt:

  • Alle veröffentlichten Posts aus WordPress laden
  • [caption]-Shortcodes im Inhalt ersetzen
  • für jeden Post eine Markdown-Datei im passenden Verzeichnis (content/blog/<JAHR>/<POST-NAME>.md) erstellen, dabei ist <POST-NAME> der bisherige Permalink-Teil, <JAHR> leitet sich aus dem Veröffentlichungsdatum ab
  • der Beitragstitel ist im Front Matter-Bereich der Datei als title abgelegt
  • das Veröffentlichungsdatum kommt ebenfalls in das Front Matter
  • Kategorien und Tags werden im Front Matter-Bereich als Listen abgelegt

Beispiel für einen Post:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
---
title: "Das eigene Sägewerk"
date: "2021-01-16T13:13:38+01:00"
categories:
  - "Sonstiges"
---

Wer gerne Dinge aus Holz baut, benötigt neben einer Menge Werkzeuge natürlich auch nicht unerhebliche Mengen an – genau – Holz. Dieses lässt sich ganz normal im Handel kaufen oder deutlich günstiger aus privaten Quellen beziehen.

[...]

Es ist möglich, die URL eines Posts explizit im Front Matter festzulegen, das war aber für meine Blogposts nicht notwendig.

Kategorien/Tags

Wie im vorherigen Abschnitt beschrieben, reicht es die Kategorie- und Tag-Namen im Front Matter-Bereich des jeweiligen Posts aufzuzählen.

Da die Kategorie- und Tag-Namen einfach Worte sind, sind die von Hugo erzeugten URL-Slugs identisch zu denen von WordPress, hier ist keinerlei weitere Anpassung notwendig gewesen: alle alten URLs funktionieren weiterhin.

Beispiel: Der Kategoriename „Sonstiges“ wird in URLs als sonstiges dargestellt. (Link zur Kategorie)

Definition der Kategorien/Tags in einem Post:

1
2
3
4
5
6
7
8
9
---
title: "Testpost"
categories:
    - Kategorie
tags:
    - Tag1
    - Tag2
    - Tag3
---

Bilder/Medien

Neben den oben beschriebenen Anpassungen für den [caption]-Shortcode habe ich an den Bildern nichts weiter geändert – auch die URLs bleiben identisch. Dazu behalte ich das wp-content/uploads-Directory einfach bei.

Neu hinzukommende Bilder lege ich allerdings passend zur Hugo-Struktur an, sie kommen zusammen mit der Markdown-Datei für den Blogpost in ein Unterverzeichnis. Beispiel:

1
2
3
4
5
6
7
8
9
/content
  /blog
    /2021
      /staemme-bohlen-saegen-kettensaege-saegewerk.md
      /blog-relaunch-hugo-2021
        /index.md
        /assets
          /server-setup-hugo.svg
          /server-setup-wordpress.svg

Ich hatte bei 15 Beiträgen den [gallery]-Shortcode verwendet, diese Beiträge habe ich vorher im WordPress manuell so geändert, dass die Bilder direkt über das <img>-Element eingebunden werden.

RSS- und Atom-Feeds

Für die Feeds habe ich ein einfache Weiterleitung im Webserver konfiguriert, sodass die alten WordPress-Feeds (z. B. /feed/) auf die neuen URLs (z. B. rss.xml) umgeleitet werden.

Redirect im Webserver (nginx):

1
rewrite /feed/ /rss.xml permanent;

Statische Seiten

Neben chronologisch geordneten Blogposts gibt es noch noch eine Hand voll Seiten im Blog, z. B. das Impressum, die Kontaktseite usw.

Diese habe ich passend zu ihren bisherigen Links in die Dateisystemstrukur einsortiert:

/content
  /impressum
    /index.md
  /kontakt
    /index.md

Kommentare

Dieses Blog hatte bisher keine Kommentarfunktion, daher war hier auch keine Datenübernahme notwendig.

Sonstiges

Emojis in URLs

Einige URLs enthielten Emojis. Diese werden URL-encoded dargestellt und führen u. U. zu Problemen. Ich habe den Relaunch zum Anlass genommen, die betreffenden URLs anzupassen.

Die Emojis wurden aus den URLs entfernt und im Webserver entsprechende Weiterleitungen angelegt, sodass die alten URLs trotzdem noch funktionieren.

Beispiel:

  • alte URL: /blog/2020/die-sonne-geht-auf-🌅/ (Link)
  • neue URL: /blog/2020/die-sonne-geht-auf (Link)
  • Redirect im Webserver (nginx):
1
rewrite /blog/2020/die-sonne-geht-auf-🌅 /blog/2020/die-sonne-geht-auf permanent;

Caching mit Varnish

Bisher hat ein Varnish die erzeugten Seiten des Blogs zwischengespeichert um eine gute Performance beim Laden zu gewährleisten.

Da Varnish kein HTTPS unterstützt, kam ein nginx als TLS-Terminator vor Varnish zum Einsatz. Varnish wiederum holte sich die Inhalte vom eigentlichen Webserver (nginx), welcher die Requests über FastCGI an PHP-FPM weitergegeben hat:

Server-Setup WordPress

Mit Hugo liegt das komplette Blog jederzeit komplett fertig gerendert vor. Der Webserver muss nur noch HTML-Dateien und statische Assets wie CSS-Dateien und Bilder ausliefern. Obiges Diagramm vereinfacht sich damit zu:

Server-Setup Hugo