Eigene Fehlermeldungen und was man mit ihnen anfangen kann III

von Eric Horn.

1. Das Problem

Bereits im "Eigene Fehlermeldungen und was man mit ihnen anfangen kann I" hat Friedemann Lindenthal gezeigt, wie man sich von den standardisierten Fehler-Seiten trennen und damit der eigenen Homepage ein professionelles Auftreten verpassen kann. Im vorliegenden Artikel soll es nun darum gehen, wie man insbesondere mit den 404er-Fehlern ("Page Not Found") umgehen kann. Denn das von Friedemann vorgestellt Verfahren, Fehler durch Manipulation der Datei .htaccess auf eigene Seiten umzuleiten, bringt einen gravierenden Nachteil mit sich: Alle Fehler, die auf diese Weise durch Umleitung behandelt werden, werden auf dem Server nicht mehr als Fehler erkannt. Konkret auf den 404er-Fehler bezogen heißt das:

  1. Ein Link-Checker wie HTDIG oder Xenu findet keine fehlenden Seiten mehr, da ihm vom Server nicht der Status 404 übermittelt wird, sondern statt dessen eine ganz normale Seite, nämlich das 404er-Dokument.
  2. In den LOG-Dateien für den Server-Zugriff wird ebenfalls für fehlende Seiten kein 404er-Status eingetragen, sondern statt dessen der Status 302 ("Page Temporarily Moved"). Da die wenigsten LOG-Analyseprogramme jedoch eine Überprüfung des 302er-Status vornehmen, hat man keinen Zugriff auf diese Meldungen.

Das bedeutet also konkret: die Einrichtung einer Fehler-Umleitung für den 404er-Fehler per Datei .htaccess nimmt dem Webmaster erst einmal jede Möglichkeit, fehlende Links auf dem eigenen Server zu erkennen - es sei denn, er geht manuell die kompletten LOG-Dateien durch und sucht nach allen Aufrufen des Umleitungsdokumentes.

2. Die Lösung

Die Lösung dieses Problems ist denkbar einfach: Man lässt 404er-Fehler nicht auf statische Internetseiten weiterleiten, sondern auf dynamische, die mit Hilfe eines Scripts den Fehler analysieren und entsprechende Maßnahmen einleiten. Im "Eigene Fehlermeldungen und was man mit ihnen anfangen kann II" dieser Artikelserie erklärte Thomas, wie man dies mit Hilfe eines CGI-Scripts erreicht. An dieser Stelle soll eine Variante besprochen werden, die mit PHP realisiert wird. Warum verwende ich für dieses Vorhaben PHP? Zum einen muss ich gestehen, dass PHP bei mir meist Vorzug vor CGI-Scripten bekommt, da einige Stärken (etwa die direkte Integration in das HTML-Dokument, einfachere Programmstrukturen etc.) einfach für sich sprechen. Andererseits ist PHP vom Sicherheitsaspekt her für Administratoren wesentlich einfacher zu installieren und konfigurieren, da man mit wenigen Einstellungen die Möglichkeiten, die man dem Anwender gibt, festlegen kann. Auf diese Weise bieten immer mehr Hoster PHP-Unterstützung an, obwohl sie keine eigenen CGI-Scripte zulassen. Als Server-Administrator bevorzuge ich PHP auch deshalb, da die Evaluierung eines PHP-Programms weniger Server-Leistung kostet als das Ausführen eines CGI-Scripts; alles Gründe, weshalb in nächster Zeit PHP wohl früher oder später Perl den Rang ablaufen wird.

