Pit: php header:location

Hallo Forum,

ich habe gerade (anscheinend) ein Problem mit "header("Location: index.php");".

Mir ist bewußt, dass header("Location:") eine absolute URL erwartet, aber da es nie Probleme hiermit gab, habe ich es so gelassen.

Nun gibt es aber (anscheinend) Probleme hiermit, denn das Script springt auf eine index.php, die ich "pfadmäßig" nicht erwartet hatte.

Kann das möglich sein?

Sollte ich lieber über $_SERVER['SERVER_NAME']."path/to/my/destination/index.php" gehen?

Pit

  1. Hallo Pit,

    Mir ist bewußt, dass header("Location:") eine absolute URL erwartet,

    Mir nicht. Und der MDN auch nicht. Im RfC 2616 steht es tatsächlich so drin, aber RfC 7231, der den 2616 ersetzt, lässt auch relative URIs zu. Vermutlich hat sich nie ein Browser strikt an die Spec gehalten, und sie wurde daraufhin den Realitäten angepasst. Ist ja auch einfacher für Autoren, das so zu machen.

    Insofern - keine Panik, du kannst den Host-Teil der URI auf jeden Fall weglassen.

    Wenn Du //example.org/path/to/somewhere/foo.php abrufst und dort location: index.php gesetzt wird, leitet der Browser zu //example.org/path/to/somewhere/index.php

    Wenn Du //example.org/path/to/somewhere/foo.php abrufst und dort location: ../index.php gesetzt wird, leitet der Browser zu //example.org/path/to/index.php

    Wenn Du //example.org/path/to/somewhere/foo.php abrufst und dort location: /some/other/path/index.php gesetzt wird, leitet der Browser zu //example.org/some/other/path/index.php weiter.

    Zumindest sollte es so sein.

    Rolf

    --
    sumpsi - posui - clusi
    1. Hallo Rolf,

      Mir nicht. Und der MDN auch nicht. Im RfC 2616 steht es tatsächlich so drin, aber RfC 7231, der den 2616 ersetzt, lässt auch relative URIs zu. Vermutlich hat sich nie ein Browser strikt an die Spec gehalten, und sie wurde daraufhin den Realitäten angepasst. Ist ja auch einfacher für Autoren, das so zu machen.

      Insofern - keine Panik, du kannst den Host-Teil der URI auf jeden Fall weglassen.

      Hm.. gut zu wissen, danke für den Hinweis.

      Wenn Du //example.org/path/to/somewhere/foo.php abrufst und dort location: index.php gesetzt wird, leitet der Browser zu //example.org/path/to/somewhere/index.php

      Wenn Du //example.org/path/to/somewhere/foo.php abrufst und dort location: ../index.php gesetzt wird, leitet der Browser zu //example.org/path/to/index.php

      Wenn Du //example.org/path/to/somewhere/foo.php abrufst und dort location: /some/other/path/index.php gesetzt wird, leitet der Browser zu //example.org/some/other/path/index.php weiter.

      Zumindest sollte es so sein.

      Hätte ich dann auch so erwartet, aber:

      Ich erhalte aufgrund einer (ich habe nicht den blassen Schimmer - warum - und von wem) ins "../-Verzeichnis" geworfenen index.php einen geloggten Fatal Error. Diese Datei hätte aber nie aufgerufen werden dürfen, da deren Aurfuf aus einem "header("Location: index.php");" resultiert, das eine Verz.-Ebene darunter notiert ist.

      Ich habe eine rekursive Suche nach dem Aufruf des im Errorlog verzeichneten Code gemacht und dieser befindet sich ausschließlich in der (woher auch immer stammenden) index.php in der Ebene darüber. Zeilennummer, Pfad und alles andere stimmen auch überein. Und wie gesagt, ist dieser Code nirgends anders zu finden. Aber er löst einen Fatal-Error aus, also muß er aus dieser Datei kommen. Und weitere Logdaten lassen erkennen, dass die Datei aus einer Weiterleitung einer Datei stammt, die eine Ebene darunter liegt und schlicht auf "header("Location: index.php");" per header:location umleitet.

      Wie kann/soll ich mir das erklären?

      Pit

      1. Tach!

        Ich erhalte aufgrund einer (ich habe nicht den blassen Schimmer - warum - und von wem) ins "../-Verzeichnis" geworfenen index.php einen geloggten Fatal Error. Diese Datei hätte aber nie aufgerufen werden dürfen, da deren Aurfuf aus einem "header("Location: index.php");" resultiert, das eine Verz.-Ebene darunter notiert ist.

        Es kommt nicht darauf an, wie die Datei-Struktur am Server ist. Der Browser kann zwar einen relativen Redirect empfangen, aber er bezieht ihn nicht auf die Datei, in der er steht, denn davon hat er keine Ahnung. Stattdessen bezieht er ihn auf seine aktuelle URL, für die die Response ein Redirect ist. Er kann nämlich keine relativen Requests stellen, weil ein Server wiederum keine Ahnung hat, wo sich der Client gerade befindet, und ob sich der Request überhaupt aus einer URL in einer Webseite ergibt oder aus einer eingetippten Adresse oder einem Bookmark.

        Wenn du beispielsweise eine URL aufgerufen bekommst, die zu einer Datei führt, und darin befindet sich ein include/require(_once) einer Datei in einem anderen Pfad, und da wiederum ist der Redirect drin, dann bezieht das der Browser nicht auf die inkludierte Datei - weil er es nicht kann - sondern auf die URL des Requests.

        Wie kann/soll ich mir das erklären?

        Es kann sich nur um eine Unstimmigkeit bei den tatsächlichen und angenommenen URLs handeln. Zur Not müsstest du die angeforderte URL mitloggen, damit du im Fehlerfall weißt, was der Client angefordert hat.

        dedlfix.

        1. Hallo dedlfix,

          der Redirect wird nicht vom Server ausgeführt, sondern der Browser reagiert auf den Response-Header. Und er ruft, bei einer relativen URL, die neue Adresse relativ zu der Adresse aus dem Request ab.

          Wenn also foo.php und bar.php im gleichen Ordner stehen, sollte eine Response aus foo.php mit Header Location: bar.php die gewünschte Umleitung erzielen (Location, nicht location, das hatte ich oben falsch geschrieben, sorry).

          Um das zu debuggen, würde ich im Netzwerk-Tab des Browsers beginnen. Welche Requeste führt der Browser durch, welche Header sind in der Response des ersten Requests?

          Übrigens, Pit, wenn Du schon die URL verabsolutieren willst, dann ist $_SERVER['SERVER_NAME'] das falsche Präfix. Richtig ist

          $protocol = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') ? "https" : "http";
          $url = $protocol.'://'.$_SERVER['HTTP_HOST'].'/path/to/app/foo.php';
          

          Das Protokoll wird über die $_SERVER Variable HTTPS ermittelt, die für HTTP leer ist. Bzw. beim IIS den Wert "off" enthält (argh!). Der Wert, der bei HTTPS drin steht, ist undefiniert, darum die krumme Abfrage.

          Das // vorneweg sagt, dass die URL mit einem Hostnamen beginnt. HTTP_HOST enthält auch den Port, falls Du die Seite nicht über Port 80 aufgerufen hast. Und zwischen Hostname und Pfad muss ein / stehen, das ist in der HTTP_HOST Variablen nicht drin.

          Aber wie gesagt, das ist unnütze Akrobatik. Man kann protokollfreie URLs verwenden (//example.org/path/foo.php) und auch hostfreie URLs (/path/foo.php). Das Problem dürfte anderswo liegen.

          Rolf

          --
          sumpsi - posui - clusi
          1. Tach!

            der Redirect wird nicht vom Server ausgeführt, sondern der Browser reagiert auf den Response-Header. Und er ruft, bei einer relativen URL, die neue Adresse relativ zu der Adresse aus dem Request ab.

            Wenn also foo.php und bar.php im gleichen Ordner stehen, sollte eine Response aus foo.php mit Header Location: bar.php die gewünschte Umleitung erzielen (Location, nicht location, das hatte ich oben falsch geschrieben, sorry).

            Jein, wie du auch sagst, ist das kein Thema für den Server, also sind dessen Gegebenheiten im Dateisystem nicht direkt maßgebend. Wenn foo.php und bar.php im selben Ordner stehen, führt das nur dann zum selben Ergebnis, wenn dazu als Bedingung erfüllt ist, dass die sie über URLs auf derselben Ebene aufgerufen werden. Mit selber Ebene meine ich, dass der Path-Anteil zwischen Hostnamen und dem am weitesten rechts stehenden '/' derselbe ist.

            http://example.com/x/y/foo/parametername/value
            http://example.com/x/y/bar
            

            Beide URLs könnten zwar per Rewrite-Mechanismus auf foo.php und bar.php im selben Verzeichnis umgeschrieben werden, sie sind aber aus Client-Sicht auf unterschiedlichen Ebenen. Ein relatives Location auf index php führt zu

            http://example.com/x/y/foo/parametername/index.php
            http://example.com/x/y/index.php
            

            Solch ein Rewriting wäre neben dem von mir bereits erwähnten include/require von Dateien eine zweite Möglichkeit, dass URLs nicht mit Dateinamen gleichgesetzt werden können.

            Um das zu debuggen, würde ich im Netzwerk-Tab des Browsers beginnen. Welche Requeste führt der Browser durch, welche Header sind in der Response des ersten Requests?

            Im Prinzip ja, dazu muss man sie aber auch aus den Fehlermeldungen im Logfile nachvollziehen können.

            Übrigens, Pit, wenn Du schon die URL verabsolutieren willst, dann ist $_SERVER['SERVER_NAME'] das falsche Präfix. Richtig ist ... [Protokoll + $_SERVER['HTTP_HOST']]

            Ja, Begründung ist, dass der SERVER_NAME der Name des VHost ist, der HTTP_HOST hingegen der Hostname, den der Client anfordert. Das kann durchaus auch ein Alias zum Servernamen sein, und dann hat man eine Umleitung zu einem anderen Domainnamen als Ergebnis. Soweit die Theorie. In der Praxis ist es wohl so, dass im Zusammenspiel zwischen Apache und PHP der SERVER_NAME ebenfalls auf den Wert Hostname gesetzt und damit beides gleich ist. Das kann aber anderenorts anders sein, weshalb man der Ordnung halber doch lieber HTTP_HOST nimmt.

            dedlfix.

            1. Hallo zusammen,

              vorab erstmal Dankeschön für Eure Hilfe.

              Glücklicherweise gelingt es mir inzwischen, den Fehler zu reproduzieren, damit ist schonmal ein wichtiger Punkt erfüllt, ihn zu verhindern.

              Es scheint auch so zu sein, dass der Fehler im Errorlog nur mittelbar mit meinem header-Location zu tun hatte. Das header-Location hat nämlich gar nicht funktioniert, weil bereits eine header-Information geflossen war. (seltsam, dass das im Erorlog nicht auftaucht, ist aber trotzdem so). Fast zeitgleich hat (laut accesslog) irgendwer versucht, die index eine Ebene höher aufzurufen, daher dachte ich, es hinge mit der Umleitung zusammen. Dem war aber nicht so!

              Ein Hinweis im Errorlog auf die fehlerhafte Umleitung (header already sent oder sowas) hätte mir hier vermutlich bereits früher geholfen. 😕

              Pit

              1. Tach!

                (seltsam, dass das im Erorlog nicht auftaucht, ist aber trotzdem so).

                error_reporting steht so, dass es auch da landen kann? Vielleicht ist es für bestimmte niedere Level ausgestellt, damit nicht ganz penibel programmierte 3rd-party-Libs nicht zu viel darein kippen?

                dedlfix.

                1. error_reporting steht so, dass es auch da landen kann? Vielleicht ist es für bestimmte niedere Level ausgestellt, damit nicht ganz penibel programmierte 3rd-party-Libs nicht zu viel darein kippen?

                  Hi dedlfix,

                  das halte ich für möglich, da das aber bei meinem Provider über eine GUI läuft und ich das da grad nicht finde, kann ich das auch nur vermuten…

                  Pit

  2. Lass Protokoll und Servername weg und notiere das was übrigbleibt, beginnend mit einem Slash. Beispiel:

    http://example.com/foo/bar/baz.html
                      /foo/bar/baz.html
    

    MFG