Von Wordpress zu Hugo über ein paar Umwege – eine Migrationsgeschichte

Beim Versuch, von Wordpress loszukommen, habe ich einiges mit Pico, Grav und Hugo erlebt. Fast wäre ich wieder bei Wordpress gelandet.

Lesezeit: 14 Min., von Titus Gast gepostet am Fri, 31.3.2017
Tags: hugo, wordpress, html, cms, grav, pico, blog

Ich hatte ein Problem: Eine Menge Webseiten, die viel zu selten bearbeitet werden. Ein viel zu selten gefüttertes Blog. Mein Ziel wäre eigentlich, mindestens einmal wöchentlich was zu schreiben. Schon dafür ist es eigentlich mit Kanonen auf Spatzen geschossen, wenn dauernd ein ausgewachsenes dynamisches CMS wie Wordpress läuft, das bei jedem Aufruf erst mal eine Datenbank abfragen muss, bevor es Content ausspuckt; Content wohlgemerkt, an dem sich manchmal wochenlang nichts ändert. Das muss nicht sein.

Am liebsten HTML

Schon vor einiger Zeit hatte ich meine Wordpress-Seiten mit einem Caching-Plugin beschleunigt. Mein subjektiver Eindruck war allerdings immer, dass das unter dem Strich wenig bringt, weil der Cache ja hin und wieder neu invalidiert und dann gefüllt werden muss. Außerdem läuft im Hintergrund ja die Datenbank mit – also keine echte Vereinfachung. PHP und MySQL als Sicherheitsrisiken bleiben präsent.

Elektrisiert hat mich dann ein Text über alternative CMS, die auf der Basis einfacher Markdown- oder Textdateien arbeiten: Keine Datenbank, kein Bullshit, kein Overhead. Das wollte ich.

Schnell war mir klar: Eigentlich hätte ich gerne einfache HTML-Dateien, die ich auf einen Server lege, wo sie kein Unheil anrichten können. Keine täglichen Updates für Plugins, Themes, etc. – und wenn ich wieder mal den Server wechsle, einfach runter- und wieder hochladen: Das wäre alles an Migration.

Das Produktionssystem könnte weiterhin Wordpress bleiben – nur z.B. ausschließlich in meinem lokalen Heimnetz und von außen allenfalls via VPN zugänglich. Damit hätte ich immer eine Sicherheitskopie im Haus und wäre abgesichert gegen Datenverlust und Hackerangriffe.

HTML aus Wordpress

In der Tat kann Wordpress auch statische Seiten generieren. Mit „Simply Static“ und „WP Static HTML Output“ gibt’s zwei gute Plugins, die diesen Job übernehmen.

Das ist super, wenn man keine Kommentare im Blog hat und nicht viel Inhalt. Allerdings dauerte bei mir die Generierung der HTML-Dateien mindestens 15-20 Minuten (beim zweiten Mal geht’s dann schneller). Noch ärgerlicher finde ich aber, dass man das nach jeder Publikation erst mal neu anstoßen muss und das nicht automatisch im Hintergrund passiert, wenn man neue Beiträge abspeichert. Komfortabel ist anders – wenngleich ich für Blogs, bei denen ich aus verschiedenen Gründen nicht auf Wordpress verzichten möchte, immer noch verschärft über diesen Workflow nachdenke.

Bei der angestrebten Migration einer dieser Websites musste ich dann noch die Erfahrung machen, dass das Ganze in einer Wordpresse-Multisite-Umgebung nicht ganz fehlerfrei funktioniert: Auch die generierten statischen HTML-Dateien wollten immer wieder Kontakt zur Mutterseite aufnehmen. Seltsames Phänomen – das führt letztlich den Zweck des Plugins völlig ad absurdum.

Markdown – der bessere Texteditor

Bei der Beschäftigung mit den diversen Flat-File-CMS im oben schon genannten t3n-Artikel stieß ich auf die Tatsache, dass viele der kleinen, feinen CMS mit Markdown-Dateien arbeiteten. Markdown ist eine einfache Auszeichnungssprache, mit der man Texte strukturieren und einfache Textmarkierungen als kursiv, fett, durchgestrichen, etc. vornehmen kann. Markdown ist sehr mächtig; wer gerne Texte strukturiert – so wie ich – kann damit viel Spaß haben beim Schreiben. Man kann sich auf den Text konzentrieren und muss nicht immer nebenbei im Text-Modus HTML ausbessern, weil der WYSIWYG-Editor wieder zerschreddertes HTML geliefert hat – ganz zu schweigen davon, dass der Standard-Editor in Wordpress keine Zwischenüberschriften kennt (Korrektur: Wordpress kennt die zumindest inzwischen).

Pico und Grav – oder: meine Datenbank heißt Markdown

Pico

Bei meiner Suche nach einem geeignete CMS versuchte ich es zuerst mit Pico. Klein, minimalistisch, schnell, Open Source – und als Basis nimmt man einfach Markdown-Dateien, aus denen das CMS on the fly HTML generiert. Die wirft man nur in den richtigen Ordner und Unterordner – daraus erstellt das CMS auch gleich die Seitenstruktur.

Für kleine Websites ist Pico sehr gut geeignet. Es ist schlank, schnell installiert und läuft auf PHP-Basis. Das ist auch schon der Nachteil: Man muss das CMS für jede Site einzeln installieren (kann also keine Multisite-Netzwerk in einer Shared-Host-Umgebung damit aufbauen wie mit Wordpress). Und: Für Blogs ist es noch nicht ideal.

Grav

Hier kommt Grav ins Spiel: Das ist ein ausgewachsenes CMS (ebenfalls PHP-basiert), dessen Content ebenfalls über Markdown-Dateien gepflegt wird. Was mich für Grav einnimmt: Man kann es per Shell-Befehl installieren, updaten und erweitern – und damit wäre es wohl auch möglich, mehrere Installationen zu verwalten. Auf einem Server mit virtuellen Hosts (mehrere Sites werden vom selben Webserver bedient) sollen damit sogar mit einer Installation und Unterverzeichnissen mehrere Websites zu betreiben sein. Einen guten Überblick über weitere Vorteile gibt dieser Artikel hier.

Grav ist schlanker und schneller als Wordpress und die lebendige Community stellt ein paar schöne Themes zur Verfügung. Auch der Open-Source-Ansatz gefällt mir – und es hat sogar ein webbasiertes Admin-Interface (das man aber nicht benutzen muss – man kann auch einfach die Konfigurationsdateien und die Kommandozeile benutzen).

Nachteile sind:

  1. Grav braucht immer noch PHP und
  2. die Wordpress-Migration funktioniert nicht gut, denn die URL-Struktur bleibt nicht erhalten, sodass man für jede Seite erst mal eine 301-Umleitung einrichten müsste.

Außerdem gefällt mir nicht so gut, dass ich beim Bloggen für jeden Post erst mal einen Ordner mit dem sprechenden URL-Anteil des Blogposts anlegen müsste und dann nochmal eine Datei mit demselben Dateinamen. Das finde ich ein bisschen umständlich.

Nachdem ich mein Blog nicht mit vertretbarem Aufwand auf Grav migrieren konnte, bleibt es aber für weitere Website-Projekte auf jeden Fall immer in der engeren Wahl – insbesondere, wenn Markdown einerseits und ein Web-Interface andererseits zur Content-Pflege verwendet werden sollten.

Nerdig, aber richtig schnell: Jekyll und Hugo

Jekyll und Hugo sind zwei CMS, die eigentlich keine sind. Beides sind HTML-Generatoren, die direkt auf dem installierten Rechner aus Markdown-Dateien HTML-Websites erstellen. Diese kann man dann einfach vom lokalen Rechner per FTP oder Rsync hochladen. Eingestellt wird das Ganze in beiden Fällen über Konfigurationsdateien, es sind verschiedene Themes verfügbar. Beide Systeme sind Open Source. Jekyll ist in der Programmiersprache Ruby geschrieben, Hugo in Go. Wer mit der Kommandozeile gar nicht kann, kann jetzt auch aufhören zu lesen.

Erster Versuch: Hugo

Hugo ist schnell installiert - zumindest auf dem Mac: Mit Homebrew sind es nur ein paar Zeilen im Terminal, schon kann’s losgehen. Als ich allerdings dann versuchte, ein anderes Theme zu installieren, stolperte ich über die ersten Fehlermeldungen bei der Ausgabe im lokalen Vorschau-Webserver. Es gelang mir erst mal nicht, Seiten mit der Struktur zu generieren, die ich haben wollte. Meine erste Analyse: Man muss sich da wohl am besten sein Theme selbst bauen, damit es für die eigenen Bedürfnisse funktioniert.

Bei längerem Nachdenken machte mich am Ende auch die Vorstellung nervös, dass ich Posts nur lokal bearbeiten kann bei so einem System. Wer häufiger von unterschiedlichen Computern aus arbeitet, für den ist ein lokal installierter Webseiten-Generator natürlich ein Problem. Ich beschloss erst mal, noch ein wenig weiter zu suchen.

Zweiter Versuch: Jekyll

Auch die Installation von Jekyll geht über die Kommandozeile ganz fix. Die Dateistruktur ist hier ebenfalls recht einfach. Meine ersten Gehversuche waren vielversprechend und ich dachte schon: Hey, Jekyll und ich werden jetzt richtig gute Freunde. Dann versuchte ich, Themes zu installieren. Das geht am besten über Ruby-Gems und funktioniert, wenn man das Theme einfach so von der Stange nehmen kann. Ich wollte allerdings Dinge in diesem Theme verändern, kann kein Ruby und musste an dieser Stelle dann ziemlich schnell passen. Also, nächster Versuch mit dem nächsten CMS HTML-Generator – oder vorherigen, ganz wie man’s nimmt.

Hugo bezwingen

Das mit den Themes ist bei Hugo eindeutig besser gelöst – die werden ähnlich wie bei Wordpress runtergeladen und in einem bestimmten Ordner gespeichert; anschließend müssen sie nur noch in einer Konfigurationsdatei definiert werden. Durch irgendeinen Zufall habe ich es mit einem bestimmten Theme und einem bestimmten Setup der Dateistruktur dann einstweilen zufriedenstellend hinbekommen. Inzwischen ist das Theme nicht mehr im Originalzustand – mehr dazu unten.

Also erst mal Hugo.

Mein Unbehagen darüber, dass ich ein Blog dann nur an einem Rechner lokal bearbeiten könnte, stellte ich erst mal hintan; denn erstens tat ich das in gefühlten 99% der Fälle tatsächlich – und zweitens besteht bei schnellen Änderungen und statischen HTML-Seiten ja immer die Möglichkeit, direkt auf dem Server die HTML-Dateien zu ändern. Theoretisch ginge das auch unterwegs vom Smartphone aus.

So oder so ist das Ganze für mich ein interessantes Experiment. Ich bin schon sehr gespannt, wann die ersten Enterprise-CMS auftauchen, die ganz auf dieses Konzept setzen: einfach hochperfomantes, statisches HTML ausspucken.

Halbautomatische Publikation – oder: Deployment mit Rsync

Zurück zu meinem Projekt: Jetzt hatte ich also lokal mein Blog in Form von HTML-Dateien herumliegen. Natürlich konnte ich das nach jeder Änderung via FTP-Client hochladen. Das ist aber erstens langwierig und zweitens unkomfortabel. Es gibt wohl ein Tool namens hugodeploy. Ich habe das aber nicht selbst ausprobiert; falls es jemand erfolgreich im Einsatz hat, freue mich aber über Hinweise, wie es funktioniert.