Was soll nun das Script können? Das im Folgenden hier vorgestellte Script ermittelt die Daten, die zum 404er-Fehler führten (fehlendes Dokument und aufrufendes Dokument). Handelt es sich um einen internen Fehler - wird also innerhalb der eigenen Seiten auf eine fehlende Datei gelinkt - wird eine entsprechende interne Fehlermeldung ausgegeben, handelt es sich um einen externen Fehler - linkt also z.B. eine fremde Seite auf ein Dokument des eigenen Servers, das nicht oder nicht mehr existiert - wird eine entsprechende externe Fehlermeldung ausgegeben. Nicht berücksichtigt werden Fehler, die aufgrund veralteter Bookmarks oder fehlerhafter URL-Direkt-Eingaben eines Benutzers entstehen - man kann sich ja schließlich nicht um alles kümmern. Anhand interner Fehlermeldungen kann man eigene Links korrigieren, anhand der externen Fehlermeldung kann man die fremde Seite aufrufen, um sich dann bei Bedarf die Adresse des Webmasters heraus zu suchen und ihm eine Mail mit Bitte um Abänderung des Links zukommen zu lassen. Alle Fehlermeldungen können entweder in einer eigenen LOG-Datei dokumentiert werden oder aber (so mache ich es) live an die Mail-Adresse des Webmasters versendet werden. Als besonderes Feature bietet das Script zusätzlich die Möglichkeit, eine Mapping-Datei anzulegen, in der man Dateiverschiebungen oder -umbenennungen dokumentiert. Heißt zum Beispiel die Impressum-Seite nicht mehr impressum.html, sondern info.html, dann kann man dies in der Mapping-Datei hinterlegen. Wird nun ein 404er-Fehler durch Aufruf einer auf diese Weise "gemappten" Datei erzeugt, leitet das Script automatisch auf die neue Seite weiter, ohne dass der Benutzer davon direkt was mitbekommt.

3. Die Realisierung

Die Realisierung ist denkbar einfach. Wir gehen einfach davon aus, dass wir schon im ersten Teil (Link) ein Dokument erstellt haben, dass standardmäßig als 404er-Dokument ausgegeben wird. Sinnvoller Weise sollte dieses Dokument nicht nur einen Fehlerhinweis enthalten, sondern irgend eine Hilfe für den Benutzer. Ich benutze hier meist Sitemaps oder zumindest eine Suchfunktion, so dass der Benutzer eine Chance hat, das gesuchte Dokument doch noch zu finden - vorausgesetzt natürlich, es ist noch vorhanden. Im Text der 404er-Seite sollte darauf hingewiesen werden, dass neben der Fehlerausgabe zugleich auch eine Benachrichtigung an den Webmaster erfolgt ist - das erhöht den positiven Eindruck des Benutzers im Falle eines Fehlers ungemein und macht zumindest schon einen Teil der Negativpunkte, die man sich durch einen Fehler auf den eigenen Seiten eingehandelt hat, wieder wett.

Das bisherige 404er-Dokument benennen wir nun einfach in 404.php3 um. Der Dateiname ist eigentlich egal, nur die Endung ist wichtig. Sie ist notwendig, damit der Server erkennt, dass das Dokument PHP-Anweisungen enthält und entsprechend vor der Übergabe an den Clienten durch den PHP-Interpreter ausgewertet (evaluiert) werden muss. Entsprechend muss auch in der Datei .htaccess der Eintrag für den 404er-Fehler verändert werden. An Stelle der bisherigen Anweisung muss dort nun stehen:

  ErrorDocument 404 /404.php3

Ich gehe dabei davon aus, dass sich die Fehlerseite im Hauptverzeichnis befindet. Wichtig ist hier die absolute Adressierung mit führendem Slash ("/"), damit das Fehler-Dokument auch gefunden wird, wenn der 404er-Fehler in einem Unterverzeichnis auftritt. Keinesfalls darf aber mit voller Server-Angabe referenziert werden (also etwa mit "ErrorDocument 404 https://www.brauchbar.de/404.php3"), weil eine solche URL-Referenzierung dazu führt, dass bei einem 404er-Fehler vom Server-Programm ein Page-Request ausgeführt wird. Damit verliert aber der Server aber zugleich die Angaben, die wir im Folgenden noch brauchen werden: welches Dokument nicht gefunden werden konnte und von welcher Seite dieses nicht auffindbare Dokument aufgerufen wurde (also verlinkt ist). Diese beiden Angaben finden sich nämlich im Anschluss an einen 404er-Fehler in den Umgebungsvariablen REQUEST_URI (fehlende Datei) und HTTP_REFERER (aufrufendes Dokument).

