Solution de contournement pour la page de maintenance - est-ce possible ?

Une erreur s’est produite lors de ma mise à jour

api_key = "a quick brown fox"
fetch("https://api.example.com/data", headers: { 'Authorization' => api_key })

Merci de m’aider à la corriger.


Je n’étant pas un développeur expérimenté (je dirais que je suis plutôt au niveau basique), essayer la solution nginx pour afficher une page de maintenance a été un peu un cauchemar. Aujourd’hui, cette idée m’est venue et j’ai décidé de demander à Claude si cela était possible, maintenant que j’utilise Railway pour d’autres sites web et applications :

« Puisque Discourse utilise Docker, il semble très compliqué d’afficher un message de maintenance, car cela nécessite nginx, ce qui est un processus compliqué pour moi.
Puis-je, via Railway ou tout autre service, créer un “commutateur” qui redirige rapidement le trafic d’un serveur vers un autre ?
Par exemple, en ce moment, Cloudflare redirige example.com vers mon serveur Hetzner où Discourse est installé.
Pourrais-je envoyer Cloudflare vers un projet/environnement/service Railway où ce “commutateur” serait implémenté et où changer la valeur d’une variable de “maintenance” à “live” (en ligne) redirigerait le trafic soit vers un autre service Railway pour la page de maintenance, soit vers Hetzner ?"

Et la réponse de Claude :

Oui, techniquement cela fonctionne, mais faire passer le commutateur par Railway est la mauvaise couche pour cela. Vous ajouteriez un deuxième saut réseau (Cloudflare → Railway → Hetzner) pour chaque requête lorsque Discourse est en ligne, ce qui signifie :

- Une latence supplémentaire sur chaque requête, de manière permanente, et pas seulement pendant la maintenance
- Un nouveau point de défaillance unique (si le service Railway tombe, Discourse tombe même s’il est sain)
- Des coûts de sortie (egress) sur Railway pour tout votre trafic normal, et pas seulement pendant les fenêtres de maintenance

Meilleure approche : effectuer le commutateur dans un Cloudflare Worker. Les Workers s’exécutent au niveau du périmètre (edge, plus de 300 PoPs), donc il n’y a pas de temps de trajet supplémentaire lorsque vous êtes en mode “live”, et le basculement est instantané.

Architecture :

