Chargement sans fin derrière Cloudflare

Nous avons déplacé notre serveur d’un fournisseur de VPS à un autre et j’ai mis à niveau l’instance via launcher rebuild vers la dernière version également, de 3.5.0.beta3 à 3.5.0.beta4.

L’instance fonctionnait toujours bien derrière Cloudflare, mais maintenant, essayer d’y accéder entraîne une animation de chargement infinie avec 5 points.

J’ai une entrée dans mon fichier hosts sur mon système local pour contourner Cloudflare, car mon FAI (Deutsche Telekom AG) a des politiques de peering merdiques, de sorte que l’accès via Cloudflare est parfois très lent. Donc, au début, je n’ai pas remarqué le problème, car l’accès sans Cloudflare fonctionne bien. J’ai donc mis à niveau l’instance, et par conséquent, je ne suis plus sûr si le VPS modifié ou la mise à niveau de Discourse était le changement pertinent. J’ai assuré via VPN et réseau mobile que le problème est bien Cloudflare lui-même maintenant, pas le mauvais peering de mon FAI, et d’autres utilisateurs rencontrent également le même problème. L’ancien et le nouveau VPS ont l’IPv6 disponible, et tout le système est exactement le même, transféré sous forme de fichier image brut.

Il n’y a aucun message d’erreur, ni dans le navigateur (console), ni par le proxy du système hôte, ni par Nginx dans le conteneur, ni par Rails ou ailleurs. Les documents HTML et plusieurs scripts se chargent correctement, et en les comparant avec ceux servis lors du contournement de Cloudflare, tout (ce que j’ai vérifié) est identique. Les en-têtes de réponse semblent également en grande partie identiques, à part quelques-uns spécifiques à Cloudflare, bien sûr. Les dernières choses que je vois se charger sont le mini profiler :

Bien sûr, vider le cache du navigateur, utiliser des fenêtres privées, etc., n’ont rien changé. Vider/désactiver le cache Cloudflare n’aide pas non plus, donc le cache n’est pas le problème. J’ai temporairement désactivé complètement le cache CF pour l’ensemble du forum.

Il est notable de dire que le forum fonctionne sur un sous-chemin derrière un proxy Apache sur l’hôte, en suivant ces instructions : Serve Discourse from a subfolder (path prefix) instead of a subdomain
Auparavant, nous avions simplement créé un lien symbolique ln -s . forum au lieu des liens symboliques uploads/backups et doublé les réécritures des instructions, ce qui a bien fonctionné pendant des années (et fonctionne aussi maintenant sans Cloudflare), mais dans le cadre de mes efforts de débogage, je suis passé à ces instructions pour m’assurer que le proxy interne applique toutes les règles comme prévu. L’en-tête de confiance est CF-Connecting-IP, bien que j’aie également activé cloudflare.template.yml, même si cela double un peu les choses. Et j’ai aussi essayé de revenir en arrière et d’aller de l’avant sur diverses parties de ces modèles et instructions ci-dessus, également dans le but de vérifier si les en-têtes IP du proxy font une différence, car l’absence de CF-Connecting-IP est une chose lors du contournement de Cloudflare.

À ce stade, je suis complètement à court d’idées, je n’ai aucune trace de l’origine du problème, aucun journal/sortie connexe nulle part. Via Cloudflare, Discourse reste bloqué dans l’animation de chargement sans aucune autre trace.

J’espère que quelqu’un aura une idée sur la façon de déboguer cela, ou s’il y a eu un changement entre 3.5.0.beta3 et 3.5.0.beta4 qui pourrait être lié. Je suppose qu’une rétrogradation est problématique ?

C’est l’instance : https://dietpi.com/forum/
EDIT : J’ai désactivé Cloudflare pour l’instant. Mais il y a un CNAME qui est toujours transmis via Cloudflare, donc ces deux-là peuvent être comparés : https://www.dietpi.com/forum/

Problème intéressant.

