Ce site, que j’exploitais autrefois sur un domaine personnalisé avec Google Sites (époque Google Apps), est resté longtemps à l’abandon depuis l’arrêt du service et l’URL https://www.ixam.net dormait dans un coin. J’ai fini par décider qu’il était temps de le remettre en service et de lui donner une nouvelle vie : voici le résultat de mes essais avec ChatGPT pour obtenir une plateforme sans serveur qui ne dépende pas d’un service susceptible de disparaître comme Google Sites.

Je pensais honnêtement que ChatGPT suffirait largement pour y arriver sans peine, mais la réalité a été différente : oublis de besoins fondamentaux, débogage en boucle… Il a fallu que je complète par mes propres recherches, notamment en m’appuyant sur le dépôt GitHub https://github.com/patrickgrey/website/, pour finir par construire le site en mode coopération.

La sensation était très forte de diriger un subordonné techniquement compétent mais qui part régulièrement dans la mauvaise direction, et de devoir le remettre sur les rails pour atteindre le résultat voulu. En un sens, c’est impressionnant de voir une IA arriver à ce niveau. Et quand les sujets deviennent vraiment complexes, l’assistant se met vite à m’agacer par son degré d’ineptie insupportable. Est-ce que ce n’est pas finalement très proche du monde réel ?

Pour l’instant, il semble plus efficace de repartir sur un nouveau fil de discussion bien cadré dès que la conversation devient trop chargée, plutôt que d’exiger de l’IA qu’elle comprenne tous les prérequis en accumulant les échanges. Cela dit, en termes de volume de recherche, de travail et d’apprentissage, je n’aurais clairement pas pu mener ce projet seul : l’effet de levier de l’IA générative est incroyable.


Objectifs

  • Afficher le blog sur https://www.example.com/
  • Pouvoir se connecter à DecapCMS via https://www.example.com/admin/ pour créer des articles
  • Les articles créés sont validés sur GitHub et déployés automatiquement sur Cloudflare
  • À ce stade, les coûts d’exploitation supplémentaires sont nuls (hors frais d’enregistrement du domaine)

0. Prérequis et structure des dossiers

0-1. Prérequis

  • Supposons que le domaine (example.com) est déjà enregistré

  • Le système d’exploitation peut être Windows ou macOS (les exemples de commandes sont fournis pour les deux)

  • Services utilisés (niveau gratuit suffisant)

0-2. Structure principale du dépôt (exemple)

hugo.toml
content/
  blog/
  posts/
data/
functions/
  api/
    auth.js
    callback.js
layouts/
  _default/
    baseof.html
    index.html
    list.html
    single.html
static/
  _headers
  admin/
    config.yml
    index.html
  css/
    main.css