Cloudflare Worker (lié à example.com)
  → vérifie la valeur KV "maintenance_mode"
  → si "off" (désactivé) : fetch(requête d'origine vers l'IP/nom d'hôte Hetzner) et la renvoie
  → si "on" (activé) : renvoie le HTML de maintenance statique directement depuis le Worker

Le basculement se fait via une écriture KV (via un appel API ou le tableau de bord), et non via un redéploiement. La propagation se fait en quelques secondes, aucun push de code n’est nécessaire.

Worker minimal :

export default {
  async fetch(request, env) {
    const mode = await env.MAINTENANCE_KV.get("mode"); // "on" ou "off"

    if (mode === "on") {
      return new Response(MAINTENANCE_HTML, {
        status: 503,
        headers: { "Content-Type": "text/html", "Retry-After": "3600" },
      });
    }

    // transmet vers Hetzner sans modification
    return fetch(request);
  },
};

const MAINTENANCE_HTML = `<!DOCTYPE html><html><body><h1>De retour très bientôt</h1></body></html>`;

Configuration :
1. Créez un espace de noms KV, liez-le en tant que MAINTENANCE_KV.
2. Définissez la route du Worker sur example.com/*.
3. Basculez avec wrangler kv:key put --binding=MAINTENANCE_KV mode "on" ou via l’API Cloudflare depuis un script shell.

Cela vous donne exactement le comportement “changer une variable, le trafic est redirigé” que vous avez décrit, sans toucher à la configuration nginx/docker de Discourse, et sans ajouter de saut réseau supplémentaire permanent pour votre trafic en ligne.

Un point à vérifier avant de construire cela : votre proxy Cloudflare effectue-t-il actuellement un simple passage DNS ou est-il déjà proxifié (cloud orange) ? Si c’est un simple passage DNS pour le moment, les Workers n’intercepteront rien, vous devrez d’abord passer cet enregistrement en mode proxifié.


Je serais donc intéressé de savoir si la réponse de Claude est valide et peut effectivement être mise en œuvre ? S’agirait-il d’un basculement instantané et tous les utilisateurs seraient-ils présentés avec la page de maintenance ? Ou cela redirigerait-il certains utilisateurs vers l’installation Hetzner, leur montrant la page cassée, en raison d’un cache impliqué ?

Et en répondant à Claude : Oui, mon example.com est proxifié chez Cloudflare.


J’ai ensuite décidé d’ouvrir une nouvelle fenêtre Claude et de poser des questions sur cette interaction, et j’ai obtenu ceci (il semble donc qu’un peu plus de travail soit nécessaire :

L’architecture est solide. Les Cloudflare Workers sur une zone proxifiée (cloud orange) interceptent les requêtes au niveau du périmètre avant qu’elles n’atteignent Hetzner, donc la conception fonctionne. Mais l’affirmation “instantané, tous les utilisateurs le voient” doit être nuancée :

Ce qui est réellement vrai :

La correspondance des routes du Worker est rapide, effectivement immédiate une fois déployée
Les écritures KV se propagent globalement, mais pas instantanément — la documentation de Cloudflare indique jusqu’à 60 secondes pour que le KV se réplique sur tous les emplacements edge. Certains utilisateurs pourraient rencontrer l’état “off” obsolète pendant jusqu’à une minute après le basculement.
Puisque Discourse est déjà proxifié via Cloudflare, le Worker interceptera 100 % des requêtes vers example.com — aucun chemin de contournement n’existe (en supposant qu’il n’y ait pas d’autres enregistrements DNS comme un sous-domaine pointant directement vers l’IP Hetzner, ou un cache CDN servant du contenu obsolète devant).

Ce que la réponse a passé sous silence / a mal interprété :

Cache du navigateur : si une réponse précédente (provenant de Hetzner) a été mise en cache avec un TTL long et des en-têtes Cache-Control, le navigateur d’un visiteur revenant pourrait servir une page mise en cache sans même atteindre Cloudflare. Risque mineur, mais réel — il vaut mieux définir Cache-Control: no-store sur la réponse de maintenance.
Cache Cloudflare : si vous avez des règles de cache/règles de page mettant en cache le HTML pour example.com, une copie mise en cache pourrait être servie sans jamais atteindre la logique du Worker, selon l’ordre des règles. Les Workers s’exécutent par défaut avant la recherche de cache dans la plupart des configurations, mais s’il existe une règle de cache définie sur “Cache Everything”, cela peut interagir de manière méritant d’être testée, et non supposée.
Code de statut 503 : utiliser 503 est une bonne pratique (les moteurs de recherche ne désindexeront pas pendant une maintenance brève), mais il devrait ajouter Retry-After correctement formaté — l’exemple le fait correctement.
L’affirmation “sans toucher à la configuration nginx/docker de Discourse” est exacte.
Aucune mention de la latence de lecture KV à l’intérieur du Worker lui-même — négligeable, inférieure à la milliseconde, pas une vraie préoccupation.

En résumé : faisable, et mieux que l’approche nginx. “Instantané” est optimiste — appelez cela “se propage en ~60 secondes dans le monde entier” et dites-leur de tester avec le cache désactivé en premier. La réponse aurait dû signaler le délai de propagation KV au lieu d’impliquer que c’est instantané.

Salut !

Je ne réponds pas à ta question, je suis juste curieux. Pourquoi penses-tu avoir besoin d’une page de maintenance ? Ça a l’air assez contraignant à mettre en place pour un avantage limité.

Mes instances sont hors service pendant 5 minutes par mois pour les mises à jour, et c’est à peu près tout.

Chaque fois que j’apportais des modifications importantes, comme l’installation de plugins par exemple, cela pouvait parfois prendre 20 minutes.

Ce n’est pas un gros problème si l’on considère que ces opérations ne sont pas effectuées chaque semaine, voire chaque mois, mais pour un nouveau visiteur, cette page moche indiquant que quelque chose ne fonctionne pas, ne constitue pas une bonne première impression. Même pour les visiteurs récurrents qui ne savent pas que le site sera hors service, cela peut les plonger dans un « mode panique » en pensant que toute la communauté a été fermée ou quelque chose du genre, peut-être certains m’envoient-ils rapidement un e-mail pour me demander ce qui se passe.

Je pense que c’est simplement une petite attention à offrir au visiteur, nouveau ou ancien, pour lui faire savoir ce qui se passe.

Si cette approche suggérée par Claude est quelque chose qui ne prend que quelques minutes, mais qui n’est plus modifiée par la suite, je crois que c’est un temps bien investi.

Cela ne prend que quelques secondes si vous passez à l’installation avec deux conteneurs.

Vous pouvez même planifier la bascule pendant la nuit si vous le souhaitez vraiment, pendant que vous et/ou votre public principal dormez.

Vous n’avez pas besoin d’une page de maintenance.

J’utilise la construction à conteneurs doubles derrière le CDN Cloudflare. Le conteneur double minimise les temps d’arrêt et mes deux forums de production principaux ont un maximum de 30 secondes hors ligne pendant les reconstructions. J’aime avoir une page de maintenance car la page par défaut du serveur web est moche et ne donne aucun indice sur la durée de l’arrêt.

Par exemple, pour un site à your-domain.com, j’utilise simplement une route de travailleur Cloudflare définie sur *yourdomain.com/* (incluez les astérisques).

Étape 1 : Créer la page de travailleur

Vous pouvez configurer la page de maintenance sur la page des paramètres workers & pages de Cloudflare - cliquez sur le bouton create application :

puis utilisez le modèle hello world :

puis donnez-lui un nom et cliquez sur deploy :

puis allez sur la page d’aperçu de cette page de maintenance et cliquez sur edit code :

et collez ce code dans la fenêtre de code worker.js (substituez votre domaine et quel que soit le message que vous voulez, modifiez le texte, la couleur du texte, l’arrière-plan, etc.) :

export default {
  async fetch(request, env, ctx) {
    try {
      // Fetch the original request from your server
      const response = await fetch(request);

      // If YOUR SITE is swapping containers, it throws a 502, 521, or 530
      if (response.status === 502 || response.status === 521 || response.status === 530) {
        return returnCustomErrorPage();
      }

      // If everything is fine, return the normal forum traffic
      return response;
    } catch (e) {
      // If the server is completely unreachable
      return returnCustomErrorPage();
    }
  }
};

function returnCustomErrorPage() {
  const html = `
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>System Refresh - YOUR-SITE.com</title>
      <style>
        body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; text-align: center; padding: 50px; color: #333; background-color: #f9f9f9; }
        h1 { font-size: 2.5em; margin-bottom: 0.5em; color: #9400D3; }
        p { font-size: 1.2em; line-height: 1.5; }
        .container { max-width: 600px; margin: 0 auto; background: white; padding: 40px; border-radius: 8px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); }
      </style>
    </head>
    <body>
      <div class="container">
        <h1>Just a quick update window</h1>
        <p><strong>YOUR-SITE</strong> is undergoing a brief 30-second system refresh.</p>
        <p>Grab a quick sip of your drink—this page will automatically refresh as soon as we are back online.</p>
      </div>
      <script>
        // Automatically check if the site is back up every 10 seconds
        setInterval(function() {
          window.location.reload();
        }, 10000);
      </script>
    </body>
    </html>
  `;

  return new Response(html, {
    status: 503, // 503 is best for SEO so Google doesn't penalize you for downtime
    headers: {
      "Content-Type": "text/html;charset=UTF-8",
      "Retry-After": "30"
    }
  });
}

ça devrait ressembler à quelque chose comme ça. Cliquez sur deploy pour le sauvegarder :

Étape 2 : Ajouter la route

allez maintenant sur la page de route de travailleur Cloudflare et cliquez sur le bouton add route pour faire apparaître la nouvelle fenêtre modale de route, remplissez la route de domaine et sélectionnez la page de travailleur que vous venez de créer, puis cliquez sur save :

Étape 3 : Exécuter des mises à jour ou des reconstructions

maintenant, lorsque vous vous connectez en ssh à votre serveur et exécutez vos mises à jour système ou effectuez une reconstruction, au lieu d’un site web hors ligne ou d’une page d’erreur, cette page s’affichera lorsque le temps d’arrêt se produira réellement (j’utilise 30 secondes dans le mien car c’est le maximum pour mes sites)

j’ai tendance à ajouter et supprimer ma page de route de travailleur chaque fois que je fais de la maintenance, mais certains aiment simplement la laisser en place tout le temps (je laisse le code réel de la page, j’ajoute et supprime juste la route).

je pense que c’est tout.

Optionnel : Exécuter des mises à jour avec un script et planifier

j’utilise également un script shell unix sur mon serveur pour exécuter la mise à jour spécifique au conteneur double, que j’ai créée comme ceci :

cat << 'EOF' > /root/update-web.sh
#!/bin/bash
cd /var/discourse
echo "➡️ Pulling latest Discourse docker scripts..."
git pull

echo "➡️ Bootstrapping new web container in the background (takes ~8 mins)..."
./launcher bootstrap web_only

if [ $? -eq 0 ]; then
    echo "✅ Bootstrap successful! Swapping containers..."
    ./launcher destroy web_only && ./launcher start web_only
    echo "🚀 Done! Site updated with almost zero downtime."
else
    echo "❌ Bootstrap failed! Aborting swap to keep current site online."
fi
EOF

chmod +x /root/update-web.sh

puis je l’exécute à l’invite root sur mon serveur avec la commande ./update-web.sh.


:warning: edit: ne faites pas les étapes ci-dessous à moins que vous n’ayez un compte cloudflare payant.

vous pouvez l’automatiser à une heure spécifique, comme 1h du matin dimanche matin votre heure locale avec une tâche cron. Pour moi 1:00am local est 8:00 AM UTC, (vous pouvez déterminer la date UTC de votre serveur en tapant date à l’invite de commande lorsque vous êtes connecté en ssh).

donc :

exécutez cette commande pour ouvrir le planificateur de tâches de votre serveur :

crontab -e

(s’il vous demande de choisir un éditeur, sélectionnez votre éditeur préféré, 1 pour nano est probablement le plus simple)

je fais défiler jusqu’au bas des commentaires et colle ceci :

0 8 * * 0 /root/update-web.sh >> /var/log/discourse-update.log 2>&1

(ce qui signifie exécuter le fichier /root/update-web.sh à la minute 0, heure 8 (UTC), chaque dimanche matin)

puis sauvegardez et quittez (si vous utilisez nano) :

  1. Ctrl + O pour sauvegarder.
  2. Enter pour confirmer.
  3. Ctrl + X pour quitter.

puis je peux exécuter cat /var/log/discourse-update.log lorsque je me réveille les dimanches matins pour vérifier s’il s’est exécuté correctement. Si vous utilisez un planificateur de tâches cron, vous voudrez laisser la route de la page de travailleur.

je pense que c’est tout. Faites-moi savoir si vous avez des questions lol. C’est beaucoup plus simple que ce à quoi je l’ai fait ressembler lol. :grin:

Waouh ! Je vous remercie vraiment pour ce tutoriel détaillé ! :raising_hands:
Je viens de sauvegarder votre réponse dans mes notes, afin de la consulter lorsque je réinstallerai Discourse prochainement. Je vous tiendrai absolument au courant du résultat, même si cela prend un certain temps avant que je ne procède à l’installation. Je suis en train de terminer quelques tâches de codage en ce moment, et ce n’est qu’après que je me concentrerai à nouveau sur Discourse.

Je suis également d’accord pour dire que la page « le serveur web est hors service » est moche, et que ce message n’a pas beaucoup de sens pour les utilisateurs inexpérimentés. Avoir une simple page de maintenance avec un message personnalisé est toujours préférable, même si cela demande un peu de travail pour la mettre en place.

Encore merci énormément d’avoir pris le temps de partager cela. J’espère que d’autres personnes en tireront profit :slight_smile:

Et c’est là que ça se complique.

Ce n’est pas un changement simple, et cela implique une infrastructure supplémentaire à gérer et à maintenir. À mon avis, cela ne vaut vraiment pas la peine pour quelques secondes d’interruption de service.

Je comprends ce que tu veux dire, mais c’est juste un cas de figure. Et si quelque chose se cassait vraiment et que je devais le réparer, en prenant plus que quelques secondes ?

Ensuite, comment est-ce que tu as quelques secondes d’indisponibilité alors que moi, après l’installation de plugins, j’en avais comme 20 minutes ?

Parce que dans la configuration à deux conteneurs (liée dans nos deux messages et dépendance non négociable), vous initialisez la nouvelle version en utilisant ./launcher bootstrap web_only (pendant laquelle votre site reste 100 % fonctionnel), puis vous détruisez simplement et démarrez immédiatement le nouveau conteneur déjà construit (ce qui prend quelques secondes).

Ah, d’accord, ça concerne toujours la configuration à 2 conteneurs. Je pensais que tu disais que reconstruire entièrement cette configuration à 2 conteneurs ne valait pas la peine. Ce que tu dis, c’est que le travail supplémentaire pour la page de maintenance ne vaut pas la peine, c’est bien ça ?

Personnellement, oui.

Si vous souhaitez une double sécurité pour les visiteurs web qui accèdent au site pendant la période de navigation fraîche de 30 à 60 secondes, alors une page de maintenance est justifiée.

Vous devez peser l’avantage de cette mesure contre le temps nécessaire pour maintenir l’infrastructure associée.

Maintenant, je comprends. Merci.

Donc, je me demande : avec 2 conteneurs, ces 20 minutes environ sont-elles toujours d’actualité ? La seule différence est que je peux laisser le traitement se faire sur l’un des conteneurs, celui qui n’est pas en production, et quand c’est prêt, faire le basculement. Est-ce ainsi que cela fonctionne ?

Oui, la construction du conteneur prend encore un certain temps. Pour information, assurez-vous que votre serveur est suffisamment puissant pour permettre à ce processus de s’exécuter en parallèle tout en servant votre communauté normalement (l’élément le plus important est de garantir une mémoire suffisante au départ – il est donc très important de vous assurer d’avoir assez d’espace SWAP). La construction monopolisera également au moins un cœur de processeur, donc envisagez d’utiliser un serveur avec 1 ou 2 cœurs supplémentaires.

Maintenant, tout a du sens. Merci.

Au départ, j’utilisais ceci, mais il semble que ce ne soit plus disponible :

Donc maintenant, je dois passer par celui-ci :

Ce qui représente une augmentation assez brutale du prix… :confused: et qui semble être un serveur moins performant… ?

Je recommande au moins 4 Go de RAM et 3 cœurs (« vcpu ») pour ce type de configuration…

Pour référence, car cela a été demandé dans un autre sujet, la (édit : fausse) réponse : Any cheaper alternatives to Hetzner? - #2 by Canapin

C’est le seul qui corresponde à cela… quelle différence de prix…

image

Je suppose que je vais devoir le construire avec un seul conteneur pour l’instant, et le mettre à niveau quand le moment (:money_bag:) sera venu.

Merci pour les informations, Robert !

Je ne suis pas d’accord et je pense que cela vaut la peine d’avoir une page de maintenance. D’après mon expérience, quand les gens voient la page d’erreur de Discourse, la première chose qu’ils font est d’appuyer sur Actualiser, puis ils voient la brève page de maintenance qui rechargera automatiquement le site une fois le changement de conteneur terminé. Ce n’est pas beaucoup de travail à mettre en place et vous n’avez à le faire qu’une seule fois. Pendant une reconstruction avec la configuration à double conteneur, la partie de démarrage se fait en arrière-plan et les utilisateurs peuvent continuer à utiliser le site ; c’est le changement de conteneur qui cause la brève interruption (contrairement à une configuration standard à conteneur unique).

Le coût du serveur et son choix sont une question complètement différente et devraient faire l’objet d’un autre sujet séparé.

Je suppose que je suis dans le juste milieu pour l’instant. Je comprends ce que vous voulez dire, tout comme @merefield. C’est une petite touche sympathique de l’avoir, et si cela ne se configure qu’une seule fois, ce n’est pas vraiment un problème, je suppose ?

En même temps, si cela prend effectivement jusqu’à 60 secondes dans le pire des cas, tout le monde ne visitera pas le site exactement à la marque 0 seconde et n’aura pas à attendre 60 secondes. Certaines personnes verront la page moche pendant 5 secondes, d’autres 20, d’autres encore 60. D’autres personnes ne la verront même pas (je crois ?), par exemple si elles sont en train de lire une réponse ou d’en écrire une, ce changement pourrait survenir avant qu’elles n’appuient sur ENVOYER ou avant qu’elles n’arrêtent de lire le sujet et n’appuient sur RÉPONDRE (ou ne visitent une autre page).

Mon problème avec la solution nginx était plus lié à l’affaire des 20 minutes sur une configuration à un seul conteneur. Avec l’option d’en avoir deux et de passer de 20 minutes à peut-être 60 secondes au maximum, je me demande si c’est vraiment pertinent ? Surtout si je peux ajouter un annonce en haut indiquant que les choses vont être indisponibles juste pour quelques secondes, à une heure de faible trafic, ce ne sera pas un gros problème ?

Je dois m’asseoir et y réfléchir. La configuration à deux conteneurs sera certainement quelque chose à mettre en œuvre, si je trouve une bonne offre de serveur, cependant.

Pourrais-tu clarifier cela, de manière à ce que quelqu’un de basique comme moi puisse comprendre ? Je ne connais pas encore bien les workers…

Pourquoi ajoutes-tu et supprimes-tu la page de route des workers, si les laisser en place ne pose pas de problème ? Donc, si j’ajoute la page de maintenance, puis-je vraiment la configurer une fois pour toutes, et à partir de ce moment-là, dois-je simplement faire un SSH sur mon serveur via le Terminal et tout faire là-bas comme je le fais avec un conteneur unique, sans jamais avoir à aller sur Cloudflare ?

@merefield a mentionné que cela (page de maintenance, workers, etc.) est aussi quelque chose qui nécessite de la maintenance, donc je me demande si c’est vraiment quelque chose que tu configures et oublies, ou s’il y a quelque chose à faire de temps en temps ?