Endloses Laden hinter Cloudflare

Wir haben unseren Server von einem VPS-Anbieter zu einem anderen umgezogen und die Instanz über launcher rebuild auf die neueste Version aktualisiert, von 3.5.0.beta3 auf 3.5.0.beta4.

Die Instanz lief immer einwandfrei hinter Cloudflare, aber jetzt führt der Versuch, darauf zuzugreifen, zu einer endlosen Ladeanimation mit 5 Punkten.

Ich habe einen Hosts-Dateieintrag auf meinem lokalen System, um Cloudflare zu umgehen, da mein ISP (Deutsche Telekom AG) schlechte Peering-Richtlinien hat, sodass der Zugriff über Cloudflare zeitweise sehr schlecht ist. Daher habe ich das Problem zunächst nicht bemerkt, da der Zugriff ohne Cloudflare problemlos funktioniert. Dann habe ich die Instanz aktualisiert und bin mir daher nicht sicher, ob der geänderte VPS oder das Discourse-Upgrade die relevante Änderung war. Ich habe über VPN und Mobilfunknetz sichergestellt, dass das Problem jetzt wirklich Cloudflare selbst ist und nicht das schlechte Peering meines ISPs, und auch andere Benutzer haben das gleiche Problem. Sowohl der alte als auch der neue VPS verfügen über IPv6, und das gesamte System ist exakt dasselbe, als Roh-Image-Datei übertragen.

Es gibt keine Fehlermeldungen, weder im Browser (Konsole), noch beim Proxy des Host-Systems, noch bei Nginx im Container, noch bei Rails oder anderswo. Die HTML-Dokumente und mehrere Skripte werden einwandfrei geladen, und wenn man sie mit denen vergleicht, die beim Umgehen von Cloudflare geladen werden, sieht man, dass alles (was ich überprüft habe) identisch ist. Auch die Antwort-Header sehen größtenteils gleich aus, abgesehen von einigen Cloudflare-spezifischen. Die letzten Dinge, die geladen werden, ist der Mini-Profiler:

Natürlich hat das Leeren des Browser-Caches, die Verwendung von privaten Fenstern usw. nichts geändert. Auch das Leeren/Deaktivieren des Cloudflare-Caches hilft nicht, sodass der Cache nicht das Problem ist. Ich habe den CF-Cache vorübergehend vollständig für das gesamte Forum deaktiviert.

Bemerkenswert ist, dass das Forum hinter einem Apache-Proxy auf dem Host unter einem Unterpfad läuft, gemäß dieser Anleitung: Serve Discourse from a subfolder (path prefix) instead of a subdomain
Zuvor haben wir nur einen ln -s . forum Symlink anstelle der Uploads/Backups Symlinks erstellt und die Anweisungen doppelt überarbeitet, was jahrelang gut funktionierte (und auch jetzt ohne Cloudflare), aber im Rahmen meiner Fehlersuche habe ich diese Anweisungen übernommen, um sicherzustellen, dass der interne Proxy alle Regeln wie beabsichtigt anwendet. Vertrauenswürdiger Header ist CF-Connecting-IP, obwohl ich auch cloudflare.template.yml aktiviert habe, auch wenn sich dadurch Dinge etwas verdoppeln. Und ich habe auch versucht, verschiedene Teile dieser Vorlagen und der obigen Anweisungen hin und her zu ändern, auch um zu prüfen, ob die Proxy-IP-Header einen Unterschied machen, da das Fehlen von CF-Connecting-IP eine Sache ist, wenn man Cloudflare umgeht.

An diesem Punkt bin ich völlig ratlos, habe keine einzige Spur, woher das Problem kommen könnte, keine einzige damit zusammenhängende Protokoll-/Ausgabe irgendwo. Über Cloudflare hängt Discourse einfach in der Ladeanimation ohne weitere Spuren fest.

Ich hoffe, jemand hat eine Idee, wie man das debuggen kann, oder ob es zwischen 3.5.0.beta3 und 3.5.0.beta4 eine Änderung gab, die damit zusammenhängen könnte. Ich schätze, eine Herabstufung ist problematisch?