Cette structure remplit les rôles suivants :

  • hugo.toml … Configuration globale du site (baseURL à remplacer par l’URL de production)
  • functions/api/*.js … Cloudflare Pages Functions (OAuth GitHub pour /api/auth et /api/callback)
  • layouts/_default/*.html … Modèles Hugo
  • static/admin/ … Interface et configuration de DecapCMS
  • static/css/main.css … Apparence (CSS)
  • static/_headers … Paramètres HTTP pour Cloudflare Pages (optionnel)

Dans cette procédure, les éléments à adapter à votre environnement sont indiqués explicitement sous la mention « Contrôle de remplacement ».


1. Préparation

1-1. Créer un compte GitHub

  • Ouvrir GitHub dans un navigateur et s’inscrire

1-2. Créer un compte Cloudflare

  • Ouvrir Cloudflare dans un navigateur et s’inscrire

1-3. Installer Git et Hugo

  • Windows (PowerShell)

    PS C:\Users\alice> winget install Git.Git
    PS C:\Users\alice> winget install Hugo.Hugo.Extended
    
  • macOS (Terminal)

    mac:~ dev$ brew install git
    mac:~ dev$ brew install hugo
    

2. Procédure principale (jusqu’au déploiement)

2-1. Préparer le dépôt en local

  1. Créer un dépôt vide sur GitHub

    • Interface : GitHub > New repository
    • Nom suggéré : my-hugo-blog
  2. Cloner le dépôt en local

    • Windows

      PS C:\work> git clone https://github.com/<YOUR_GH_USERNAME>/my-hugo-blog.git
      PS C:\work> cd .\my-hugo-blog
      
    • macOS

      mac:~/work dev$ git clone https://github.com/<YOUR_GH_USERNAME>/my-hugo-blog.git
      mac:~/work dev$ cd my-hugo-blog
      
  3. Placer l’ensemble des dossiers et fichiers présentés ci-dessus à la racine de ce dépôt

Contrôle de remplacement (à ajuster impérativement)

  • hugo.toml

    baseURL = "https://www.example.com"   # ← remplacer par l'URL de production (ex. : https://www.example.com)
    languageCode = "ja-jp"
    title = "Example Blog"
    publishDir = "public"
    
    [permalinks]
      blog = "/blog/:year/:month/:slug/"
    
  • static/admin/config.yml

    backend:
      name: github
      repo: <YOUR_GH_USERNAME>/my-hugo-blog   # ← remplacer par votre dépôt GitHub
      branch: master                          # ← nom de la branche par défaut (main ou master)
      base_url: https://www.example.com       # ← remplacer par l'URL de production
      auth_endpoint: /api/auth                # ← point de terminaison Functions (fixe)
    media_folder: static/uploads
    public_folder: /uploads
    

Remarque : functions/api/auth.js et functions/api/callback.js peuvent être utilisés tels quels (ils s’appuient sur url.origin, il n’y a donc aucun paramètre spécifique à votre environnement à coder en dur).

  1. Premier commit et push

    • Windows

      PS C:\work\my-hugo-blog> git add -A
      PS C:\work\my-hugo-blog> git commit -m "Initial commit: Hugo + [DecapCMS](https://decapcms.org/) + CF Pages"
      PS C:\work\my-hugo-blog> git push -u origin master
      
    • macOS

      mac:~/work/my-hugo-blog dev$ git add -A
      mac:~/work/my-hugo-blog dev$ git commit -m "Initial commit: Hugo + [DecapCMS](https://decapcms.org/) + CF Pages"
      mac:~/work/my-hugo-blog dev$ git push -u origin master
      

2-2. Créer une application GitHub OAuth (pour la connexion DecapCMS)

  1. Interface : GitHub > Settings > Developer settings > OAuth Apps > New OAuth App

  2. Saisir :

    • Application name : [DecapCMS](https://decapcms.org/) for my-hugo-blog
    • Homepage URL : https://www.example.com
    • Authorization callback URL : https://www.example.com/api/callbackimportant
  3. Après la création, relever :

    • Client ID
    • Client Secret (générer une valeur et la conserver)

Ces éléments seront enregistrés comme variables d’environnement dans Cloudflare Pages.


2-3. Créer le projet Cloudflare Pages

  1. Interface : Cloudflare Dashboard > Pages > Create a project > Connect to Git

  2. Connecter GitHub et sélectionner le dépôt my-hugo-blog

  3. Paramétrer la compilation :

    • Framework preset : None (ou laisser Cloudflare détecter automatiquement)

    • Build command : hugo

    • Build output directory : public

    • Variables d’environnement :

      • HUGO_VERSION = 0.128.0 (par exemple, pour correspondre à la version utilisée en local)
      • GITHUB_CLIENT_ID = (valeur récupérée à l’étape précédente)
      • GITHUB_CLIENT_SECRET = (valeur récupérée à l’étape précédente)
  4. Cliquer sur Save and Deploy et attendre le premier déploiement

    • Une URL de prévisualisation *.pages.dev est générée en cas de succès

2-4. Associer le domaine personnalisé www.example.com

  1. Interface : Cloudflare > Pages > (projet concerné) > Custom domains > Set up a custom domain

  2. Saisir www.example.com pour l’ajouter

  3. Si le domaine n’est pas transféré chez Cloudflare :

    • Noter les enregistrements DNS affichés dans Cloudflare (ex. : CNAME www -> <project>.pages.dev)
    • Sur l’interface de votre registrar, configurer ces enregistrements conformément aux instructions (les étapes varient selon le registrar)
  4. Après propagation, vérifier que https://www.example.com/ s’ouvre correctement

Astuce : si le domaine est déjà ajouté côté Cloudflare, un simple bouton suffit pour appliquer automatiquement les enregistrements DNS nécessaires.


2-5. Accéder au panneau d’administration DecapCMS

  1. Ouvrir https://www.example.com/admin/ dans le navigateur
  2. Cliquer sur le bouton « Login with GitHub » et autoriser GitHub OAuth
  3. Lors de la première connexion, GitHub demande « Autorisez-vous cette application ? » → cliquer sur Authorize

En cas d’échec de connexion, vérifier à nouveau l’URL de rappel OAuth et les variables d’environnement Cloudflare (GITHUB_CLIENT_ID/SECRET).


3. Exploitation (création d’articles, synchronisation locale, modification des templates)

3-1. Créer un article depuis le CMS

  1. Interface : https://www.example.com/admin/
  2. Barre latérale : BlogNew blog
  3. Saisir : Title, Publish Date, Description, Body
  4. Cliquer sur Publish → un commit est généré sur GitHub et Cloudflare déploie automatiquement

Le Markdown généré est placé dans content/blog/.

3-2. Mettre à jour le dépôt local après une publication via le CMS

  • Windows

    PS C:\work\my-hugo-blog> git pull origin master
    
  • macOS

    mac:~/work/my-hugo-blog dev$ git pull origin master
    

Cela permet de récupérer en local les derniers fichiers d’article et de continuer sereinement les modifications de templates ou de CSS.

3-3. Ajuster le design et les templates en local

  1. Lancer le serveur de développement local

    • Windows

      PS C:\work\my-hugo-blog> hugo server -D
      
    • macOS

      mac:~/work/my-hugo-blog dev$ hugo server -D
      
    • Ouvrir http://localhost:1313/ dans le navigateur pour vérifier

  2. Points de modification possibles :

    • layouts/_default/baseof.html<head> et en-têtes/pieds de page
    • layouts/_default/index.html … Liste « derniers articles » en page d’accueil
    • layouts/_default/single.html … Corps d’un article
    • static/css/main.css … Couleurs, police, espacements
  3. Valider les modifications et pousser

    • Windows

      PS C:\work\my-hugo-blog> git add -A
      PS C:\work\my-hugo-blog> git commit -m "Update theme/layouts"
      PS C:\work\my-hugo-blog> git push
      
    • macOS

      mac:~/work/my-hugo-blog dev$ git add -A
      mac:~/work/my-hugo-blog dev$ git commit -m "Update theme/layouts"
      mac:~/work/my-hugo-blog dev$ git push
      
    • Quelques secondes à quelques minutes plus tard, Cloudflare déploie automatiquement

3-4. Conseils pour la gestion des branches

  • Harmoniser la branche par défaut (main ou master) entre le config.yml de DecapCMS et la configuration Cloudflare
  • Pour prévisualiser, créer une Pull Request GitHub : Cloudflare Pages produit alors automatiquement un environnement de prévisualisation

4. Compléments utiles (optionnel)

4-1. Exemple de static/_headers (ne pas mettre en cache l’interface d’administration)

/admin/*
  Cache-Control: no-store

4-2. Robots et sitemap (selon les besoins)

  • Préparer static/robots.txt pour contrôler l’accès des robots
  • Ajouter des sorties supplémentaires (RSS, sitemap, etc.) via outputs dans hugo.toml

5. Problèmes fréquents et solutions

  • La connexion CMS tourne en boucle
    → Vérifier que l’URL de rappel GitHub OAuth est https://www.example.com/api/callback et que les variables Cloudflare GITHUB_CLIENT_ID/SECRET sont correctes
  • La page d’accueil fonctionne mais les articles renvoient 404
    → S’assurer que baseURL dans hugo.toml correspond bien au domaine réel. Contrôler dans les journaux Cloudflare que public/ est généré
  • /admin reste vide
    → Vérifier que le chargement de DecapCMS dans static/admin/index.html n’est pas bloqué (ex. par une extension navigateur). Désactiver les extensions et recharger

6. Récapitulatif des éléments à personnaliser

Fichier/paramètre Clé Valeur à définir (exemple)
hugo.toml baseURL https://www.example.com
static/admin/config.yml repo <YOUR_GH_USERNAME>/my-hugo-blog
static/admin/config.yml branch main ou master selon votre configuration
static/admin/config.yml base_url https://www.example.com
Cloudflare Pages HUGO_VERSION Ex. : 0.128.0
Cloudflare Pages GITHUB_CLIENT_ID Client ID de l’application GitHub OAuth
Cloudflare Pages GITHUB_CLIENT_SECRET Client Secret de l’application GitHub OAuth
Application GitHub OAuth Callback URL https://www.example.com/api/callback
Cloudflare Pages Custom domain Ajouter www.example.com

7. Annexe : exemples de fichiers (à adapter selon vos besoins)

7-1. functions/api/auth.js

// Cloudflare Pages Functions (/api/auth)
export async function onRequest(context) {
  const { request, env } = context;
  const client_id = env.GITHUB_CLIENT_ID;
  try {
    const url = new URL(request.url);
    const redirectUrl = new URL('https://github.com/login/oauth/authorize');
    redirectUrl.searchParams.set('client_id', client_id);
    redirectUrl.searchParams.set('redirect_uri', `${url.origin}/api/callback`);
    redirectUrl.searchParams.set('scope', 'repo user');
    redirectUrl.searchParams.set('state', crypto.getRandomValues(new Uint8Array(12)).join(''));
    return Response.redirect(redirectUrl.href, 302);
  } catch (err) {
    return new Response(String(err?.message || err), { status: 500 });
  }
}

7-2. functions/api/callback.js

function renderBody(status, content) {
  const html = `
  <script>
    const receiveMessage = (message) => {
      window.opener.postMessage(
        'authorization:github:${status}:${JSON.stringify(content)}',
        message.origin
      );
      window.removeEventListener("message", receiveMessage, false);
    }
    window.addEventListener("message", receiveMessage, false);
    window.opener.postMessage("authorizing:github", "*");
  </script>`;
  return new Blob([html]);
}

export async function onRequest(context) {
  const { request, env } = context;
  const client_id = env.GITHUB_CLIENT_ID;
  const client_secret = env.GITHUB_CLIENT_SECRET;
  try {
    const url = new URL(request.url);
    const code = url.searchParams.get('code');
    const response = await fetch('https://github.com/login/oauth/access_token', {
      method: 'POST',
      headers: { 'content-type': 'application/json', 'user-agent': 'cf-pages-oauth', 'accept': 'application/json' },
      body: JSON.stringify({ client_id, client_secret, code }),
    });
    const result = await response.json();
    if (result.error) {
      return new Response(renderBody('error', result), { headers: { 'content-type': 'text/html;charset=UTF-8' }, status: 401 });
    }
    const token = result.access_token;
    const provider = 'github';
    return new Response(renderBody('success', { token, provider }), { headers: { 'content-type': 'text/html;charset=UTF-8' }, status: 200 });
  } catch (error) {
    return new Response(error.message, { headers: { 'content-type': 'text/html;charset=UTF-8' }, status: 500 });
  }
}

7-3. static/admin/config.yml

backend:
  name: github
  repo: <YOUR_GH_USERNAME>/my-hugo-blog
  branch: main
  base_url: https://www.example.com
  auth_endpoint: /api/auth

media_folder: static/uploads
public_folder: /uploads

collections:
  - name: 'blog'
    label: 'Blog'
    folder: 'content/blog'
    create: true
    slug: '{{slug}}'
    editor:
      preview: false
    fields:
      - { label: 'Title', name: 'title', widget: 'string' }
      - { label: 'Publish Date', name: 'date', widget: 'datetime' }
      - { label: 'Description', name: 'description', widget: 'string' }
      - { label: 'Body', name: 'body', widget: 'markdown' }


9. Points clés pour optimiser SEO et performances

Après la mise en place du blog, les actions suivantes peuvent améliorer encore le référencement et la vitesse d’affichage.

  1. Configurer sitemap et robots.txt

    • Enregistrer public/sitemap.xml (généré par Hugo) dans Google Search Console
    • Ajouter des règles de crawl dans static/robots.txt
  2. Configurer OGP (Open Graph) et les Twitter Cards

    • Ajouter <meta property="og:title">, <meta property="og:image">, etc. dans layouts/_default/baseof.html
    • Soigner l’apparence lors du partage sur les réseaux sociaux
  3. Convertir les images en WebP

    • Exploiter resources/_gen/images/ et le pipeline Hugo pour générer automatiquement des WebP
    • Améliorer les temps de chargement
  4. Ajouter des catégories et tags

    • Enrichir le Front Matter dans content/blog/ avec categories et tags, afficher un nuage de tags dans le template
  5. Introduire des données structurées

    • Fournir explicitement les informations d’article en JSON-LD pour viser les rich results

8. Conclusion

  • Créez un dépôt sur GitHub, reliez-le à Cloudflare Pages, configurez GitHub OAuth pour DecapCMS et associez votre domaine personnalisé : votre site est prêt
  • Les articles se créent depuis /admin/ → commit sur GitHub → déploiement automatique par Cloudflare
  • Pour les templates et le design, travaillez en local avec hugo server, puis poussez vos changements via Git

Vous disposez désormais d’un environnement de blog sans serveur basé sur Hugo + GitHub + Decap CMS + Cloudflare, prêt pour la production.

Nous avons ainsi obtenu une structure de site à coût d’exploitation minimal. Le revers de la médaille : en l’état, la recherche interne, les catégories ou encore les nuages de tags restent modestes face aux blogs clés en main. Je vais continuer à enrichir tout cela pas à pas, main dans la main avec ChatGPT.