Da ich perspektivisch darüber nachdachte, eine Kopie meiner Hugo-Installation auf einem Server zu spiegeln (um vielleicht doch von jedem Rechner mit Markdown bloggen zu können), kam nun Rsync ins Spiel. Wenn man mal davon absieht, dass ich mich einen halben Tag geärgert habe, bis ich feststellte, dass der von mir dafür vorgesehene FTP-User keine SSH-Rechte auf meinem Server hatte (und ich u.a. deswegen erst einen neuen Webserver aufsetzte), funktioniert das super. Damit ich nicht jedes Mal das Passwort neu eingeben muss, identifiziert sich der Client beim SSH-Server über ein Keyfile. Den Rsync-Befehl habe ich zusammen mit dem Befehl für die Erstellung der Seite in ein Shell-Script gepackt und muss jetzt nur noch ./publish.sh ins Terminal tippen, schon wird erst meine Website lokal erstellt und werden anschließend die geänderten Dateien hochgeladen.

#!/bin/bash
cd /[PFAD]/[ZU]/[LOKALEN]/[HUGO-DATEIEN]/
hugo
rsync --delete --stats -aPvz /[PFAD]/[ZU]/[LOKALEN]/[HUGO-DATEIEN]/public/ -e ssh [USER]@[SERVER]:/var/www/html/

Aufmerksame User werden bemerken, dass ich mir zur Sicherheit von Rsync noch alles ausgeben lasse. Das kann man – wenn man mehr Erfahrung und keine Probleme damit hat – natürlich auch weglassen.

Kommentare mit Disqus – natürlich anonymisiert

Die Kommentarfunktion ist ein kritischer Punkt in einem Blog aus statischen HTML-Dateien: Will man eigentlich haben, aber dynamische Inhalte wie Kommentare und statisches HTML sind natürlich ein Widerspruch in sich. Die Lösung kann da nur ein Kommentarsystem sein, das in Form von iFrames oder via JavaScript nachgeladen wird. Damit hat man dann statische HTML-Dateien mit dynamischen, externen Elementen.

Disqus ist ein sehr verbreitetes Kommentarsystem. Ich hatte schon gelesen, dass Hugo Disqus unterstützt. Wie einfach es war, das zu aktivieren, hat mich dann aber doch überrascht: Einfach anmelden, in der config.toml freischalten und noch ein bisschen im Theme basteln, damit es sauber aussieht – schon hat man statisches HTML mit Kommentarfunktion.

