Heiko Richler

Computer — Know How!

Redirect depending on the given hostname

Motivation

Jump to description

Any Website may be accessible by several addresses. Often host names like example.com and www.example.com address the same website. It may be useful to be reachable through both addresses but to display only one of them in the browsers address bar. To do so you can redirect from example.com to www.example.com

One server may host many websites at the same time. Which site to deliver is decided based on the given host name within the HTTP request.

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

www.example.com — unknown host!

What should a server do when it gets a request for a host it doen not know? It provides it default page. Some of the big commercial hosters have error or advertising pages for that purpose.

Under the cover of host names the internetuses IP addresses. These are based on numbers and they may be long and ugly. The domain name system (DNS) is there to get the corresponding IP address to a host name. When you enter www.richler.de in your webbrowser it asks a nameserver for the IP address (87.106.74.74) and sends a HTTP request to this address.

The domains richler.de and richler.info are set up, like many others too, as wildcard domains. This means any hostnames in this domain point to the same address. In my case both domains point to the same address. So invalid.richler.de points to the same address as invalid.richler.de and so to the same default page of the webserver.

Redirect — but individually

If you don't want to loose visitors you should provide a useful standard page or redirect them to your main website. This should happen based on the given host name. A request for invalid.richler.de should redirect to www.richler.de while a request for not-there.richler.info should redirect to www.richler.info

What is checkHostname()?

Jump to source code

checkHostname uses a config file to determ where to redirect to.

To be able to perform a redirect the function must be called before any HTML code is send to the client.

In case of a redirection the original PHP script is terminated in the other case the function returns true or false. false indicates a redirection loop. The destination of the redirection is performing this redirection again. Some browsers, like Microsoft Internet Explorer™ 6, wound't detect that.

A tiny example:

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>

The redirection is controlled by a config or ini file. The hostnames are given in squared brackets followed by some parameters. Only the first matching host name will be observed, all others will be ignored.

In the example (hosts.ini) in line 1 a rule for www.richler.de is defined. As there are no parameters no redirection will be performed. This may be used to exclude certain hosts from being redirected.

Starting from line 4 a redirection from ww.richler.de to www.richler.de is configured.

In line 7 with *.richler.de a rule for all sub domains of richler.de is defined. This matches any host name ending with .richler.de. So this rule would even match for www.richler.de. But as the rule in line 1 matches first this rule would not be reached.

The rule for richler.de has two new parameters. keepquery = true tells to keep the given query. So from http://richler.de/page.html will be redirected to http://www.richler.de/page.html. Without keepquery the destination would be http://www.richler.de.

HTTP knows to kinds of redirection; temporarily and permanent. Permanent redirections may be remembered by browsers or proxies. So there is no need to send the same request again. But even search engines may use this information to replace a permanent redirected page in their index with the redirection destination.

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/

The function checkHostname()

Jump to 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 chkHostname.php

here you may download the source with an example: chkHostname.zip.