Als nächstes brauchen wir also eigentlich nur noch das PHP-Programm. Um dies nun in unser Fehler-Dokument 404.php3 einzufuegen, öffnen wir mit einem Text- oder Quellcode-Editor den Quellcode der Seite und kopieren das folgende Programm an den Anfang. Es ist wichtig, dass der gesamte PHP-Block ganz zu Anfang der Datei zu stehen kommt, da bei der Mapping-Funktion die Weiterleitung an die neue Adresse geschieht, indem ein Location-Header gesendet wird. Wurde schon irgendeine andere Zeile übertragen (etwa vom Dokument das html-Tag), erzeugt die Übertragung des Location-Headers einen Fehler. Deshalb in jedem Fall den Code an erster Stelle in die Datei einfügen!

<?php

  $reportType = "0";      /* Benachrichtigungsart "0": per e-Mail    */
                                             /*   "1": per LOG-Datei */
                                             /*   "2": beides        */

  $webmasterMail = "webmaster@horn-netz.de";   /* e-Mail-Adresse des Webmaster */
  $errorFile = $DOCUMENT_ROOT."/404.log";      /* Dateiname der LOG-Datei */

  $holeReferer = $HTTP_REFERER;
  $holeDefekte = $REQUEST_URI;

  if (isset($holeDefekte))
  {

    /* MAPPING-Teil */

    if (file_exists($DOCUMENT_ROOT."/mapping.txt"))
    {
      $umleitung = file($DOCUMENT_ROOT."/mapping.txt");
      if (is_array($umleitung))
      {
        while (list($key,$val) = each($umleitung))
        {
          $angabe = split(" ; ",$val);
          if (strtolower($angabe[0]) == strtolower($holeDefekte))
          {
            $adresse = strtolower($angabe[1]);
          }
        }
      }
    }

    if (isset($adresse))
    {
      header("Location: https://".$SERVER_NAME.$adresse);
      exit;
    }
      else
    {

      /* ERROR-Teil */

      if (isset($holeDefekte) and isset($holeReferer))
      {
        if (strpos($holeDefekte, "'mailto:") == 0)
        {
          $splitArray = parse_url($holeReferer);
          $referingDomain = $splitArray["host"];

          /* Setzen Sie in der folgenden Zeile Ihre Domainnamen ein,
             bei mehreren durch "|" getrennt */

          if (eregi("(www\.){0,1}(abitur-1994|horn-netz|deutschunterricht)\.de",
              $referingDomain, $regs))
          {
            $body = "Fehler 404 verursacht durch aufgerufene Seite https://"
                  .$SERVER_NAME.$holeDefekte ."\n";
            $body = $body."Fehler ausgeloest durch interne Seite "
                  .$holeReferer ."\n";
            $body = $body."Bitte den angegebenen Link korrigieren!\n\n";

            switch ($reportType == "0")
            {
              case "0":
                mail($webmasterMail,"[EIG] Interner 404-Fehler",$body);
                break;
              case "1":
                $handle = fopen($errorFile,"a");
                fputs($handle,$body);
                fclose($handle);
                break;
              case "2":
                mail($webmasterMail,"[EIG] Interner 404-Fehler",$body);
                $handle = fopen($errorFile,"a");
                fputs($handle,$body);
                fclose($handle);
                break;
             }
          }
            else
          {
            $body = "Fehler 404 verursacht durch aufgerufene Seite https://"
                  .$SERVER_NAME.$holeDefekte ."\n";
            $body = $body."Fehler ausgeloest durch externe Seite "
                  .$holeReferer ."\n";
            $body = $body."Bitte recherchieren und dem externen Webmaster Bescheid geben!\n\n";
            switch ($reportType == "0")
            {
              case "0":
                mail($webmasterMail,"[EIG] Externer 404-Fehler",$body);
                break;
              case "1":
                $handle = fopen($errorFile,"a");
                fputs($handle,$body);
                fclose($handle);
                break;
              case "2":
                mail($webmasterMail,"[EIG] Externer 404-Fehler",$body);
                $handle = fopen($errorFile,"a");
                fputs($handle,$body);
                fclose($handle);
                break;
             }
          }
        }
      }
    }
  }

