PHP: Wie leitet man auf eine andere Seite weiter?

Eine Weiterleitung in PHP ist eine Zeile Code. Trotzdem gibt es dabei zwei Fehler, die so häufig vorkommen, dass sie den größeren Teil dieses Artikels ausmachen.

Der richtige Weg

<?php
  header('Location: /ziel.php');
  exit;
?>

Das war es schon. header() setzt den HTTP-Header Location, der Browser folgt ihm. Schauen wir uns die tatsächliche Antwort des Servers an:

HTTP/1.1 302 Found
Content-Type: text/html; charset=UTF-8
Location: /ziel.php

Statuscode 302, ein Location-Header, kein Inhalt. Genau so soll es aussehen.

Fehler 1: das vergessene exit

Der Aufruf von header() beendet dein Skript nicht. Alles was danach kommt, wird weiterhin ausgeführt und ausgeliefert:

<?php
  header('Location: /ziel.php');
  echo "GEHEIM: dieser Text wird trotzdem ausgeliefert\n";
?>

Die Antwort des Servers sieht dann so aus:

HTTP/1.1 302 Found
Location: /ziel.php

GEHEIM: dieser Text wird trotzdem ausgeliefert

Im Browser fällt das nicht auf, weil dieser der Weiterleitung folgt und den Text nie anzeigt. Der Text ist aber Teil der Antwort. Jedes Werkzeug, das der Weiterleitung nicht folgt, bekommt ihn zu sehen. Ein einfaches curl ohne -L reicht dafür schon.

Und jetzt stell dir vor, an dieser Stelle steht nicht GEHEIM, sondern der Inhalt einer Seite, die eigentlich nur für angemeldete Benutzer gedacht ist. Genau so entsteht eine Zugriffsprüfung, die im Browser funktioniert und sonst nirgends:

<?php
  // So bitte nicht!
  if (!$benutzerIstAngemeldet) {
    header('Location: /login.php');
    // hier fehlt das exit
  }

  echo $geheimeDaten;
?>

Deshalb: nach jedem header('Location: ...') ein exit.

Fehler 2: es wurde schon etwas ausgegeben

HTTP-Header müssen vor dem Inhalt verschickt werden. Sobald PHP das erste Zeichen ausgegeben hat, sind die Header unterwegs und lassen sich nicht mehr ändern:

<?php
  echo "Ausgabe vor dem Redirect\n";
  header('Location: /ziel.php');
  exit;
?>

Das Ergebnis ist bemerkenswert:

HTTP/1.1 200 OK

Ausgabe vor dem Redirect
Warning: Cannot modify header information - headers already sent by
(output started at /var/www/html/nach-ausgabe.php:3) in ... on line 4

Der Statuscode ist 200, es gibt keinen Location-Header. Die Weiterleitung findet also gar nicht statt. Und auf einem Produktivsystem, wo display_errors ausgeschaltet ist, siehst du nicht einmal die Warnung — sondern nur eine Seite, die sich einfach nicht weiterbewegt.

Die häufigsten Auslöser sind:

  • ein echo oder print vor dem header()
  • eine Leerzeile vor dem einleitenden <?php
  • eine Leerzeile nach einem abschließenden ?> in einer eingebundenen Datei
  • ein BOM am Dateianfang, das dein Editor hinterlassen hat

Der letzte Punkt ist besonders tückisch, weil man das Zeichen nicht sieht. Lass das schließende ?> in reinen PHP-Dateien am besten ganz weg, dann kann dort auch nichts mehr hinter dem Code stehen.

Der richtige Statuscode

Ohne weitere Angabe schickt PHP einen 302 Found, also eine temporäre Weiterleitung. Ist die Seite dauerhaft umgezogen, willst du einen 301:

<?php
  header('Location: /ziel.php', true, 301);
  exit;
?>
HTTP/1.1 301 Moved Permanently
Location: /ziel.php

Der Unterschied ist nicht kosmetisch. Suchmaschinen übernehmen bei einem 301 die neue Adresse in ihren Index, bei einem 302 nicht. Browser merken sich einen 301 außerdem und rufen die alte Adresse gar nicht mehr auf — was beim Testen gerne für Verwirrung sorgt. Setze also nur dann einen 301, wenn du dir sicher bist.

Leitest du nach einem abgeschickten Formular weiter, ist 303 See Other die saubere Wahl. Damit stellst du sicher, dass der Browser die Zielseite per GET anfordert und ein Neuladen die Daten nicht erneut abschickt.

Relative oder absolute Adresse?

Früher schrieb HTTP/1.0 einen vollständigen URI vor. Seit RFC 7231 sind relative Angaben ausdrücklich erlaubt und funktionieren in allen aktuellen Browsern. /ziel.php ist also in Ordnung.

Zusammenfassung

  • header('Location: ...') gefolgt von exit.
  • Ohne exit wird der restliche Inhalt mit ausgeliefert, auch wenn du ihn im Browser nicht siehst.
  • Vor dem header() darf nichts ausgegeben worden sein, sonst gibt es überhaupt keine Weiterleitung.
  • 302 ist temporär, 301 dauerhaft, 303 nach einem Formular.

Hinweis zu Netcup (Werbung)

Der deutsche Hoster Netcup bietet unter anderem günstige und zugleich leistungsstarke Webhosting Pakete, KVM-basierte Root Server und dezidierte Server an. Mit unseren Gutscheincodes kannst du noch mehr Geld sparen (6€ bei deiner ersten Bestellung, 30% Rabatt auf alle KVM-basierten Root Server, ...).