Heiko Richler

Computer — Know How!

Weiterleiten mit PHP abhängig vom Hostnamen

Motivation

Weiter zur Beschreibung

Eine Website kann im Internet unter vielen Adressen erreichbar sein. Oft sind example.com und www.example.com die selbe Site. Es kann aber von Vorteil sein unter beiden Adressen erreichbar zu sein, aber in der Adresszeile des Browsers immer nur eine Adresse zu haben. Dazu könnte bei example.com eine Weiterleitung auf www.example.com eingerichtet werden.

Webserver können viele Webseiten gleichzeitig hosten. Welche Website sie liefern sollen, unterscheiden Sie anhand des angegebenen Hosts in der HTTP-Abfrage.

HTTP Anfrage
  1 GET /directory/file.html HTTP/1.1
  2 Host: www.richler.de

www.example.com — habe ich nicht!

Was macht ein Server wenn er eine Anfrage für einen Host bekommt, den er nicht kennt? In diesem Fall liefert der Server seine Standardseite. Einige der großen Webspace Anbieter haben dazu Fehlermeldungen oder Eigenwerbung als Standard eingerichtet.

Das Internet arbeitet „ intern ” mit IP-Adressen. Diese bestehen nur aus Zahlen. Um zu den bekannten Hostnamen eine IP-Adresse zu erhalten gibt es sogenannte Nameserver. Wenn Sie im Webbrowser www.richler.de eingeben, fragt dieser die dazugehörige Adresse (87.106.74.74) ab und schickt an diese Adresse seinen HTTP-Request.

Die Domänen richler.de und richler.info sind, wie viele anderen auch, so eingerichtet, dass alle Hostname auf die selbe IP-Adresse zeigen. Bei diesen beiden Domänen ist das sogar die selbe. Darum liefern invalid.richler.info und invalid.richler.de die selbe Standardseite.

Weiter schicken — aber ganz individuell

Um Besucher nicht zu verlieren soll die Standardseite Besucher auf existierende Angebote weiterleiten. Dies soll abhängig vom benutzten Hostnamen geschehen. Eine Anfrage auf invalid.richler.de soll auf www.richler.de und eine Anfrage auf not-here.richler.info auf www.richler.info geleitet werden.

Was ist checkHostname()?

Weiter zum Quellcode

checkHostname kontrolliert anhand einer angegebenen Konfigurationsdatei was wohin umgeleitet werden soll.

Die Funktion muss aufgerufen werden bevor Teile einer Inhaltsseite (HTML) zum Client übertragen wurde. Andernfalls kann eine Weiterleitung (Redirect) nicht mehr angestoßen werden.

Im Falle einer Weiterleitung wird die Abarbeitung der Anfrage abgebrochen. Die Funktion wird nicht mehr verlassen. Erfolgt keine Weiterleitung liefert sie true wenn kein Bedarf an einer Weiterleitung besteht oder false wenn eine Weiterleitung nicht sinnvoll ist. Dies ist in der Regel dann der Fall, wenn als Ziel die aufgerufene Adresse eingetragen ist. Sonst würde eine Endlos-Schleife erzeugt. Manche Browser, z.B. Microsofts Internet Explorer™ 6, erkennen so eine Schleife nicht!

Ein kleines und verbesserungswürdiges Beispiel:

index.php
  1 <?php
  2
  3 require_once('chkHostname');
  4
  5 if (!checkHostname('hosts.ini')) {
  6     echo 'this is an error!';
  7 }
  8
  9 ?><html>
 10 <body>
 11     <h1>Welcome</h1>
 12 </body>
 13 </html>

Gesteuert wird die Weiterleitung durch eine Konfigurationsdatei bzw. ini-Datei. In eckigen Klammern stehen die Hostnamen und danach die dazugehörigen Parameter. Es wird immer nur die erste zutreffende Regel angewandt alle weiteren werden nicht mehr beachtet.

Im Beispiel (hosts.ini) wird in Zeile 1 eine Regel für den Hostanmen www.richler.de angelegt. Da dieser keine Parameter folgen wird keine Weiterleitung ausgeführt. Dies kann benutzt werden eine Weiterleitung für diesen Hostnamen auszuschließen.

Ab Zeile 4 ist für ww.richler.de eine Weiterleitung auf www.richler.de angegeben.

In Zeile 7 ist *.richler.de eine Regel für alle Subdomains von richler.de eingerichtet. Sie trifft auf alle Hostnamen zu die vorne beliebe Zeichen haben und mit .richler.de enden. Diese Regel würde also auch auf www.richler.de und ww.richler.de zutreffen. Da diese Namen aber schon vorher angegeben worden sind, würde die Regel *.richler.de nicht mehr erreicht.

Die Regel für für richler.de gibt mir keepquery = true an, dass die Anfrage durchgereicht werden soll. Die Adresse http://richler.de/seite.html wird also auf http://www.richler.de/seite.html weitergeleitetn. Ohne keepquery wäre das Ziel http://www.richler.de.

Das HTTP kennt zwei Arten von Weiterleitungen; Vorrübergehende und permanente. Bei permanenten Weiterleitungen können Browser oder Proxys sich die Weiterleitung merken und brauchen den Server nicht jedes mal erneut zu fragen. Aber auch Suchmaschinen benutzen diese Informationen um Permanent weitergeleitete Seiten in ihrem Index durch das Weiterleitungsziel zu ersetzen.

hosts.ini
  1 [www.richler.de]
  2 ; do nothing - no more rules will be checked
  3
  4
  5 [ww.richler.de]
  6 redirect = http://www.richler.de/
  7
  8
  9 [*.richler.de]
 10 redirect = http://www.richler.de/
 11
 12
 13 [*.richler.info]
 14 redirect = http://www.richler.info/
 15
 16
 17 [richler.de]
 18 redirect = http://www.richler.de
 19
 20 keepquery = true
 21
 22 permanent = true
 23
 24
 25 ; Default:
 26
 27 [*]
 28 redirect = http://www.richler.de/

Die Funktion checkHostname()

Weiter zum Download
chkHostname.php
  1 <?php
  2 /*********************************************************************
  3 *
  4 * Reads a given ini-File and does some Redirection based on Hostnames
  5 *
  6 * If there is a matching rule this function will redirect to an other
  7 * site. If there is no matching rule or one with no destination it
  8 * returns true. If there is a matching rule but the function detects
  9 * a loop it returns false.
 10 *
 11 * (C) 2006 by Heiko Richler  http://www.richler.de/
 12 *
 13 * This Code is provided unter the GNU Lesser General Public License
 14 * http://www.gnu.org/licenses/lgpl.html
 15 *
 16 * No warranty at all!
 17 */
 18 function checkHostname($inifile = 'hosts.ini') {
 19     // Load ini-File. Each section in this file represents an
 20     // Domainname to match.
 21     $ini = parse_ini_file($inifile, true);
 22     foreach($ini as $domain => $vArgs) {
 23         // Create regular expression out of Domainname. It may
 24         // contain * as a wildcard
 25         $regex = '/^' . str_replace(array('.',  '*'),
 26                                     array('\.', '.*'), $domain) . '$/i';
 27         if (preg_match($regex, $_SERVER['HTTP_HOST'])) {
 28             if (isset($vArgs['redirect'])) {
 29                 $destination = $vArgs['redirect'];
 30                 if (isset($vArgs['keepquery']) and $vArgs['keepquery']) {
 31                     $destination .= $_SERVER['REQUEST_URI'].
 32                                     $_SERVER['QUERY_STRING'];
 33                 }
 34                 $old = 'http'.
 35                        (($_SERVER['HTTPS'] == 'on') ? 'S' : '').
 36                        '://'.
 37                        $_SERVER['HTTP_HOST'].
 38                        $_SERVER['REQUEST_URI'].
 39                        $_SERVER['QUERY_STRING'];
 40                 if ($old == $destination) { // this would be a loop!
 41                     // error_log(...) ?
 42                     return false;
 43                 }
 44                 $counter = 0;
 45                 if (isset($_COOKIE['RedirectCounter'])) {
 46                     $counter = @intval($_COOKIE['RedirectCounter']);
 47                 }
 48                 if (isset($_COOKIE['Redirected'])) {
 49                     // I redirected from here ...
 50                     if ($_COOKIE['Redirected']>=time()-5) {
 51                         // ... and I did so within the last 5 seconds
 52                         // this should be a lot of time - even for a
 53                         // slow client
 54                         $counter++;
 55                     }
 56                     else {
 57                         // after more than 5 seconds I believe this was a
 58                         // reload by user
 59                         $counter = 0;
 60                     }
 61                 }
 62                 if ($counter>19) { // Firefox waits till 20 to detect a loop
 63                     // this may be a loop!
 64                     // error_log(...) ?
 65                     return false;
 66                 }
 67                 setcookie('Redirected',time(),time()+60);        // 60 seconds
 68                 setcookie('RedirectCounter',$counter,time()+60); // 60 seconds
 69                 if (isset($vArgs['permanent']) and $vArgs['permanent']) {
 70                     // Use a permanent reddirect means the client may
 71                     // remember it and doesn't ask for the original site
 72                     // again soon.
 73                     header('HTTP/1.1 301 Moved Permanently');
 74                 }
 75                 header("Location:$destination");
 76                 echo "<html><body>
 77                      <a href='$destination'>Redirecting to $destination.</a>
 78                      </body></html>";
 79                 die(); // do nothing else
 80             }
 81             // No Destination to redirect to, so do nothing
 82             // This can be used to set a rule without redirecting to
 83             // avoid following rules to match.
 84             return true;
 85         }
 86     }
 87     // No Matching Rule
 88     return true;
 89 }
 90 ?>

Download von chkHostname.php

Hier können Sie die Projektdateien in einem Archiv beziehen: chkHostname.zip.