Pro-Tipp: Man sollte dafür sorgen, dass die Preview (http://localhost:1313) keine Kommentarthreads aufmacht, wenn man die Seiten vor der Produktion testet.

Auch die Migration der Wordpress-Kommentare zu Disqus ist nicht schwierig: Man exportiert seine Wordpress-Kommentare als XML-Datei, die man dann bei Disqus importiert. Wichtig: Vorher sollte man alle E-Mail-Adressen und IP-Adressen aus Datenschutzgründen in der XML-Datei anonymisieren. Bei meinem überschaubaren Kommentaraufkommen war das vielleicht eine Stunde Arbeit.

Alten Traffic umleiten: Nginx und 301-Redirects

Wenn man sich wie ich beruflich viel mit Suchmaschinenoptimierung beschäftigt, erträgt man im privaten Blog natürlich nur in begrenztem Umfang 404-Fehlerseiten, weil die Inhalte nach der Migration neue Adressen bekommen.

Da ich als Webserver für die statischen HTML-Dateien auf den schnellen Nginx setze, muss ich die Umleitungen direkt in der Server-Konfigurationsdatei anlegen. Das ist ein wenig Gefrickel. Bei mir kamen von Wordpress noch eingehende Links nach dem Schema /tag/[tag] an, die ich gerne auf /tags/[tag] umleiten wollte, damit sie funktionierten. Das ging, indem ich in den relevanten Server-Block in der Konfigurationsdatei (/etc/nginx/sites-available/default/) folgende Zeilen ergänzte:

location ~ ^/tag/(.*) {
	return 301 /tags/$1;
}

Um eine konkrete Seitenadresse auf eine andere umzuleiten, hat bei mir dieses Schema gut funktioniert:

location ~ ^/link/zur/alten-seite {
	return 301 https://example.net/neuer-link/;
}

Damit kann man dann alle Seiten, die einem z.B. die Google Search Console oder die Logdateien als 404-Fehler anzeigen, auf ihre neuen Ziele umleiten.

Alles so schön schnell hier

Einige Tests, die ich vorab mit einer Testinstallation gemacht hatte, waren schon vielversprechend. Nachdem ich die neue Site dann aber aus diversen Gründen (siehe z.B. oben) bei den Google Webmaster Tools angemeldet hatte und sie ein paar Tage lief, sah ich, dass ich mit dem Grund für die ganze Aktion richtig lag: Der Geschwindigkeitsgewinn durch die statischen HTML-Seiten ist enorm. Die Ladezeiten haben sich nicht etwa halbiert, sondern sind nun bis zu 10 Mal schneller. User (oder zumindest der Google-Bot) warten nicht mehr über 1.600 Millisekunden, sondern nur noch 133 122 bis 300. Das ist einigermaßen rasant - zumindest im Vergleich.

Download-Zeiten aus der Google Search Console: Von 1500 auf 133 ms

Nachtrag: Auch zwei Monate später gefällt mir die Download-Geschwindigkeit sehr.

Download-Zeiten aus der Google Search Console: Von 1500 auf 122 ms

Zusätzlich habe ich auch dadurch die Performance der Site steigern können, dass sie nun auch bei den Besucherstatistiken auf Datenbankabfragen verzichtet. Bei meinen Wordpress-Blogs nutze ich dafür Piwik. Das ist ein tolles Tool – kommuniziert aber auch via PHP mit einer MySQL-Datenbank und das braucht eben seine Zeit. Und sind wir ehrlich: Dies hier ist ein einfaches Blog ohne Gewinnerzielungsabsicht – da brauch ich keine elaborierten Tracking-Tools, sondern will höchstens hin und wieder mal wissen, was besonders häufig aufgerufen wird. Dafür tut’s auch eine Auswertung der Logfiles via AWStats. Dadurch ist jetzt das einzige von mir eingebaute externe Element, das Ladezeiten hat, Disqus. Und das lädt sich erst, wenn de Seite aufgebaut ist. Schneller geht’s nicht (bzw. nur noch, indem man HTML, CSS und JavaScript verschlankt und Bilder optimiert).

Schriften ersetzen

Nun packte mich der Ehrgeiz: Wenn ich schon mit so wenig dynamischen und externen Elementen auskam, dann ist es doch mehr als ärgerlich, dass die Schriften für das Bootstrap-basierte Theme („Start Bootstrap Clean Blog“) von Google geladen werden müssen. Also: Weg damit! Das ist gar nicht so schwierig …

Die einfache Methode:

Man kann die Google-Schriften „Open Sans“ und „Lora“ einfach durch Standardschriften ersetzen (Helvetica und Times New Roman wären die nächsten in der Reihe). Und zwar so:

  1. CSS-Datei im Texteditor öffnen.
  2. Suche: Open Sans – Ersetze: [leer]
  3. Suche: Lora – Ersetze: [leer]
  4. Speichern.

Dadurch wird (im Falle meines Themes) für Überschriften Helvetica verwendet, für den Fließtext Times New Roman. Das ist sicher nicht kreativ, aber machbar.

Die kompliziertere Methode

Klar, dass mir das zu einfach war. Ich mag ja die Schriften „Roboto“ und „Roboto Slab“ und habe die schon früher für mein Blog eingesetzt. Die kann man lokal vom Server laden lassen, indem man sie dort abspeichert und über die CSS-Datei einbindet. Das habe ich gemacht – mit optisch für mich akzeptablem Ergebnissen.

Update, 28.9.2019: Inzwischen verwende ich eine andere Schriftart und unterscheide nicht mehr zwischen Überschriften und Fließtext, das ändert aber nichts am Prinzip.

Wichtig bei beiden Methoden: Externe Referenzen im HTML-Header löschen!

Allerdings sah ich bei einer Netzwerkanalyse der Seitenaufrufe, dass immer noch Verbindungen zu Google und bootstrapcdn.com hergestellt wurden. Den Übeltäter hatte ich schnell im Header-Template ausgemacht (bzw. im entsprechenden „Partial“, wie diese HTML-Teildateien eines Themes bei Hugo heißen). In der Datei .../layouts/partials/header.html des Theme-Verzeichnisses fand ich die folgenden Codezeilen:

<!-- Custom Fonts -->
<link href="//maxcdn.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.min.css" rel="stylesheet" type="text/css">
<link href='//fonts.googleapis.com/css?family=Lora:400,700,400italic,700italic' rel='stylesheet' type='text/css'>
<link href='//fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800' rel='stylesheet' type='text/css'>

Wenn man diesen Absatz einfach löscht, kommt die Website gänzlich ohne externe Aufrufe aus. Vermutlich würde es auch funktionieren, wenn man es dabei bewenden ließe – und die CSS-Datei würde dann einfach als Fallback auf die nächsten Schriften in der Reihe rekurrieren. Aber sauberer ist es natürlich, die Schriften an beiden Stellen zu entfernen.

Nächste Schritte

Damit läuft jetzt erst mal alles und ich kann mich aufs Schreiben konzentrieren. Allerdings ist so ein Projekt natürlich keines, wenn man nicht noch ein paar Sachen auf der To-Do-Liste hat. Bei mir sind das:

  • Die Kommentarfunktion: Disqus ist zwar ein tolles Tool, aber ich habe schlechte Erfahrungen damit gemacht, Tools zu verwenden, die bei externen Anbietern liegen. Diese können plötzlich ihr Geschäftsmodell ändern oder ihr Kerngeschäft auch einfach mal einstellen, weil der Käufer des Startups z.B. nur am Personal interessiert war. So ging mir das etwa seinerzeit mit Posterous. Datenschutz ist ein weiteres Thema in diesem Zusammenhang – Disqus ist nun mal eine US-Firma. Wenn ich Zeit habe, werde ich mich also nochmal eingehender mit selbstgehosteten Kommentarsystemen auseinandersetzen. Isso klingt zum Beispiel ganz vielversprechend. Update 19.5.2018: Ausprobiert und wieder eingestellt.
  • Hosting über GitHub Pages: Hugo macht besonders viel Spaß, wenn man vielleicht noch eine Versionsverwaltung und ähnlichen Kram hat – und über einen großen Anbieter noch ein CDN oder was auch immer davor. Website-Generatoren wie Hugo oder Jekyll werden oft zusammen mit GitHub Pages eingesetzt. Das wäre dann ein möglicher nächster Level. Update 21.10.2018: Versionsverwaltung und automatisches Deployment habe ich nun mit GitLab realisiert – das ist insgesamt noch bessser, als ich dachte. Die Idee mit dem Hosting bei GitHub/GitLab habe ich erst mal verworfen.
  • Hosting über Amazon AWS: Eine Alternative zum eigenen virtuellen Server ist natürlich eine Cloud-Lösung. Deshalb werde ich sicher bei Gelegenheit versuchen, die Site auf Amazon AWS zu hosten (falls das nicht zu teuer wird). Die notwendige Basis für einen einfachen Einstieg habe ich ja nun dadurch, dass alles nur aus HTML, CSS und etwas JavaScript besteht.

Natürlich gehören zu einem Umstieg von Wordpress zu Hugo auch Inhalte. Mehr zur Migration gibt’s im Artikel „Aufräumen im Blog“.