Ça faisait pas loin d’un an que l’idée trottait dans un coin de ma tête, migrer de WordPress vers Hugo. Je n’avais plus envie de cette usine à gaz toujours trouée, de ce moteur monopolistique sur internet. Et puis le Markdown c’est nettement plus sympa.

Hugo en deux mots

Hugo est un générateur de site statique libre, écrit en Go et livré sous la forme d’un unique binaire. Là où WordPress fabrique chaque page à la volée en interrogeant sa base de données, Hugo fait tout le travail une fois pour toutes : il prend les articles écrits en Markdown, les passe dans les gabarits du thème et produit un site complet en HTML, prêt à être servi tel quel.

Un projet Hugo, c’est une poignée de dossiers :

.
├── hugo.yaml     # configuration du site
├── content/      # les articles en Markdown
├── static/       # fichiers copiés tels quels
├── themes/       # le thème : gabarits, CSS, JS
└── public/       # le site généré, à publier

Pendant l’écriture, hugo server sert le site en local et le recharge à chaque modification. Et c’est rapide : les quelque 280 pages du blog sont générées en quelques secondes, l’essentiel du temps de construction étant passé à copier les photos.

Pourquoi quitter WordPress

La première raison, c’est la maintenance. Un WordPress, c’est PHP, MySQL, le cœur de WordPress et une poignée d’extensions à tenir à jour en permanence, avec une surface d’attaque qui va avec : wp-login.php et xmlrpc.php sont scannés en boucle par tous les robots du net. Un site statique n’exécute rien côté serveur, il n’y a plus rien à patcher en dehors de nginx.

La deuxième, c’est l’écriture. J’écris maintenant mes articles en Markdown dans mon éditeur, chaque article est un dossier dans un dépôt git, et l’historique des modifications vient tout seul. Publier un article revient à faire un commit.

Enfin l’hébergement devient trivial : un nginx qui sert des fichiers, aucune base à sauvegarder, le dépôt git sert de sauvegarde.

L’export avec wp2hugo

Pour la conversion, j’ai utilisé wp2hugo, qui part du fichier d’export XML de WordPress. Il récupère les articles, les pages, les médias et même les commentaires, et chaque article conserve son URL d’origine grâce au paramètre url de son en-tête.

Pour que l’export fonctionne, cela a nécessité pas mal de préparation. J’ai beaucoup utilisé l’extension NextGEN Gallery et malheureusement wp2hugo ne sait pas la prendre en charge. J’ai donc tout migré vers des galeries standard WordPress et j’en ai profité pour redimensionner les images insuffisamment compressées.

Les commentaires sont exportés dans un fichier data/comments.yaml et réaffichés sous chaque article : les 122 commentaires du blog ont survécu. Bon après je me fiche un peu des commentaires.

Le résultat est directement exploitable, mais un export de plus de 230 articles, dont les plus anciens datent du début des années 2000, ne passe jamais sans quelques retouches. Et puis surtout, la migration est un peu brute de fonderie, elle conserve la structure du wp-content et la présentation des photos est assez rudimentaire.

Ce qu’il a fallu reprendre

Le thème. J’ai écrit un thème maison, sobre : fond gris anthracite, titres vert sauge et un menu latéral construit à partir des catégories. Pas de police ni de script tiers : tout est servi depuis le site.

Les images. wp2hugo dépose tous les médias dans static/wp-content/uploads/, à la WordPress : 5 000 photos et 3,7 Go dans une seule arborescence, sans lien visible avec les articles. J’ai automatisé la conversion de chaque article en page bundle, c’est-à-dire un dossier qui contient l’article et ses images :

content/posts/24h-a-peak-district/
├── index.md
├── 01092025-DSC_1545-scaled.jpg
├── 01092025-DSC_1595-scaled.jpg
└── ...

Un script a déplacé les 5 107 images utilisées par un seul article et réécrit les 6 685 références en chemins relatifs. Les quelques logos partagés entre plusieurs articles sont restés dans static/.

Le ménage. La migration a été l’occasion de corriger ce que WordPress cachait : des liens vers des images -scaled qui n’existaient plus, des doublons hérités de NextGEN Gallery, deux pages qui portaient la même URL qu’un article, ou encore des blocs de code exportés en code en ligne, qui s’affichaient sur une seule ligne.

Les galeries. Les photos sont chargées à la demande (loading="lazy") et les galeries s’affichent en mosaïque, en CSS seulement, à partir des dimensions de chaque photo lues par Hugo. Seules les images de couverture des listes ont droit à une vignette.

Construction et publication

La génération du site se résume à une commande, le résultat atterrit dans public/ :

julien@gollum:~$ hugo --minify --gc --cleanDestinationDir

La publication passe par GitLab CI : à chaque commit sur main, le pipeline télécharge la version de Hugo voulue (empreinte SHA-256 vérifiée), construit le site et l’envoie sur le serveur avec rsync en SSH. Côté serveur, la clé de déploiement est restreinte avec rrsync, elle ne peut qu’écrire dans le dossier du site :

command="/usr/bin/rrsync -wo /var/www/morot.fr",restrict ssh-ed25519 AAAA... gitlab-ci morot.fr

Seuls les fichiers modifiés partent :

rsync -rlz --checksum --delete public/ deploy@www.morot.fr: