Des pages d'erreur personnalisées avec Apache et PHP
URL de référence : http://www.apachefrance.com/Articles/7/

Pourquoi personnaliser ses pages d'erreur ?

"Butiner" sur le Web...j'adore cette expression. Non contente d'être conforme avec les souhaits de l'Académie Française, elle apporte un petit côté buccolique à la Grande Toile. Pourtant, n'y a-t-il rien de moins buccolique qu'au cours d'un butinage en règle, une triste page blanche vous annonce désespérément (et en Anglais) "Error 404 : Page not found" ?
Heureusement, Apache vient à notre secours en proposant des mécanismes simples et efficaces de personnalisation qui permettront d'agrémenter les dites pages d'erreur d'une explication en Français, du logo de la société ou encore du plan du site afin de permettre aux brebis égarées de retrouver le chemin de la bergerie, à savoir le contenu du site.

Le présent article a donc pour but de vous guider dans la mise en place de pages d'erreur personnalisées sur vos sites.
Nous étudierons tout d'abord le mécanisme des codes de statut HTTP, puis comment personnaliser Apache en fonction des codes d'erreur, puis nous terminerons avec un exemple d'intégration avec PHP.

Les codes de statut HTTP

A chaque requête d'un client Web (i.e. le navigateur), le serveur Web doit retourner à ce client un nombre comportant 3 chiffres appelé code de statut HTTP. Un client Web peut ainsi tenter de comprendre la réponse du serveur en examinant le code de statut, celui-ci parvenant via l'en-tête Status-Line. Le code est accompagné d'une courte phrase, nommée raison, qui doit fournir une brêve explication à l'utilisateur. Par exemple, un en-tête HTTP Status-Line peut ressembler à ceci :

HTTP/1.1 404 Not Found

"404" est ici le code d'erreur, et "Not found" la raison. Sur un client Web, la phrase "Not Found" s'affiche dans la fenêtre de navigation.

Il existe cinq classes de codes de statut dans les dernières spécifications HTTP/1.1, dont les codes majeurs sont repris dans le tableau ci-dessous :

  • 1xx : Codes d'information
  • 2xx : Codes de succès
  • 3xx : Codes de redirection
  • 4xx : Code d'erreur client
  • 5xx : Code d'erreur serveur

