Nginx HTTP/3 Proxyserver zeigt Inhalte vom falschen Virtual Host
Ende Mai 2025 habe ich den metalhead.club Song über eine Faircamp-Website unter music.metalhead.club veröffentlicht. Doch nicht alle Nutzer konnten meinen Links auf die Faircamp-Seite problemlos folgen. In wenigen Einzelfällen berichteten mir User von mindestens einem der folgenden Fehler:
- Wenn Nutzer die Albumseite aufriefen: Fehler 404 - not found
- Wenn Nutzer die Hauptseite aufriefen: Der watch.metalhead.club Globus wurde angezeigt
Nach einigen Versuchen konnte ich den Fehler sporadisch selbst reproduzieren. In den Access-Logs des Nginx-Proxys ist mir dabei aufgefallen, dass alle fehlerhaften Anfragen mit HTTP/3 gestellt wurden. Das war mein erster Anknüpfungspunkt.
Da der Anteil der HTTP/3-kompatiblen Webbrowser stetig steigt, hatte ich mich einige Monate zuvor nämlich entschlossen, auf dem Server eine sehr aktuelle Nginx-Version mit aktiviertem HTTP/3 zu nutzen. Doch: Für music.metalhead.club hatte ich HTTP/3 nicht aktiviert. Da stellt sich zuerst die Frage: …
Wieso wird eigentlich HTTP/3 genutzt?
Für meine Mastodon Instanz unter metalhead.club war als einzigen virtual Host auf dem Nginx-Server HTTP/3 aktiviert, um vor allem für weiter entfernte Benutzer den Verbindungsaufbau zu verringern und so Ladezeiten zu verkürzen. Für diesen virtual Host war auch der Alt-Svc Header passend auf “h3” (also HTTP/3) gesetzt. Doch diese Einstellung galt nicht für andere virtual Hosts wie z.B. music.metalhead.club - wieso also stellten verschiedene Webbrowser dennoch HTTP/3-basierte Anfragen an den nicht-HTTP/3 virtual Host music.metalhead.club ?
Um das Phänomen zu erklären, muss man wissen, dass HTTP/3 ebenso wie sein Vorgänger HTTP/2 eine Funktion namens “Connection Coalescing” kennt. Wenn …
- IP-Adresse
- Port und
- SSL-Zertifikat
gleich sind, werden “alte” QUIC-Verbindungen wiederverwendet (siehe auch RFC 7540 “Connection reuse” - auch “Connection Coalescing genannt”). Obwohl also womöglich später ein anderer virtual Host auf dem Server angesprochen wird, wird eine bestehende QUIC-Verbindung zum Server verwendet, falls die drei Parameter übereinstimmen. Ob dieser “andere” virtual Host überhaupt HTTP/3 unterstützt, wird dabei nicht mehr überprüft. In meinem Fall ist offenbar folgendes passiert:
- Ein Nutzer ruft zuerst metalhead.club auf. Der Browser erkennt den HTTP/3-Support über den “Alt-Svc” Header
- Weitere Verbindungen zu metalhead.club werden nun mit HTTP/3 gestellt - dazu wird eine QUIC Verbindung aufgebaut
- Der Benutzer surft nun weiter zu music.metalhead.club. Dieser andere virtual Host verwendet dieselbe IP / Port / SSL-Zertifikat-Kombination (weil Wildcard-Zertifikat). Der Browser erkennt dies und benutzt die bereits bestehende QUIC-Verbindung
So kommt es, dass der Webbrowser mit dem Webserver nun auch HTTP/3 spricht, wenn es um music.metalhead.club geht.
… doch music.metalhead.club spricht kein QUIC!
In der Nginx-Konfiguration für music.metalhead.club gibt es allerdings keinen QUIC Listener. Und hier kommt die zweite Stolperfalle ins Spiel: Für den Fall, dass über eine QUIC-Verbindung ein virtual Host angefordert wird, der gar keinen QUIC-Listener hat, sucht sich Nginx den “default” QUIC virtual Host heraus. Der “default” vHost ist jener, der explizit als solcher festgelegt wurde - bzw. jener, der (wie in meinem Fall) durch die Konfigurationsreihenfolge implizit von Nginx festgelegt wurde. In meinem Fall gab es nur einen vHost, der QUIC spricht: den vHost für metalhead.club.
Anfragen an music.metalhead.club wurden also letztendlich vom vHost für metalhead.club verarbeitet, weil es sonst keinen vHost mit eingeschaltetem QUIC gab.
Da dieser vHost nur ein Proxy ist, der auf eine weitere Nginx-Instanz (“Nginx app server”) zeigt, wird die Anfrage nochmal weitergeleitet. Die App-Server Nginx Instanz in meinem Setup bekommt nun schließlich die Anfrage bezüglich music.metalhead.club - kann damit aber nichts anfangen. Schließlich besitzt sie selbst keinen vHost, der für music.metalhead.club zuständig sein könnte (siehe Diagramm). Also wird wieder einmal ein “default” vHost ausgesucht. Und da watch.metalhead.club der erste vHost ist, der in der Konfiguration des zweiten Nginx vorkommt, wird die Anfrage an watch.metalhead.club weitergereicht und dort beantwortet: Der Nutzer sieht den Globus auf der Startseite… oder eine 404-Fehlerseite, weil die angeforderte Albumübersicht auf watch.metalhead.club natürlich nicht existiert.
Rot: Die fehlerhafte Route zwischen den beiden Nginx-Instanzen. Grün: Die Soll-Route. (1) Wegen des fehlenden QUIC-Listeners wird der metalhead.club vHost angesprochen. (2) Dieser wiederum zeigt nun auf den falschen Nginx App-Server.
Die Lösung
Rätsel gelöst! Doch wie lässt sich das Problem beheben? Das Kernproblem liegt darin, dass Webbrowser annehmen, dass QUIC Verbindungen auch für andere virtual Hosts wiederverwendet werden können, wenn IP-Adresse, Port und Zertifikat dieselben sind. Deshalb sollten wir die passenden Bedingungen dafür auf der Serverseite schaffen. Die Problemlösung liegt also darin, für alle virtual Hosts QUIC zu aktivieren, die sich eine IP-Adressen / Port / Zertifikats-Kombination teilen. In meinem Fall habe ich auf dem Proxy-Nginx QUIC also auch für music.metalhead.club (und watch.metalhead.club) aktiviert. Das Problem war damit gelöst.