Gestion des erreurs avec PHP sur un serveur Apache

Afin d'éviter qu'un visiteur ne se retrouve devant une page blanche (par défaut sur un serveur de ce type) mais plutôt qu'il lui soit proposé une porte d'entrée honnorable, voici un script permettant de "prendre la main" sur l'erreur en question, qui peut avoir plusieurs origines. Ce script est actuellement en place sur l'ensemble de mes sites.

Deux fichiers : .htaccess et erreur.php à la racine du site

Le fichier .htaccess (doc httpd.apache.org/docs-2.1/howto/htaccess.html) est lu en premier par le serveur Apache, avant toute autre opération. Il est d'ailleurs une façon très puissante de contrôler les entrées d'un site (lire article spip-contrib.net www.spip-contrib.net/article106.html sur la réécriture d'URL, ou encore la doc Apache httpd.apache.org/docs/misc/rewriteguide.html). La syntaxe est simple : indiquer avec ErrorDocument suivi du numéro d'erreur comment traiter cette dernière.

En ce qui concerne les ID, le fichier gère ici les plus courantes : 403 (accès interdit) 404 (fichier non trouvé) 500 (erreur interne du serveur). D'autres codes peuvent être gérés : il suffit d'ajouter la ligne correspondante. Suit la chaîne /erreur.php?id=404 (ne pas oublier le slash) qui transmet au fichier erreur.php le numéro d'erreur. Reportez-vous à httpd.apache.org httpd.apache.org/docs-2.0/mod/core.html#errordocument pour d'autres erreurs/syntaxes ou voir la page de webrankinfo www.webrankinfo.com/outils/faq_8_61.htm pour une liste de codes.

Fichier .htaccess

ErrorDocument 403 /erreur.php?id=403
ErrorDocument 404 /erreur.php?id=404
ErrorDocument 500 /erreur.php?id=500

Le fichier erreur.php est déjà plus sioux ! Il consiste à traiter l'erreur (quel que soit son numéro, juste pour info) en fonction de divers paramètres, afin de présenter une page à l'utilisateur malheureux (ou aux robots) tout en envoyant un email au webmestre via la fonction mail() fr2.php.net/manual/fr/ref.mail.php pour l'avertir du problème. Le code est, j'espère, suffisamment commenté et vous trouverez les deux fichiers dans une archive zip.

Fichier erreur.php

<?php
/* ---------------------------------------------
variables à modifier selon paramètres et besoins
--------------------------------------------- */
# nom de domaine (ex. yabon.com)
$hostndd = "yabon.com";
# fichier de redirection (ex. plan.php) vide = index
$rep_fichier = "plan.php";
# identifiant de l'email destinataire (ex. pierre)
# donc le nom placé avant l'arobas
$maildest = "pierre"; 
# nom de domaine si le destinataire du mail n'est pas
# le webmaster du nom de domaine ci-dessus (option)
$hostndd_bis = ""; 
# initialisation de l'envoi du mail
# vraie par défaut (false pour Free)
$envoi_email = true; 
# URL cible pour les robots si aucune occurrence
# n'est trouvée (vide = index)
$renvoiRobots = ""; 
# tableau des robots surveillés (option)
# sinon écrire : $robots = "";
$robots = array("avantbrowser","almaden","archiver"); 
/*
tableau des occurrences posant un problème (anciennes pages ou renommées)
Syntaxe = "item" => "cible" (par défaut : "item" => $rep_fichier,)
La boucle s'arrête au 1er item trouvé, il faut donc gérer les priorités :
l'occurrence "ppan.htm" est avant ".htm"
*/
$redirection = array(
	"ppan.htm" => "ppan.php", 
	".htm" => $rep_fichier, 
	"portail" => "internet/",
	"perso" => "private/?id=4", 
	"job" => "cv/moncv.php#fonction", 
	"form-bin" => $rep_fichier, 
);
/* ------------------
fin des modifications
------------------ */

/*
simplification des variables serveur
*/
$iderreur	= $_GET['id']; # type d'erreur (via .htaccess)
$agent_http	= $_SERVER['HTTP_USER_AGENT']; # type de navigateur
$reference	= $_SERVER['HTTP_REFERER']; # URL de référence
$url_demandee	= $_SERVER['REQUEST_URI']; # page demandée

/*
on vérifie la correspondance avec une des entrées du tableau
à la fois dans la page demandée et l'URL de référence
cette dernière est souvent vide (non transmise)
*/
foreach($redirection as $key => $value) {
	if(stristr($reference,$key) || stristr($url_demandee,$key)) {
		$rep_fichier = $value; # on trouve
		$envoi_email = false; # envoi d'email inutile
		break; # on sort de la boucle
	}
}

/*
si aucune occurrence trouvée précédemment
on redirige les robots identifiés dans nos logs
*/
if($envoi_email && is_array($robots)) {
	foreach($robots as $key) {
		if(stristr($agent_http,$key)) {
			$rep_fichier = $renvoiRobots;
			$envoi_email = false;
			break;
		}
	}
}

/*
composition de l'URL cible, email destinataire et objet
*/
$destination = "http://www.".$hostndd."/".$rep_fichier;
$email = $maildest."@".$hostndd;
if(!empty($hostndd_bis))
	$email = $maildest."@".$hostndd_bis;
$objet = "[".$hostndd."] Erreur ".$iderreur;

/*
si l'envoi du mail n'est pas annulé (via les deux boucles)
*/
if( $envoi_email ) {
	$message  = "Le ".date("d m Y à H:i");
	$message .= "\nDestination : $destination";
	$message .= "\nDemandé : $url_demandee";
	$message .= "\nIP : ".$_SERVER['REMOTE_ADDR']; # IP distant
	$message .= "\nNavigateur : $agent_http";
	# HTTP_REFERER n'est pas toujours transmis
	if(!empty($reference))
		$message .= "\nRéférant : $reference";
	@mail($email, $objet, $message);
}

/*
Affichage de la page HTML
Eviter l'en-tête de redirection auto :
<meta http-equiv="refresh" content="0;URL=<? echo $destination?>">
et préférer la variante header()
*/
?>

<html>
<head>
<meta name="robots" content="noindex, follow">
<title>Erreur | Error <? echo $iderreur?></title>
</head>
<body style="font: 12px sans-serif; text-align: center">
<b>Erreur d'aiguillage !</b><br>
Si vous lisez ce texte <a href="<? echo $destination?>">cliquez ici</a><br>
Demandé : <? echo $url_demandee?><br>Renvoi : <? echo $destination?>
</body>
</html>

Variante pour erreur.php

Ajouter un traitement d'erreur immédiat, sans affichage intermédiaire avec ou sans envoi d'email (selon emplacement). Il suffit d'utiliser la fonction PHP header() fr2.php.net/manual/fr/function.header.php qui opère une redirection (302) . Il faut alors être certain de l'URL cible, sans quoi une boucle serait fatale ! Mieux vaut utiliser la réécriture httpd.apache.org/docs/misc/rewriteguide.html d'URL via le fichier .htaccess si celle-ci est rendue disponible par votre hébergeur.

Variante

/*
Ajouter ce code après les variables à modifier
*/
# items pour redirection immédiate (vide = inopérant)
# on utilise les expressions régulières, donc diverses
# occurrences entre parenthèses, séparées par |
# préférer le traitement via .htaccess
$reg_urgent = "/favicon|msoffice|_vti_bin|form-bin/i"; 

# URL cible absolument valide et si possible absolue
$url_urgent = "http://www.yabon.com/perdu.php"; 

# si $reg_urgent et $url_urgent sont renseignées,
# si une des occurrences dans $reg_urgent est identifiée
# redirection immédiate (en-tête HTTP)
if($reg_urgent && $url_urgent && preg_match($reg_urgent, $url_demandee))
	@header("Location: $url_urgent");

Notes

Il est ainsi facile de gérer les erreurs les plus évidentes et d'en être averti. Bien évidemment, on grossira régulièrement les deux tableaux d'occurrences "surveillées" (ainsi que les robots), afin d'optimiser le flux... et ne pas reçevoir trop d'emails ! C'est également un façon rapide de connaître la popularité de certaines anciennes pages.

On aurait pu agrémenter la page de fioritures graphiques, mais dans la mesure où le visiteur est déjà échaudé par une erreur, inutile d'alourdir cette page, à mon sens (pensez aux connexions RTC).

http://dev.ppan.net [Haut de page] [Document mis à jour le 11.06.2010]