Retour 1xx : Codes d'information / Information codes
Code Statut / Status Description / Comment
100 Continuer Attente de la suite de la requête.
La partie initiale de la requête a bien été reçue et le client peut continuer avec la suite de cette requête.
Continue Waiting for the end part of the request.
The initial part of the request has been received and the client may continue with its request.
101 Changement de protocoles Le serveur accepte la requête du client de changer de protocole.
Le client a demandé au serveur d'utiliser un autre protocole que celui actuellement utilisé, et le serveur accepte cette requête.
Switching Protocols The server is complying with a client request to switch protocols to the one specified in the Upgrade header field.
The client asked the server to use another protocol than the one actually used, and the server complied this request.
Retour 2xx : Codes de succès / Success codes
Code Statut / Status Description / Comment
200 OK La requête HTTP a été traitée avec succès.
L'information retournée avec la réponse dépend de la méthode utilisée dans la requête. Par exemple la réponse à une requête GET classiquement émise par un navigateur web sera la ressource demandée (c'est-à-dire une page HTML, une image, etc).
OK The HTTP request has succeeded.
The information returned with the response is dependent on the method used in the request. For example the response to a standard GET request issued by a web browser is the requested resource (i.e. an HTML page, an image, etc).
201 Créé La requête a été correctement traitée et a résulté en la création d'une nouvelle ressource.
Cette ressource peut être référencée par l'URI retournée dans le corps de la réponse, avec l'URL la plus précise pour la ressource indiquée dans l'en-tête du champ "Location".
Created The request has been fulfilled and resulted in a new resource being created.
The newly created resource can be referenced by the URI(s) returned in the entity of the response, with the most specific URL for the resource given by a Location header field.
202 Accepté La requête a été acceptée pour être traitée, mais son traitement peut ne pas avoir abouti.
Ce code est utilisé en remplacement du 201 lorsque le traitement ne peut pas avoir lieu immédiatement, son résultat est donc indéterminé.
Accepted The request has been accepted for processing, but the processing has not been completed.
This code is used instead of 201 when the processing of the request cannot be carried out immediately, leaving the result undetermined.
203 Information non certifiée L'information retournée n'a pas été générée par le serveur HTTP mais par une autre source non authentifiée.
Non-Authoritative Information Usually the preliminary information sent from a server to a browser comes directly from the server. If it does not, then this code might also be sent to indicate that information did not come from a known source.
204 Pas de contenu Le serveur HTTP a correctement traité la requête mais il n'y a pas d'information à envoyer en retour.
Cela peut par exemple se produire lorsqu'un fichier HTML ou le résultat d'un programme CGI-BIN est vide.
No Content The request was accepted and filled but no new information is being sent back.
The browser receiving this response should not change its screen display (although new, and changed, private header information may be sent).
205 Contenu réinitialisé Le client doit remettre à zéro le formulaire utilisé dans cette transaction.
Ce code est envoyé au logiciel de navigation quand il doit réinitialiser un formulaire généré dynamiquement par un CGI-BIN, par exemple.
Reset Content The browser should clear the form used for this transaction for additional input.
Appropriate for data-entry CGI applications.
206 Contenu partiel Le serveur retourne une partie seulement de la taille demandée.
Ce code est utilisé lorsqu'une requête spécifiant une taille a été transmise.
Partial Content The server is returning partial data of the size requested.
Used in response to a request specifying a Range header. The server must specify the range included in the response with the Content-Range header.
Retour 3xx : Codes de redirection / Redirection codes
Code Statut / Status Description / Comment
300 Choix multiples L'URI demandée concerne plus d'une ressource.
Par exemple, l'URI concerne un document qui a été traduit en plusieurs langues. Le serveur doit retourner des informations indiquant comment choisir une ressource précise.
Multiple Choices The requested URI refers to more than one resource.
For example, the URI could refer to a document that has been translated into many languages. The entity body returned by the server could have a list of more specific data about how to choose the correct resource.
301 Changement d'adresse définitif La ressource demandée possède une nouvelle adresse (URI).
Toute référence future à cette ressource doit être faite en utilisant l'une des URIs retournées dans la réponse. Le navigateur web doit normalement charger automatiquement la ressource demandée à sa nouvelle adresse.
Moved Permanently The requested resource has been assigned a new permanent address (URI).
Any future references to this resource should be done using one of the returned URIs. Web browsers should automatically load the requested resource using its new address.
302 Changement d'adresse temporaire La ressource demandée réside temporairement à une adresse (URI) différente.
Cette redirection étant temporaire, le navigateur web doit continuer à utiliser l'URI originale pour les requêtes futures.
Moved Temporarily The requested resource resides temporarily under a different URI.
Since the redirection may be altered on occasion, the client should continue to use the Request-URI for future requests.
303 Voir ailleurs L'URI spécifié est disponible à un autre URI et doit être demandé par un GET.
See Other The requested URI can be found at a different URI (specified in the Location header) and should be retrieved by a GET on that resource.
304 Non modifié Le navigateur web a effectué une requête GET conditionnelle et l'accès est autorisé, mais le document n'a pas été modifié.
Cette réponse classique signifie que vous avez configuré votre navigateur pour utiliser un cache HTTP (proxy) dans lequel une copie du document demandé est déjà stockée. Le proxy a donc demandé au serveur si le document original a changé depuis, et a reçu cette réponse : il pourra ainsi utiliser la copie locale.
Not Modified The web browser has performed a conditional GET request and access is allowed, but the document has not been modified.
This classic response means you have configured your web browser to use an HTTP cache (proxy) in which a copy of the requested document is already stored. The cache proxy thus asked the server if the original document was modified, and received this response, so it will use the local copy instead of loading it from the server.
305 Utiliser le proxy L'URI spécifié doit être accédé en passant par le proxy.
Use Proxy The requested URI must be accessed through the proxy in the Location header.
Retour 4xx : Erreur du client / Client Error
Code Statut / Status Description / Comment
400 Mauvaise requête La requête HTTP n'a pas pu être comprise par le serveur en raison d'une syntaxe erronée.
Le problème peut provenir d'un navigateur web trop récent ou d'un serveur HTTP trop ancien.
Bad Request The HTTP request could not be understood by the server due to malformed syntax.
The web browser may be too recent, or the HTTP server may be too old.
401 Non autorisé La requête nécessite une identification de l'utilisateur.
Concrètement, cela signifie que tout ou partie du serveur contacté est protégé par un mot de passe, qu'il faut indiquer au serveur pour pouvoir accéder à son contenu.
Unauthorized The request requires user authentication.
This means that all or a part of the requested server is protected by a password that should be given to the server to allow access to its contents.
402 Paiement exigé Ce code n'est pas encore mis en oeuvre dans le protocole HTTP.
Payment Required This code is not yet implemented in HTTP.
403 Interdit Le serveur HTTP a compris la requête, mais refuse de la traiter.
Ce code est généralement utilisé lorsqu'un serveur ne souhaite pas indiquer pourquoi la requête a été rejetée, ou lorsqu'aucune autre réponse ne correspond (par exemple le serveur est un Intranet et seules les machines du réseau local sont autorisées à se connecter au serveur).
Forbidden The HTTP server understood the request, but is refusing to fulfill it.
This status code is commonly used when the server does not wish to reveal exactly why the request has been refused, or when no other response is applicable (for example the server is an Intranet and only the LAN machines are authorized to connect).
404 Non trouvé Le serveur n'a rien trouvé qui corresponde à l'adresse (URI) demandée.
Cela signifie que l'URL que vous avez tapée ou cliquée est mauvaise ou obsolète et ne correspond à aucun document existant sur le serveur (vous pouvez essayez de supprimer progressivement les composants de l'URL en partant de la fin pour éventuellement retrouver un chemin d'accès existant).
Not Found The server has not found anything matching the requested address (URI).
This means the URL you have typed or cliked on is wrong or obsolete and does not match any document existing on the server (you may try to gradualy remove the URL components from the right to the left to eventualy retrieve an existing path).
405 Méthode non autorisée Ce code indique que la méthode utilisée par le client n'est pas supportée pour cet URI.
Method Not Allowed This code is given with the Allow header and indicates that the method used by the client is not supported for this URI.
406 Aucun disponible L'adresse (URI) spécifiée existe, mais pas dans le format préféré du client.
Le serveur indique en retour le langage et les types d'encodages disponibles pour cette adresse.
Not Acceptable The URI specified by the client exists, but not in a format preferred by the client.
Along with this code, the server provides the Content-Language, Content-Encoding, and Content-Type headers.
407 Authentification proxy exigée Le serveur proxy exige une authentification du client avant de transmettre la requête.
Proxy Authentication Required The proxy server needs to authorize the request before forwarding it.
408 Requête hors-délai Le client n'a pas présenté une requête complète pendant le délai maximal qui lui était imparti, et le serveur a abandonné la connexion.
Request Time-out This response code means the client did not produce a full request within some predetermined time (usually specified in the server's configuration), and the server is disconnecting the network connection.
409 Conflit La requête entre en conflit avec une autre requête ou avec la configuration du serveur.
Des informations sur les raisons de ce conflit doivent être indiquée en retour.
Conflict This code indicates that the request conflicts with another request or with the server's configuration.
Information about the conflict should be returned in the data portion of the reply.
410 Parti L'adresse (URI) demandée n'existe plus et a été définitivement supprimée du serveur.
Gone This code indicates that the requested URI no longer exists and has been permanently removed from the server.
411 Longueur exigée Le serveur a besoin de connaître la taille de cette requête pour pouvoir y répondre.
Length Required The server will not accept the request without a Content-Length header supplied in the request.
412 Précondition échouée Les conditions spécifiées dans la requête ne sont pas remplies.
Precondition Failed The condition specified by one or more If... headers in the request evaluated to false.
413 Corps de requête trop grand Le serveur ne peut traiter la requête car la taille de son contenu est trop importante.
Request Entity Too Large The server will not process the request because its entity body is too large.
414 URI trop long Le serveur ne peut traiter la requête car la taille de l'objet (URI) a retourner est trop importante.
Request-URI Too Long The server will not process the request because its request URI is too large.
415 Format non supporté Le serveur ne peut traiter la requête car son contenu est écrit dans un format non supporté.
Unsupported Media Type The server will not process the request because its entity body is in an unsupported format.
416 Plage demandée invalide Le sous-ensemble de recherche spécifié est invalide.
Requested range unsatifiable The server will not process the request because the requested range is invalid.
417 Comportement erroné Le comportement prévu pour le serveur n'est pas supporté.
Expectation failed The behavior expected fot the server is not supported.
Retour 5xx : Erreur du serveur / Server Error
Code Statut / Status Description / Comment
500 Erreur interne du serveur Le serveur HTTP a rencontré une condition inattendue qui l'a empêché de traiter la requête.
Cette erreur peut par exemple être le résultat d'une mauvaise configuration du serveur, ou d'une ressource épuisée ou refusée au serveur sur la machine hôte.
Internal Server Error The HTTP server encountered an unexpected condition which prevented it from fulfilling the request.
For example this error can be caused by a serveur misconfiguration, or a resource exhausted or denied to the server on the host machine.
501 Non mis en oeuvre Le serveur HTTP ne supporte pas la fonctionnalité nécessaire pour traiter la requête.
C'est la réponse émise lorsque le serveur ne reconnaît pas la méthode indiquée dans la requête et n'est capable de la mettre en oeuvre pour aucune ressource (soit le navigateur web est trop récent, soit le serveur HTTP est trop ancien).
Not Implemented The HTTP server does not support the functionality required to fulfill the request.
This is the appropriate response when the server does not recognize the request method and is not capable of supporting it for any resource (either the web browser is too recent, or the HTTP server is too old).
502 Mauvais intermédiaire Le serveur intermédiaire a fourni une réponse invalide.
Le serveur HTTP a agi en tant qu'intermédiaire (passerelle ou proxy) avec un autre serveur, et a reçu de ce dernier une réponse invalide en essayant de traiter la requête.
Bad Gateway The gateway server returned an invalid response.
The HTTP server, while acting as a gateway or proxy, received an invalid response from the upstream server it accessed in attempting to fulfill the request.
503 Service indisponible Le serveur HTTP est actuellement incapable de traiter la requête en raison d'une surcharge temporaire ou d'une opération de maintenance.
Cela sous-entend l'existence d'une condition temporaire qui sera levée après un certain délai.
Service Unavailable The HTTP server is currently unable to handle the request due to a temporary overloading or maintenance of the server.
The implication is that this is a temporary condition which will be alleviated after some delay.
504 Intermédiaire hors-délai Cette réponse est identique au code 408 (requête hors-délai), mais ici c'est un proxy ou un autre intermédiaire qui a mis trop longtemps à répondre.
Gateway Time-out This response is like 408 (Request Time-out) except that a gateway or proxy has timed out.
505 Version HTTP non supportée La version du protocole HTTP utilisée dans cette requête n'est pas (ou plus) supportée par le serveur.
HTTP Version not supported The server will not support the HTTP protocol version used in the request.

Les codes de statut HTTP sont définis dans le RFC 2616, chapitre 10, consultable ici.
Nous allons maintenant étudier comment définir des pages personnalisées attachées aux codes de retour.

Configuration d'Apache

Lorsque la requête HTTP du client Web est considérée comme une erreur par Apache, ou lorsqu'un problème survient suite à l'exécution de cette requête, Apache peut réagir de 4 façons différentes :

  • afficher le message d'erreur standard
  • afficher un message d'erreur personnalisé
  • rediriger l'utilisateur vers une URL locale afin de traîter le problème ou l'erreur
  • rediriger l'utilisateur vers une URL externe afin de traîter le problème ou l'erreur

Le premier comportement est celui par défaut d'Apache. Les autres comportements seront possibles au travers de la directive ErrorDocument.

La directive ErrorDocument

Celle-ci peut être défini au niveau du fichier httpd.conf (serveur principal, hôtes virtuels, répertoires), mais, et c'est cette possibilité qui intéressera la plupart d'entre vous, dans un fichier .htaccess.
Sa syntaxe est : ErrorDocument Code_d'Erreur message|URL

Afficher un message d'erreur personnalisé

Pour afficher un message d'erreur personnalisé, il suffit de le saisir au niveau du paramètre message, précédé d'un guillemet (symbole "). Ce guillement ne figurera pas dans le message affiché. Apache ajoutera également d'autres informations explicitant l'errreur.

Exemple
ErrorDocument 404 "Erreur 404 : La page n'existe pas !

Rediriger l'utilisateur vers une URL locale/externe

L'unique différence entre la redirection vers une URL locale ou externe réside dans la façon dont l'URL est écrite. Faire commencer l'URL par "/" indiquera une URL locale, alors que pour une URL externe, on fera précéder le chemin vers le document ou le script par "http://" et le nom du serveur.

Exemples
ErrorDocument 404 /404.html
ErrorDocument 404 /404.php
ErrorDocument 500 /cgi-bin/errorHandler.pl
ErrorDocument 401 http://geldenhu.free.fr/401.html
ErrorDocument 404 http://geldenhu.free.fr/404.php
ErrorDocument 403 http://geldenhu.free.fr/erreur.php

Attention toutefois que, lors de l'utilisation d'une directive ErrorDocument désignant une URL externe (donc commençant par "http://"), Apache émettra une requête de redirection au client (catégorie 3xx) pour lui indiquer l'emplacement du document. Ceci peut avoir pour effet de perturber les robots ainsi que certains clients particuliers qui vérifient la validité d'une URL en testant le code de retour de la requête.
Ajoutons également qu'en raison de la nature des mécanismes de l'authentification HTTP, l'utilisation de la directive ErrorDocument avec le code 401 (Accès non autorisé), nécessite de ne pas utiliser d'URL externe. Dans le cas contraire, suite au code de redirection émis, le client ne se verra pas présenter la demande d'authentification et les pages seront inaccessibles.

Note pour les utilisateurs d'Internet Explorer

Internet Explorer 5 et supérieur incluent des pages d'erreur standards présentées à l'internaute si le site Web ne propose pas de pages personnalisées OU si la taille de ces pages est inférieure à une certaine taille en octets (256 ou 512 selon le cas).

Pour contourner ce "problème", vous pouvez soit compléter la page d'erreur avec des commentaires HTML afin d'atteindre 512 octets, soit opter pour l'une des deux modifications suivantes dans Internet Explorer :
• Dans les options d'Internet Explorer, onglet "Avancés", décocher la case "Afficher des messages d'erreur HTTP simplifiés",
• Dans la base de registre, rechercher le dossier HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\Main\ErrorThresholds. Vous trouverez alors plusieurs entrées correspondant à un code HTTP et la taille de page minimum attendue pour afficher la page du site plutôt que la page interne d'IE.

Référence : Description des messages d'erreur HTTP, Microsoft.

Plus loin dans la personnalisation...

Comme nous venons de l'évoquer, l'URL peut désigner aussi bien une page statique qu'un script "côté serveur" écrit avec PHP ou encore un CGI écrit en Perl. Cependant, cette possibilité sera d'autant plus intéressante que certaines informations concernant l'environnement et les raisons précises de l'erreur seront disponibles, dans le but de délivrer le message le plus clair possible.
Nous en avons rêvé, Apache l'a fait. Ainsi, les variables d'environnement CGI suivantes seront disponibles :

  • REDIRECT_HTTP_ACCEPT (type MIME acceptés par le client)
  • REDIRECT_HTTP_USER_AGENT (description du navigateur client)
  • REDIRECT_PATH (chemin de recherche des binaires sur le serveur)
  • REDIRECT_REMOTE_ADDR (adresse IP du client)
  • REDIRECT_REMOTE_HOST (nom qualifié du client si disponible)
  • REDIRECT_SERVER_NAME (nom qualifié ou adresse IP du serveur)
  • REDIRECT_SERVER_PORT (port TCP/IP utilisé par le serveur pour "écouter" les requêtes)
  • REDIRECT_SERVER_SOFTWARE (description du serveur HTTP utilisé)
  • REDIRECT_URL (URL ayant conduit à l'URL actuelle)
  • REDIRECT_QUERY_STRING (paramètres de formulaire passés via l'URL)
  • REDIRECT_STATUS (code HTTP du statut)

Plusieurs éléments clés sont à prendre en compte :

  • toutes les variables sont préfixées par la chaîne "REDIRECT_",
  • ces variables n'existeront que si elles existaient sous leur forme non préfixée préalablement au traitement de l'erreur,
  • les variables REDIRECT_URL et REDIRECT_STATUS seront quant à elles toujours renseignées ; REDIRECT_QUERY_STRING sera vide à moins que des paramètres soient passés au script/CGI via l'URL de redirection,
  • aucune des deux variables précédemment citées ne sera initialisée si le document d'erreur est le résultat d'une redirection externe, c'est-à-dire débutant par "http://". Ceci est valable même si le site cible est le même que le site d'origine de la redirection.

Nous allons maintenant étudier comment utiliser au mieux la directive ErrorDocument et cet environnement CGI au travers de PHP.

Intégration avec PHP

Cette dernière partie décrit une façon de récupérer les principales erreurs et de proposer un affichage personnalisé en s'appuyant sur PHP. Le mécanisme décrit est celui employé sur Apache France.

Paramétrage d'Apache

L'hypothèse de départ est que nous souhaitons disposer des pages d'erreur personnalisées sur l'ensemble du site pour les erreurs les plus communes :

  • 401 : Authentification nécessaire
  • 403 : Accès interdit
  • 404 : La page spécifiée n'existe pas
  • 500 : Erreur interne du serveur

Nous souhaitons également traiter les erreurs dans un script unique, écrit en PHP, que nous appelerons erreur.php.

ErrorDocument 401 /erreur.php
ErrorDocument 403 /erreur.php
ErrorDocument 404 /erreur.php
ErrorDocument 500 /erreur.php

Comme indiqué précédemment lors de la présentation de la directive, les lignes de configuration seront ajoutées soit dans le fichier httpd.conf dans la section du site principal, soit dans le fichier httpd.conf dans la section des hôtes virtuels, pour un site particulier, soit dans un fichier .htaccess qui sera placé à la racine de l'arborescence.
Pour protéger l'accès malencontreux à certaines fichiers (et tester la personnalisation de l'erreur 403), nous allons également désactiver le listing de répertoires via la directive Option et le paramètre -Indexes.

Options -Indexes

Cette directive est à utiliser soit au niveau d'un bloc "Directory" correspondant à la racine de votre arborescence (httpd.conf), soit directement (fichier.htaccess).
Voici par exemple le fichier .htaccess utilisé sur Apache France :

ErrorDocument 401 /erreur.php
ErrorDocument 403 /erreur.php
ErrorDocument 404 /erreur.php
ErrorDocument 500 /erreur.php
Options -Indexes

Certains hébergeurs limitent ou désactivent l'utilisation des fichiers .htaccess. Ainsi, Free.fr autorise un fichier .htaccess avec les directives ErrorDocument, mais les redirections ne sont autorisées que vers des pages statiques.

Présentation du script Erreur.php

Le script Erreur.php est chargé de l'affichage d'un message d'erreur explicit en fonction de l'erreur commise par l'internaute ou du problème rencontré par le serveur.

Vu qu'il n'y a qu'un seul script pour toutes les erreurs, il faudra donc récupérer les variables CGI nécessaires. Nous n'avons besoin que de REDIRECT_STATUS, qui nous permettra de déterminer le code d'erreur. L'autre variable utile, REQUEST_URI (il s'agit bien d'un i majuscule), est propre à PHP et contient l'URL demandée par l'internaute. Par exemple, si ce dernier a demandé la page http://www.monsite.com/toto/titi.html, REQUEST_URI contient "/toto/titi.html".

Nous avons également choisi d'utiliser les super variables globales introduites avec PHP 4.1. Dans ce cas, REQUEST_URI et REDIRECT_STATUS sont disponibles via le tableau associatif $_SERVER. Si vous disposez d'une version antérieure ou que les super variables globales sont désactivées, il suffit de profiter du mécanisme d'instanciation automatique des variables CGI sous forme de variables locales : $REQUEST_URI et $REDIRECT_STATUS.
Nous allons maitenant étudier le script Erreur.php.

Etude du script Erreur.php

Tout d'abord, afin de protéger le script, nous testons l'existence de la variable REDIRECT_STATUS.

if (!isset($_SERVER["REDIRECT_STATUS"])) Header("Location: /");

Si elle existe, nous sommes redirigés vers le script suite à une erreur Apache : tout va bien, l'exécution du script continue. Dans le cas contraire, un internaute fait un appel direct au script : nous effectuons une redirection vers la racine du site via la fonction PHP header().
Après avoir "requis" certains librairies/fichiers, nous allons décrire un tableau associatif associant un message spécifique à un code d'erreur.

    $source_erreur $_SERVER["REQUEST_URI"];
    
$code_erreur $_SERVER["REDIRECT_STATUS"];
    
$cause_erreur['401'] = "Authentification nécessaire";
    
$cause_erreur['403'] = "Accès interdit";
    
$cause_erreur['404'] = "La page spécifiée n'existe pas";
    
$cause_erreur['500'] = "Erreur interne du serveur";
    
$libelle_erreur "Erreur $code_erreur";

Pour plus de facilité, nous récupérons l'URL demandée et le code d'erreur respectivement dans les variables $source_erreur et $code_erreur. Le tableau associatif $cause_erreur permettra de faire le lien.
Après l'affichage de l'en-tête ( showHeader() ), nous écrivons le corps de la page qui sera personnalisé par l'affichage de l'URL à la source de l'erreur, du code d'erreur, et du message d'erreur correspondant.

echo "$source_erreur : $libelle_erreur : $cause_erreur[$code_erreur]";

Enfin, nous affichons le pied de page ( showFooter() ).
Voici le script Erreur.php complet :

<?
    
if (!isset($_SERVER["REDIRECT_STATUS"])) Header("Location: /");
    require_once(
'articles.inc.php');
    
    
$source_erreur $_SERVER["REQUEST_URI"];
    
$code_erreur $_SERVER["REDIRECT_STATUS"];
    
$cause_erreur['401'] = "Authentification nécessaire";
    
$cause_erreur['403'] = "Accès interdit";
    
$cause_erreur['404'] = "La page spécifiée n'existe pas";
    
$cause_erreur['500'] = "Erreur interne du serveur";
    
$libelle_erreur "Erreur $code_erreur";
    
    
showHeader($libelle_erreur,0);
?>
<p><span class=grosTitre><?php echo "$source_erreur : $libelle_erreur : $cause_erreur[$code_erreur]"?></span></p>
<p>Si cette page est pr&eacute;sent&eacute;e suite au clic sur un lien ou un tout
  autre &eacute;l&eacute;ment devant conduire &agrave; une page existante, n'h&eacute;sitez
  pas &agrave; nous en faire part en cliquant <a href="/Contact/">ici</a>, nous
  ferons rapidement le n&eacute;cessaire.<br>
  <br>
  Nous vous remercions pour votre compr&eacute;hension.<br>
  <br>
  Cliquer <a href="/">ici</a> pour retourner &agrave; l'accueil.</p>
<?
    showFooter
(0);
?>

Vous pouvez avoir un aperçu des pages générées ici (erreur 403) ou bien ici (erreur 404).

Conclusion

Vous connaissez désormais les possibilités de personnalisation des pages d'erreur qu'offrent Apache et PHP. Pour toute question ou remarque complémentaire, rendez-vous dans le forum !.

Remarques

La présentation du code HTTP est inspirée du livre Apache Server de M.J.Kabir.
Les codes HTTP sont extraits du site INDEXA.
Les informations pour la personnalisation sont inspirée de la documentation d'Apache.