Dies ist die Instanz: https://dietpi.com/forum/
EDIT: Ich habe Cloudflare vorerst deaktiviert. Aber es gibt einen CNAME, der immer noch über Cloudflare läuft, sodass diese beiden verglichen werden können: https://www.dietpi.com/forum/

Interessantes Problem.

Es ist einfach https://www.dietpi.com/forum/, das ewig hängt.

$ 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... verbunden.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [text/html]
Saving to: âindex.html.1â

    [<=>                            

Das Interessante ist, dass Aufrufe wie https://www.dietpi.com/forum/site.json erfolgreich sind.

https://www.dietpi.com/forum/t/why-there-are-two-kernals-in-my-raspberry-pi4/23355 funktioniert nicht und hängt ewig, aber https://www.dietpi.com/forum/t/why-there-are-two-kernals-in-my-raspberry-pi4/23355.json funktioniert.

1 „Gefällt mir“

Interessant in der Tat. Ich stelle gerade fest, dass die HTML-Dokumente nicht vollständig geladen werden, sondern an einem bestimmten Punkt hängen bleiben. Ich habe /forum/ in beiden Fällen verglichen und dachte, sie wären identisch, aber wahrscheinlich habe ich mich zu sehr auf den Kopf konzentriert, während Teile des Bodens unten fehlen.

Diese letzte Zeile beim Laden über 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

Musste es abschneiden, da es das Zeichenlimit für einen Beitrag bei weitem überschreitet. Das Dokument geht normalerweise so weiter:

      <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...

Die anderen Seiten hängen an genau derselben Stelle. Ich glaube, wir sind hier etwas auf der Spur.

EDIT: Ah, warte, ich habe falsch geprüft, andere Seiten hängen woanders. Es ist also nicht dieses spezielle HTML-Element/Attribut.

Ja, jede Seite/jedes HTML-Dokument hängt beim Laden über den Browser immer wieder an demselben Zeichen, auch im privaten Fenster usw. Aber eine andere Seite hängt an einem anderen Punkt. Und wenn man diese über curl lädt, hängen sie ebenfalls immer am selben Punkt, aber an einem anderen, und wget hängt wieder immer an einem selben Punkt, aber an einem leicht anderen. Sehr seltsam.

Haben Sie eine Optimierung aktiviert?

Nein, keine (Inhalts-)Optimierungen. Ich hatte zwar die Funktion “103 Early Hints” aktiviert, sie aber bereits deaktiviert, um zu versuchen, die Probleme zu lösen. Dasselbe habe ich mit den Protokolleinstellungen versucht, aber das hat auch nichts geändert:

Übrigens gibt es keinen content-length-Antwortheader, könnte das ein Problem verursachen? Ich meine, er existiert auch nicht, wenn Cloudflare umgangen wird, aber wahrscheinlich hat Cloudflare dann ein Problem? EDIT: Nein, das scheint für dynamische Seiten normal zu sein, genauso wie bei unseren Wordpress- und Matomo-Seiten, die jedoch keine Probleme verursachen.

Und noch eine Entdeckung beim Spielen mit curl. Die Ausgabe nach STDOUT führt dazu, dass das vollständige HTML-Dokument angezeigt wird, aber es hängt immer noch und am Ende:

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


  </body>

</html>

Aber wenn ich versuche, es über -o oder eine einfache Umleitung zu speichern, oder sogar nur in grep zu leiten, hängt es an einer anderen Stelle:

            <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>

Und ich kann diese exakt gleichen 73728 Bytes zu 100 % reproduzieren, wenn ich https://www.dietpi.com/forum/ mit curl aufrufe, ohne es sofort auf der Konsole auszugeben. Das ist so seltsam :face_with_monocle:.


Also:

  • Alle Clients hängen beim Laden eines beliebigen HTML-Dokuments von unserer Discourse-Instanz.
  • Jeder Client hängt beim Laden derselben Seite an demselben Byte.
  • Verschiedene Clients hängen an unterschiedlichen Punkten, aber an demselben Byte, wenn sie mit demselben Client wiederholt werden.
  • Jede Seite hängt an einem anderen Punkt im Dokument und bei unterschiedlicher heruntergeladener Größe.
  • Das gleiche Tool curl hängt an unterschiedlichen Punkten, wenn es nur auf STDOUT ausgibt, im Vergleich zur Weiterleitung oder Speicherung des Dokuments.
  • wget kann das vollständige Dokument (zumindest https://www.dietpi.com/forum/) in eine Datei herunterladen, hängt aber immer noch am Ende, genauso wie wenn curl https://www.dietpi.com/forum/ das vollständige Dokument auf der Konsole ausgibt, aber am Ende hängt.

Ich glaube, das könnte ein Pufferungsproblem sein. Aber bei der Untersuchung ist mir etwas anderes aufgefallen.

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

Endet mit

  </body>
</html>

Aber die Verbindung wird nie geschlossen.

Theorie: Es gibt irgendwo ein Konfigurationsproblem, bei dem es eine Diskrepanz bei den HTTP-Versionen oder Headern (wie Keep-Alive-Verbindung) gibt, und dies wird erst zu einem Problem, wenn ein Dokument größer als X ist (ich vermute 64 KB).

Ja, wget lädt immer das gesamte Dokument herunter, und curl tut dies, wenn es direkt auf die Konsole ausgibt, aber die Verbindung wird nicht geschlossen. Dasselbe passiert mit viel kleineren Dokumenten, wie ich ein 14k-Dokument zu einem Thema mit nur 2 Beiträgen getestet habe. Aber selbst die kleineren werden normalerweise nicht vollständig von curl heruntergeladen, wenn sie per Pipe oder in eine Datei umgeleitet werden, weder im Browser noch.

Beide Tools zeigen immer HTTP/2 an, und in Cloudflare habe ich HTTP/2-Origin-Anfragen aktiviert. Aber es lohnt sich, explizit andere HTTP-Versionen zu testen. Gestern habe ich alle Protokolleinstellungen in Cloudflare deaktiviert, die auf dem obigen Screenshot zu sehen sind, und es hat nicht geholfen. Aber ich werde es noch einmal versuchen. Außerdem kann ich Zugriffsprotokolle auf dem Server aktivieren, um die tatsächlich eingehenden Anfragen von Cloudflare zu sehen.

Ich habe alle Kombinationen von unterstützten HTTP- (1.1-3) und TLS-Versionen (1.2-1.3) ausprobiert, aber das macht keinen Unterschied. Ich habe auch die HTTP3-Unterstützung deaktiviert, HTTP2-Origin-Anfragen diese 0-RTT-Verbindungsfortsetzung wieder. Kein Unterschied, curl hängt weiterhin bei exakt denselben 73.728 Bytes von https://www.dietpi.com/forum/.

Bezüglich der Theorie zu großen Dokumentengrößen hat https://www.dietpi.com/dietpi-software.html 199.475 Bytes und lädt perfekt. Ich sollte erwähnen, dass der Server (derselbe Webserver) eine statische Website, eine MkDocs-Instanz, Wordpress, Matomo hostet, die alle perfekt funktionieren. Außerdem gibt es eine Grafana-Instanz, bei der der vordere Webserver über einen UNIX-Socket als Proxy fungiert.

Aber ich stimme zu, dass es mit Puffern oder Chunk-Größen oder so etwas zusammenzuhängen scheint. Es ist einfach seltsam, dass die heruntergeladene Größe bis zum Hängen zwischen Clients und Seiten so stark variiert, während sie sich trotz Änderung der Protokollversionen exakt gleich bleibt und die Verbindung nicht einmal geschlossen wird, wenn das Dokument vollständig heruntergeladen wurde. Als ob das Stoppsignal fehlt, obwohl mir an dieser Stelle die Einblicke in HTTP fehlen. Daher dachte ich an den content-length-Header, aber dieser ist offensichtlich nicht zwingend erforderlich.

Der Webserver fungiert auch als Proxy für den Discourse-Container über einen UNIX-Socket. Ich könnte den TCP-Listener aktivieren, um die Discourse-Instanz zusätzlich ohne den Proxy verfügbar zu machen (natürlich unter Beibehaltung des Nginx im Container).

Könnten Sie KeepAlive Off in Apache ausprobieren?

Ich schätze, das würde zumindest den Webserver möglicherweise ausschließen, das wäre also einen Versuch wert.

1 „Gefällt mir“

Keine Änderung. Auch aus den Apache-Dokumenten:

Außerdem kann eine Keep-Alive-Verbindung mit einem HTTP/1.0-Client nur verwendet werden, wenn die Länge des Inhalts im Voraus bekannt ist.

Daher ist es angesichts des fehlenden content-length wahrscheinlich sinnvoll, dass es für diese Anfrage sowieso nicht verwendet wird.

Da dies einen Neubau erfordert, werde ich dies etwas später tun, wenn unsere übliche Website-Aktivität minimal ist. Ähm, ich denke gerade über HTTPS nach … es sieht so aus, als müsste ich einige benutzerdefinierte Anpassungen an der internen Nginx-Konfiguration vornehmen, um den UNIX-Socket sowie reine HTTP-Verbindungen funktionsfähig zu halten, während ich auf einem zusätzlichen Port für HTTPS mit den TLS-Zertifikaten vom Host lausche, aber ohne HTTPS-Weiterleitung/Erzwingung. … und ein zusätzlicher reiner HTTP-TCP-Port wäre auch interessant, für Clients, die HSTS ignorieren können.

Verwenden Sie zufällig RocketLoader in Cloudflare? Ich weiß, dass es bei einigen anderen Skripten Probleme verursacht.
Haben Sie auch den CF-Cache geleert?
Verwenden Sie eingehende Regeln auf CF, die möglicherweise mit Ihrer alten VPS-IP-Adresse verknüpft waren und nicht auf die neue aktualisiert wurden?

1 „Gefällt mir“

Kein RocketLoader: Beachten Sie, dass bei den obigen Tests mit curl und wget, die keine Syntax interpretieren und daher keine JavaScript-, Stil- oder sonstige Elemente laden, das Problem darin besteht, dass der Download des rohen HTML-Dokuments immer hängt.

Cloudflare-Cache ist für das Forum nicht aktiv, die rohen HTML-Dokumente wurden ohnehin nie gecached.

Keine VPS-spezifischen Regeln. Generell keine Regeln für das Forum, außer um den Cache zu umgehen. Das Problem tritt in beiden Fällen auf, daher ist der Cache auch nicht das Problem.

1 „Gefällt mir“

Bei Tests zur Umgehung des Apache2-Proxys auf dem Discourse-Container-Host und zum Deaktivieren erzwungener HTTPS-Weiterleitungen bei Cloudflare, um auch reine HTTP-Verbindungen über curl zu testen, habe ich schließlich den Schuldigen bei Cloudflare gefunden:

Ich bin mir nicht sicher, was sich mit unserem VPS-Wechsel und/oder dem Upgrade von Discourse 3.5.0.beta3 auf 3.5.0.beta4 und/oder gleichzeitig bei Discourse geändert hat, aber es scheint, dass etwas in den HTML-, CSS- oder JavaScript-Dokumenten von Discourse Cloudflares HTTPS-Umschreibung von eingebetteten URLs zum Absturz bringt. Es sieht so aus, als ob die partiellen und hängenden curl-Anfragen nicht wirklich damit zusammenhängen, oder vielleicht doch. Seltsam, dass man im Netzwerk-Tab des Browsers den partiellen Inhalt des HTML-Dokuments sehen kann, als ob die HTTPS-Umschreibungsfunktion dies beim Streamen durch das Dokument tut.

Hat vielleicht jemand anderes eine Instanz und ein Cloudflare-Konto, um dies zu testen, ob es sich um ein allgemeines Problem handelt oder ob es mit unserer speziellen Instanz/Konfiguration zusammenhängt?

Übrigens, um die Umgehung des Proxys sowie HTTP zu testen, während die Verbindung über den Proxy aktiv bleibt, funktioniert die manuelle Anpassung der Nginx-Konfiguration innerhalb des Containers wie folgt perfekt:

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;

Wichtig ist natürlich, HTTPS-Weiterleitungen und den HSTS-Header zu entfernen und die hinzugefügten Ports freizugeben.

Und ein weiterer Fund: Wir verwenden mod_sed, um unseren Matomo-Tracker-Code zu allen text/html-Antworten hinzuzufügen, direkt vor dem schließenden \u003c/head\u003e-Tag. Das Deaktivieren für Discourse (oder das Umgehen des Apache2-Proxys) löst die Probleme ebenfalls, obwohl Cloudflare Automatic HTTPS Rewrites aktiv ist. Das Deaktivieren von einem von beiden löst die Probleme. Auf allen anderen Seiten funktioniert die Kombination gut, auch auf sehr großen Seiten, die wir haben und die größer sind als die fehlschlagenden Forenseiten. Möglicherweise verursachen die beiden Filter, zuerst mod_set auf unserem Proxy und dann die eingebetteten URL-Rewrites von Cloudflare, dass etwas kaputt geht, das mit Dokument- oder Chunk-Größen oder Ähnlichem zusammenhängt.

Wir binden den Tracker jetzt über die Discourse-Theme-Bearbeitung ein und ich habe zusätzlich die Cloudflare Automatic HTTPS Rewrites deaktiviert. Es gibt keine gemischten Inhalte auf unserer gesamten Website. Und wenn es welche gibt, ist es gut, sie zu sehen und zu beheben, anstatt dass Cloudflare sie für immer maskiert.

[quote=“MichaIng, post:14, topic:364596”]um reine HTTP-Verbindungen auch über curl zu testen,
[/quote]

Ich bin mir ziemlich sicher, dass das nicht funktionieren kann.

Ich bin mir nicht ganz sicher, welches Problem Sie lösen möchten, aber Sie müssen wahrscheinlich force_https in Ihrer app.yml aktivieren.

4 „Gefällt mir“

Ich stelle mir vor, allein schon vom Namen „Cloudflare Automatic HTTPS Rewrites“ kann man das missverstehen. Cloudflare hat 2 Funktionen:

  • „Always Use HTTPS“ leitet alle einfachen HTTP-Anfragen an HTTPS weiter, ähnlich wie force_https in Discourse. Beide waren zuvor aktiviert, und ich habe beide deaktiviert, um zu testen, ob HTTPS etwas mit dem Problem oder den endlosen Ladezeiten von Discourse-Seiten und hängenden curl-Anfragen zu tun hat. Dies funktionierte einwandfrei und löste sogar das gesamte Problem für HTTPS-Anfragen, aber nur, weil ich „Cloudflare Automatic HTTPS Rewrites“ im selben Zug deaktiviert habe.
  • „Cloudflare Automatic HTTPS Rewrites“ ändert HTML-, CSS- und JavaScript-Dokumente, um alle eingebetteten einfachen HTTP-URLs durch HTTPS-Varianten zu ersetzen, bei denen Cloudflare glaubt, dass der Host über HTTPS erreichbar ist (basierend auf der HSTS-Preload-Liste und ähnlichem). Dies soll Warnungen vor gemischtem Inhalt vermeiden.

Das Erzwingen oder Nicht-Erzwingen von HTTPS bei Cloudflare, beim Host-Proxy oder bei Discourse spielt keine Rolle. Was das Problem verursachte, war die Kombination des mod_sed-Filters beim Host-Proxy und die von Cloudflare bearbeiteten eingebetteten einfachen HTTP-Bearbeitungen. Also zwei Stufen, auf denen der Inhalt der Dokumente durch einen Filter geleitet wurde. Das Problem war nicht, dass es eine tatsächliche Inhaltsänderung gab (es gibt keinen gemischten Inhalt auf unserer Website, „Cloudflare Automatic HTTPS Rewrites“ ändert daher nicht tatsächlich den Dokumentenkörper), sondern wahrscheinlich im Zusammenhang mit Chunks, Puffern oder dem Timing.

1 „Gefällt mir“

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