C’est simplement \u003chttps://www.dietpi.com/forum/\u003e qui reste bloqué indéfiniment.

$ wget https://www.dietpi.com/forum/
--2025-05-03 10:52:18--  https://www.dietpi.com/forum/
Resolving www.dietpi.com (www.dietpi.com)... 104.21.12.65, 172.67.193.183, 2606:4700:3035::6815:c41, ...
Connecting to www.dietpi.com (www.dietpi.com)|104.21.12.65|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [text/html]
Saving to: âindex.html.1â

    [<=>                                

La chose intéressante est que les appels comme \u003chttps://www.dietpi.com/forum/site.json\u003e réussissent.

\u003chttps://www.dietpi.com/forum/t/why-there-are-two-kernals-in-my-raspberry-pi4/23355\u003e ne fonctionne pas et reste bloqué indéfiniment, mais \u003chttps://www.dietpi.com/forum/t/why-there-are-two-kernals-in-my-raspberry-pi4/23355.json\u003e fonctionne.

1 « J'aime »

En effet, c’est intéressant. Je réalise maintenant que les documents HTML ne se chargent pas complètement, mais s’arrêtent à un certain point. J’ai comparé /forum/ dans les deux cas et j’ai pensé qu’ils étaient identiques, mais probablement je me concentrais trop sur l’en-tête, tandis que des parties du corps en bas manquent.

Cette dernière ligne lors du chargement via Cloudflare :

      <discourse-assets-json>
        <div class="hidden" id="data-preloaded" data-preloaded="{&quot;topic_list&quot;:&quot;{&quot;users&quot;...&quot;:false,&quot;allowed_iframes&quot;:&quot;https://dietpi.com/forum/discobot/certificate.svg&quot;

J’ai dû la tronquer car elle dépasse largement la limite de caractères pour un message. Le document continue généralement comme ceci :

      <discourse-assets-json>
        <div class="hidden" id="data-preloaded" data-preloaded="{&quot;topic_list&quot;:&quot;{&quot;users&quot;...&quot;:false,&quot;allowed_iframes&quot;:&quot;https://dietpi.com/forum/discobot/certificate.svg&quot;,&quot;can_permanently_delete...

Les autres pages s’arrêtent exactement au même endroit. Je pense que nous sommes sur la bonne voie.

EDIT : Ah attendez, j’ai mal vérifié, d’autres pages s’arrêtent ailleurs. Ce n’est donc pas cet élément/attribut HTML particulier.

Oui, chaque page/document HTML s’arrête au même caractère lorsqu’il est chargé via le navigateur encore et encore, en fenêtre privée, etc. Mais une page différente s’arrête à un point différent. Et lors du chargement de ceux-ci via curl, ils s’arrêtent également toujours au même point, mais un différent, et wget s’arrête à nouveau toujours à un point identique, mais légèrement différent. Très étrange.

Avez-vous activé une optimisation ?

Non, aucune optimisation du (contenu). J’avais bien activé la fonctionnalité 103 Early Hints, mais je l’ai déjà désactivée pour tenter de résoudre le problème. J’ai essayé la même chose avec les paramètres du protocole, mais cela n’a rien changé non plus :

Au fait, il n’y a pas d’en-tête de réponse content-length, cela pourrait-il causer un problème ? Je veux dire, il n’existe pas non plus lorsque l’on contourne Cloudflare, mais Cloudflare a alors peut-être un problème ? EDIT : Non, cela semble normal pour les pages dynamiques, de même que pour nos pages Wordpress et Matomo qui ne causent cependant aucun problème.

Et une autre découverte en jouant avec curl. L’affichage sur STDOUT affiche le document HTML complet, mais il se bloque toujours et à la fin :

  <p class='powered-by-link'>Powered by <a href="https://www.discourse.org">Discourse</a>, best viewed with JavaScript enabled</p>
</footer>


  </body>

</html>

Mais en essayant de l’enregistrer via -o ou une simple redirection, ou même en le redirigeant vers grep, il se bloque à un endroit différent :

            <div class="link-bottom-line">
                <a href='/forum/c/general-discussion/7' class='badge-wrapper bullet'>
                  <span class='badge-category-bg' style='background-color: #F7941D'></span>
                  <span class='badge-category clear-badge'>
                    <span class='category-name'>General Discussion</span>
                  </span>
                </a>

Et je peux à 100 % reproduire ces mêmes 73728 octets en accédant à https://www.dietpi.com/forum/ avec curl sans simplement l’afficher dans la console immédiatement. C’est tellement bizarre :face_with_monocle:.


Donc :

  • Tous les clients se bloquent lors du chargement de tout document HTML de notre instance Discourse.
  • Chaque client se bloque au même octet lors du chargement de la même page.
  • Différents clients se bloquent à différents points, mais au même octet lors de la répétition avec le même client.
  • Chaque page se bloque à un point différent du document et à une taille téléchargée différente.
  • Le même outil curl se bloque à différents points lorsqu’il affiche simplement sur STDOUT par rapport à la redirection ou au stockage du document quelque part.
  • wget est capable de télécharger le document complet (au moins https://www.dietpi.com/forum/) dans un fichier, mais se bloque toujours à la fin, de même lorsque curl https://www.dietpi.com/forum/ affiche le document complet dans la console, mais se bloque à la fin.

Je pense que cela pourrait être un problème de mise en mémoire tampon. Mais en enquêtant, j’ai remarqué autre chose.

wget -O - https://www.dietpi.com/forum/latest

Se termine par

  </body>
</html>

Mais la connexion n’est jamais fermée.

Théorie : il y a un problème de configuration quelque part où il y a une incompatibilité dans les versions HTTP ou les en-têtes (comme la connexion keep alive) et cela ne devient un problème que lorsque qu’un document est plus grand que X (je soupçonne 64 Ko).

Oui, wget télécharge toujours le document entier, et curl le fait lorsqu’il affiche directement sur la console, mais la connexion n’est pas fermée. Il en va de même pour des documents beaucoup plus petits, comme j’ai testé un document de 14 Ko sur un sujet avec seulement 2 publications. Mais même les plus petits ne sont généralement pas entièrement téléchargés par curl lors du piping ou du stockage dans un fichier, ni dans le navigateur.

Les deux outils affichent toujours HTTP/2, et dans Cloudflare, j’ai activé les requêtes d’origine HTTP/2. Mais cela vaut la peine de tester explicitement avec d’autres versions HTTP. Hier, j’ai désactivé tous les paramètres de protocole dans Cloudflare vus sur la capture d’écran ci-dessus, et cela n’a pas aidé. Mais je vais réessayer. Je peux également activer les journaux d’accès sur le serveur pour voir la requête entrante réelle de Cloudflare.

J’ai essayé toutes les combinaisons des versions HTTP (1.1-3) et TLS (1.2-1.3) prises en charge, mais cela ne fait aucune différence. J’ai également désactivé la prise en charge HTTP3, les requêtes d’origine HTTP2 reprennent cette reprise de connexion 0-RTT. Aucune différence, curl continue de se bloquer exactement sur les mêmes 73 728 octets de https://www.dietpi.com/forum/.

Concernant la théorie des tailles de documents trop importantes, https://www.dietpi.com/dietpi-software.html fait 199 475 octets et se charge parfaitement. Je dois mentionner que le serveur (même serveur web) héberge un site web statique, une instance MkDocs, Wordpress, Matomo, qui fonctionnent tous parfaitement. Il y a aussi une instance Grafana où le serveur web frontal agit comme proxy via un socket UNIX.

Mais je suis d’accord, cela semble lié aux tampons ou aux tailles de morceaux ou quelque chose comme ça. Il est juste étrange que la taille téléchargée jusqu’au blocage varie autant entre les clients et les pages, alors qu’elle reste exactement la même malgré le changement des versions de protocole, et que la connexion ne se ferme même pas lorsque le document a été entièrement téléchargé. Comme si le signal d’arrêt manquait, bien que je manque d’informations sur HTTP à ce stade. J’ai donc pensé à l’en-tête content-length, mais celui-ci n’est évidemment pas obligatoire.

Le serveur web agit également comme proxy pour le conteneur Discourse via un socket UNIX. Je pourrais activer l’écouteur TCP pour rendre l’instance Discourse également disponible sans le proxy (en laissant le Nginx dans le conteneur, bien sûr).

Pourriez-vous essayer KeepAlive Off dans Apache ?

Je suppose que cela permettrait au moins d’éliminer potentiellement le serveur web, donc cela vaudrait la peine d’essayer.

1 « J'aime »

Aucun changement. Également d’après la documentation Apache :

De plus, une connexion Keep-Alive avec un client HTTP/1.0 ne peut être utilisée que lorsque la longueur du contenu est connue à l’avance.

Par conséquent, étant donné l’absence de content-length, il est probable qu’elle ne soit de toute façon pas utilisée pour cette requête.

Comme cela nécessite une reconstruction, je le ferai un peu plus tard, lorsque l’activité de notre site web commun sera au minimum. Euh, je pense juste à HTTPS… il semble que je doive faire des ajustements personnalisés à la configuration Nginx interne pour maintenir le socket UNIX fonctionnel ainsi que les connexions HTTP simples, tout en écoutant sur un port supplémentaire pour HTTPS avec les certificats TLS de l’hôte, mais sans redirection/application HTTPS. … et un port TCP HTTP simple supplémentaire serait également intéressant, pour les clients qui peuvent ignorer HSTS.

Utilisez-vous par hasard RocketLoader dans CloudFlare ? Je sais qu’avec certains autres scripts, cela cause des problèmes.
Avez-vous également vidé le cache CF ?
Utilisez-vous des règles entrantes sur CF qui pourraient être liées à votre ancienne adresse IP VPS et qui n’ont pas été mises à jour vers la nouvelle ?

1 « J'aime »

Pas de RocketLoader : Notez que, d’après les tests ci-dessus avec curl et wget, qui n’interprètent aucune syntaxe, donc ne chargent aucun JavaScript, aucun style ou quoi que ce soit d’autre, le problème est que le téléchargement du document HTML brut est toujours bloqué.

Le cache Cloudflare n’est pas actif pour le forum, les documents HTML bruts n’ont jamais été mis en cache de toute façon.

Aucune règle spécifique au VPS. Généralement pas de règles pour le forum, à part pour contourner le cache. Le problème apparaît dans les deux cas, donc le cache n’est pas non plus le problème.

1 « J'aime »

En testant de contourner le proxy Apache2 sur l’hôte du conteneur Discourse, et en désactivant les redirections HTTPS forcées sur Cloudflare pour tester les connexions HTTP simples via curl, j’ai finalement trouvé le coupable chez Cloudflare :

Je ne suis pas sûr de ce qui a changé avec notre passage de VPS et/ou la mise à niveau de Discourse 3.5.0.beta3 vers 3.5.0.beta4 et/ou simultanément chez Discourse, mais il semble que quelque chose dans les documents HTML, CSS ou JavaScript de Discourse fasse échouer la réécriture HTTPS des URL intégrées par Cloudflare. Il semble que les requêtes curl partielles et en attente n’étaient pas vraiment liées, ou peut-être le sont-elles. Il est étrange que dans l’onglet réseau du navigateur, on puisse voir le contenu partiel du document HTML, comme si la fonctionnalité de réécriture HTTPS le faisait lors du streaming à travers le document.

Quelqu’un d’autre a-t-il une instance et un compte Cloudflare pour tester cela, qu’il s’agisse d’un problème général ou lié à notre instance/configuration particulière ?

Au fait, pour tester le contournement du proxy ainsi que le HTTP, tout en gardant la connexion via le proxy active, l’ajustement manuel de la configuration Nginx dans le conteneur comme suit fonctionne parfaitement :

root@dietpi-discourse:/var/www/discourse# cat /etc/nginx/conf.d/outlets/server/10-http.conf
listen unix:/shared/nginx.http.sock;
set_real_ip_from unix:;
listen 8080;
listen [::]:8080;
listen 8443 ssl;
listen [::]:8443 ssl;
http2 on;

ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;

ssl_certificate /shared/fullchain.cer;
ssl_certificate_key /shared/dietpi.com.key;

ssl_session_tickets off;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:1m;

Il est important de supprimer les redirections HTTPS et l’en-tête HSTS, bien sûr, et d’exposer les ports ajoutés.

Et une autre découverte : nous utilisons mod_sed pour ajouter notre code de suivi Matomo à toutes les réponses text/html, juste avant la balise de fermeture </head>. Le désactiver pour Discourse (ou contourner le proxy Apache2) résout également le problème, malgré l’activation des réécritures automatiques HTTPS de Cloudflare. Désactiver l’un ou l’autre résout le problème. Sur toutes les autres pages, la combinaison fonctionne bien, même sur de très grandes pages que nous avons, plus grandes que les pages du forum qui échouent. Donc, peut-être que les deux filtres, d’abord mod_set sur notre proxy, puis les réécritures d’URL intégrées par Cloudflare, provoquent une rupture, liée aux tailles de documents ou de blocs, ou autre chose.

Nous intégrons maintenant le tracker via l’édition du thème Discourse, et j’ai en plus désactivé les réécritures automatiques HTTPS de Cloudflare. Il n’y a pas de contenu mixte sur l’ensemble de notre site Web. Et s’il y en a, c’est bien de le voir et de le corriger, au lieu d’avoir Cloudflare qui le masque à jamais.

Je suis à peu près sûr que cela ne peut pas fonctionner.

Je ne suis pas tout à fait sûr du problème que vous essayez de résoudre, mais vous devez probablement activer force_https dans votre app.yml.

4 « J'aime »

J’imagine que rien que le nom « Cloudflare Automatic HTTPS Rewrites » peut être mal interprété. Cloudflare a 2 fonctionnalités :

  • « Always Use HTTPS » redirige toutes les requêtes HTTP simples vers HTTPS, tout comme force_https dans Discourse. Les deux étaient précédemment activés, et j’ai désactivé les deux pour tester si HTTPS avait quelque chose à voir avec le problème ou les pages Discourse en chargement infini et les requêtes curl qui se bloquent. Cela a parfaitement fonctionné, résolvant même tout le problème pour les requêtes HTTPS également, mais uniquement parce que j’ai désactivé « Cloudflare Automatic HTTPS Rewrites » dans le même temps.
  • « Cloudflare Automatic HTTPS Rewrites » modifie les documents HTML, CSS et JavaScript pour remplacer toutes les URL HTTP simples intégrées par des variantes HTTPS, là où Cloudflare pense que l’hôte est accessible via HTTPS (basé sur la liste de préchargement HSTS et autres). Ceci afin d’éviter les avertissements de contenu mixte.

Forcer ou ne pas forcer HTTPS chez Cloudflare, au niveau du proxy hôte ou chez Discourse n’a pas d’importance. Ce qui a causé le problème est la combinaison du filtre mod_sed au niveau du proxy hôte et des modifications HTTP simples intégrées par Cloudflare. Il y a donc deux étapes où le contenu des documents a été filtré. Le problème n’était pas qu’il y ait eu un changement de contenu réel (il n’y a pas de contenu mixte sur notre site, « Cloudflare Automatic HTTPS Rewrites » ne modifie donc pas réellement le corps du document), mais probablement lié aux chunks, au buffer ou au timing.

1 « J'aime »

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.