?>

<html>
<head>
 ...

Die letzten Zeile (<html><head>...) markieren den Anfang des normalen HTML-Dokuments, das wir ja bereits erstellt haben.

Was muss nun noch angepasst werden? Eigentlich nur am Anfang des Dokuments unter $reportType die Art der Benachrichtigung ("0" steht für Benachrichtigung per e-Mail, "1" für Benachrichtigung durch Eintrag in die LOG-Datei, "2" durch beides gleichzeitig), unter $webmasterMail die e-Mail-Adresse, an die eine Benachrichtigung gehen soll und unter $errorFile den Dateinamen der LOG-Datei, in die hinein die 404er-Fehler protokolliert werden sollen. Je nachdem, welches Verfahren Sie zur Benachrichtigung verwenden (per Mail oder per Datei), muss natürlich nur der korrespondierende Eintrag verändert werden. Wenn Sie die Protokollierung per Datei erstellen, wird die LOG-Datei automatisch beim ersten Mal erstellt.

Als letzte Angabe müssen Sie noch im letzten Drittel des Scripts in der "eregi"-Anweisung den/die Namen Ihrer Domain(s) angeben. Tragen Sie dazu in den runden Klammern nur den Teil zwischen den Punkten ein. Im Beispiel habe ich meine Domains "www.abitur-1994.de", "www.horn-netz.de" und "www.deutschunterricht.de" verwendet. Wenn Sie z.B. nur die Domain "www.brauchbar.de" einsetzen wollen, tragen Sie in den runden Klammern nur "brauchbar" ein (ohne Anführungszeichen natürlich). Der Rest wird durch den regulären Ausdruck erzeugt. Wenn Sie mehrere Domains besitzen, wird jeder Name mit Hilfe eines vertikalen Strichs ("|", auf der Tastatur mit AltGr+"<" erzeugbar) voneinander getrennt. Benutzen Sie einen Unterserver (etwa www.meinserver.meinedomain.de oder nur meinserver.meinedomain.de), dann tragen Sie in Klammern "meinserver.meinedomain" ein. Liegen Sie mit Ihrer Homepage auf einem Server in einem Unterverzeichnis (etwa "www.meinedomain.de/meinaccount/), geben Sie nur "meinedomain" in den Klammern an, weil das Unterverzeichnis automatisch vom Server in die referenzierenden Variablen eingetragen wird.

Sollten Sie einen Server nutzen, der eine andere Länderkennung als ".de" besitzt, müssen Sie das Script leicht erweitern. Im Folgenden eine Version der Zeile für die Domains "www.meine-seite.de" und "www.my-page.com":

if (eregi("((www\.){0,1}(meine-seite)\.de)|((www\.){0,1}(my-page)\.com)",
    $referingDomain, $regs))

Nachdem die Datei 404.php3 nun auf den Server hochgeladen wurde, sollte eigentlich die Benachrichtigung funktionieren. Wichtiger Hinweis: Eine Benachrichtigung wird nur erstellt, wenn es eine referierende Seite gibt, das nicht gefundene Dokument also per Link aufgerufen wurde. Das heißt natürlich auch, dass man das Script nicht auf seine Funktionsfähigkeit hin testen kann, in dem man in die URL-Zeile irgendein Dokument "https://www.meine-seite.de/die-seite-gibts-nicht.html" einträgt und ENTER drückt. Hier erscheint dann nur die eigene 404er-Seite. Man muss zum Testen also eine Datei (nennen wir sie "test.html") erstellen, die auf eine solche fehlende Seite referenziert. Dann sollte man aber eine Benachrichtigung per e-Mail erhalten oder der Fehler in der LOG-Datei gespeichert werden. Will man die LOG-Dateien und Windows auswerten, muss Sie natürlich im ASCII-Modus vom Server geholt werden. Sobald man eine LOG-Datei auswertet, sollte man diese auf dem Server löschen, so dass die Aufzeichnung neu beginnt.

Der Mapping-Teil des PHP-Script benutzt die Funktion "Location" der direkten Header-Übertragung zur unmittelbaren Übergabe der Umleitung an den Browser. Manche Server (etwa der Server von 1&1) ist hinsichtlich PHP falsch konfiguriert, so dass diese Funktion kein Ergebnis liefert, also nicht auf die in der Mapping-Datei angegebene Seite umgeleitet wird.

Sollte dies bei Ihnen auch der Fall sein, so müssen Sie nur die Zeilen

if (isset($adresse))
{
   header("Location: https://".$SERVER_NAME.$adresse);
   exit;
}

durch die folgenden ersetzen:

if (isset($adresse))
{
   echo "<head>";
   echo "  <meta http-equiv=\"refresh\"
content=\"0;url=https://www.".$SERVER_NAME.$adresse."\">";
   echo "</head>";
   exit;
}

Dann müsste auch bei Ihrem Server die Umleitung funktionieren. Eventuell können Sie sich die "www."-Angaben in der abgeänderten Version sparen – das ist eine Sache des Ausprobierens.

4. Nutzung der Mapping-Funktion

Wenn Sie keine Mapping-Funktion nutzen wollen, brauchen Sie nichts weiter zu machen - also nicht einmal eine leere Datei erstellen. Sollten Sie aber die Mapping-Funktion nutzen wollen, müssen Sie im Hauptverzeichnis Ihres Servers eine Datei namens "mapping.txt" erstellen und dort alle Einträge hinterlegen. Die Einträge haben folgende Struktur:

/ehemalige-datei ; /neue-datei

Wurde also beispielsweise die ehemalige Datei "https://www.meine-seite.de/impressum.html" umbenannt in "https://www.meine-seite.de/info.html" und die Datei "https://www.meine-seite.de/ueber-mich.html" in die Datei "https://www.meine-seite.de/personalities/index.html", dann sähe die Datei "mapping.txt" folgendermaßen aus:

/impressum.html ; /info.html
/ueber-mich.html ; /personalities/index.html

Wie man schon am Beispiel sieht, ist das Mapping an sich sehr flexibel. Man kann auch Dateien kreuz und quer innerhalb von Unterverzeichnissen verschieben. Wichtig bei allen Angaben ist die Referenzierung als absolute Adresse mit führendem Slash ("/") - alle Angaben müssen also immer vom ROOT aus erfolgen.

Jedes Mapping erhält eine eigene Zeile, die Angaben von altem und neuem Dateinamen werden mit " ; " (in Worten: Leerzeichen Semikolon Leerzeichen) getrennt. Es ist wichtig, dass diese Trennung strikt eingehalten wird, da sonst die einzelnen Mapping-Angaben nicht korrekt aufgeteilt werden können.

Die Anzahl der Umleitungen, die man auf diese Weise installiert, ist beliebig. Es sei hier allerdings darauf hingewiesen, dass natürlich viele Umleitungen auch ziemliche Server-Kapazitäten verbrauchen und die Reaktionszeit der eigenen Seite bei 404er-Fehlern drastisch bremst. Man sollte dieses System also nicht einsetzen, um auf bequeme Art und Weise normale Umleitungen zu installieren.

5. Ablauf des Scripts

Wie läuft nun das Script ab? Das lässt sich am besten Anhand eines vereinfachten Ablaufplans zeigen:

Bei 404er-Fehler Umleitung auf Dokument /404.php3

  • Script checkt, ob die Datei /mapping.txt vorhanden ist
    • wenn ja, dann durchsucht es die Angaben nach möglichen Ersetzungen
      • wenn Ersetzung gefunden, Umleitung auf die neue Adresse; Ende des Scripts
    • wenn nicht gefunden, dann weiter im Script
  • Script überprüft, ob es ein Link-Fehler ist
    • wenn ja, dann überprüft es ob es ein internes Problem ist
      • wenn ja, Meldung eines internen Fehlers
      • wenn nein, dann Meldung eines externen Fehlers
    • wenn es kein Link-Fehler ist (defekter Bookmark etc.), dann wird keine Fehlermeldung an den Webmaster abgesetzt
  • Ausgabe der 404er-Informationsseite mit entsprechendem Inhalt (Sitemap etc.)

Die eigentliche 404er-Seite wird also immer ausgegeben - einzige Ausnahme ist das Auffinden einer Ersetzung durch die Mapping-Funktion. Dann bekommt der Benutzer vom internen "Fehler" gar nichts mit.

6. Schlussbemerkungen

Natürlich meldet das Script nicht nur fehlende Seiten, sondern auch fehlende Grafiken, Hintergrundbilder etc. Auf diese Weise ist man immer auf dem Laufenden, welche Fehler sich noch auf der eigenen Homepage tummeln.

Die Frage, ob man Benachrichtigung per e-Mail oder per LOG-Datei bevorzugen sollte, ist Geschmackssache. Ich gestehe ein, dass ich die Funktion "LOG-Datei" extra für diesen Artikel implementiert habe - ich benutze eigentlich immer nur die Benachrichtigung per e-Mail, weil man so am schnellsten auf Fehler aufmerksam wird. Das kann aber auch zu einer wahren Mail-Flut führen, wenn sich viele Fehler auf der eigenen Homepage tummeln. Dann sollte man aber spätestens überlegen, ob es nicht Zeit für einen Relaunch ist...

Noch ein paar Worte zur Geschichte des Programms seien mir erlaubt. Dieses Skript ist ursprünglich für einen Domain-Verbund entstanden, in dem sechs eng miteinander verzahnte Domains von vier Redakteuren teilweise überschneidend bearbeitet wurden - wodurch natürlich viele Probleme durch Cross-Links entstanden. Daraus erklärt sich auch die Differenzierung zwischen internen und externen Fehler. Bei einem internen Fehler, der durch einen Link von Domain A auf eine fehlende Seite von Domain B ausgelöst wurde, entstand der Fehler natürlich auf Domain B. Bescheid bekommen musste allerdings der Redakteur von Domain A, weil er seinen Link überprüfen musste. Wurde der Fehler auf Domain B aber durch einen Link außerhalb des Domainverbundes erzeugt, war natürlich der Redakteur von Domain B die richtige Ansprechperson, denn er musste sich bei Bedarf mit dem Betreiber der externen Webseite in Verbindung setzen und eine Abänderung des Links erbeten. Auch wenn ich in dieser Version des Scripts davon ausgehe, dass es nur einen Webmaster gibt, habe ich die Unterscheidung beibehalten. Dies resultiert einfach aus meiner Erkenntnis, dass man aufgrund des unterschiedlichen Subjects besser einschätzen kann, wieviel Arbeit es sein wird, den Fehler zu beheben. Interne Fehler, die man selbst korrigieren kann, lassen sich zumeist schneller beheben, als wenn man erst die e-Mail eines externen Webmasters herausbekommen und ihn dann auch noch in einer e-Mail lang und breit anschreiben muss - und alles für einen einzigen Link.

Eric Horn
https://www.horn-netz.de

Viel Erfolg.