HTTP-Fehler im Varnish verarbeiten

Fehler passieren – Und damit diese Fehler den Webseiten-Besuch nicht unnötig unterbrechen, sollten diese soweit wie möglich in der Gestaltung der jeweiligen Webseite einherkommen. Hier eine Lösung um Fehler "ab Varnish" abzufangen.

15.04.2024 - Manuel Wohlers

Achtung, da ich fast sicher bin, dass Du vor Deinem Varnish noch einen Reverse-Proxy für die SSL-Termination verwendest, ist die Anleitung für den NGINX wahrscheinlich besser geeignet!

Ausgangssituation & Anforderung

Für eine Webseite mit vorgeschaltetem Varnish-Cache wurde in einigen Fällen eine weiße Seite mit einer läppischen Fehlermeldung der Art "Error 500 – Ein Fehler ist aufgetreten" oder "Error 401 – Zugriff verweigert!" ausgegeben. Bei Verbindungsproblemen zwischen Varnish und Backend-Webserver erschien die berühmte "Guru Meditation" Meldung. 

Zwar wurde bereits die Guru-Meditation-Meldung mit Hilfe der Subroutine vcl_backend_error durch eine eigene Seite ersetzt. Wirklich zufriedenstellend war das noch nicht. Denn die Kunden-Anforderung war sinngemäß: "Ich will da keine weiße Seite mit ungestylter Fehlermeldung mehr sehen! Damminomol!"

Varnish-Konfiguration

Die Fehler können in der Subroutine `vcl_deliver` abgefangen werden und die Fehlerseite wird durch die Subroutine `vcl_synth` ausgeliefert.

sub vcl_synth {
  set resp.http.Content-Type = "text/html; charset=utf-8";
  set resp.body = regsuball(std.fileread("/etc/varnish/error.html"), "<REASON/>", resp.reason);
  return (deliver);
}

sub vcl_deliver {
    # handle 5xx error codes
    if (resp.status >= 500 && resp.status < 600) {
        return(synth(resp.status, "Grund: Err " + resp.status));
    }
    # 403 Forbidden (e.g. real directories without listing access)
    if (resp.status == 401 || resp.status == 403) {
        return(synth(resp.status, "Grund: Sie haben keinen Zugriff (Err " + resp.status + ")"));
    }
}

Die HTML-Datei muss klein bleiben und sämtliche benötigte Ressourcen enthalten. Großformatige Hintergrundbilder, die mehrere Megabytes schwer sind, funktionieren nicht, ebenso sind gerätespezifische und "lazy loading" Bilder sinnlos, wenn diese ohnehin alle in die HTML-Datei eingebettet werden müssen.

<!DOCTYPE html>
<html lang="de">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Fehler</title>

</head>
<body>
    <main>
       <div class="sorry">
          <div class="sorry__header">
             Webseite nicht verfügbar
          </div>
          <div class="sorry__body">
             <div class="sorry__main">
                <p>
                   Diese Seite ist temporär nicht erreichbar. Bitte versuchen Sie es später nochmals.
                </p>
                <p>
                     <REASON/>
                </p>
             </div>
             <div class="sorry__info">
                Wir stehen Ihnen gerne zur Verfügung:
                <br>
                    Kontaktzeiten …<br>
                <a href="tel:000">Telefonnummer</a> <br>
                <br>
                <a href="javascript:linkTo_UnCryptMailto('demoZdomain8tld', -26)">demo@domain.tld</a>
             </div>
          </div>
       </div>
    </main>
</body>
</html>

 

Einzige Besonderheit an dieser HTML-Seite ist der Platzhalter, der frei definiert werden kann. Ich habe hier `<REASON/>` gewählt, weil dies in der HTML-Syntax erlaubt ist und das Template auch ohne die Entfernung des Platzhalters funktioniert.

Fazit

Mit wenigen Handgriffen lassen sich auf diesem Weg sämtliche Backend-Fehler aufgrund des HTTP-Response-Codes einheitlich ausgeben. Es werden keine "Oops, an error occured" Meldungen mehr erscheinen und auch keine gänzlich ungestylten Fehlermeldungen der unterschiedlichen Systeme – sofern die entsprechenden Fehlercodes in der VCL-Datei berücksichtigt wurden.

Ein Fall ist in diesem konkreten Setup nicht abgedeckt: Wenn der NGINX-Service, der für die SSL-Terminierung zuständig ist, keine Verbindung zum Varnish-Backend erhält. Warum also die Fehlermeldungen nicht gleich im NGINX lösen.