La gestion des changements d’adresse des pages d’un site Web est quelque chose d’important dans la vie d’un site.
Pour cela, il existe par exemple un système de redirection HTTP sous Apache, très utilisé, mais celui-ci présente plusieurs inconvénients comme le fait de ne pas être portable sous d’autres serveurs Web comme IIS, d’alourdir rapidement un site, et d’être difficilement testable ou déplaçable sur un serveur avec une adresse ou une racine différente (par exemple un serveur de développement).
Dans le cas du serveur Microsoft IIS, la gestion des redirections peut être fastidieuse, et ne pas être forcément disponible dans le cas d’un serveur mutualisé.
En réponse à cela, je propose sur cette page un système de gestion des redirections basé sur les pages d’erreur 404 personnalisées. C’est-à-dire que le système de redirection n’est activé que lorsqu’une ressource n’a pas été trouvée, ce qui supporte une montée en charge supérieure. Le système est compatible avec les serveurs Web Apache, Microsoft IIS, et d’autres. Une implémentation est déjà disponible en PHP, et en ASP.NET. Enfin, le portage dans un autre langage est simple (JSP est prévu).
➟ Télécharger le module pour PHP
➟ Télécharger le module pour ASP.NET
Lorsqu’une page n’est pas trouvée, plutôt que d’afficher tout de suite une page d’erreur 404, un script va chercher dans une liste de règles si une redirection est disponible. Si une telle règle est disponible, une redirection HTTP sera envoyée au client ; sinon, une page d’erreur 404 sera affichée.
En admettant que vous placiez le script de redirection dans un sous-dossier /erreurs/ de votre site Web (recommandé), celui-ci va par défaut chercher les règles de redirection dans /erreurs/404//404.txt, c’est à dire dans les fichiers 404.txt situés dans le dossier /erreurs/404/ ou ses sous-répertoires, comme l’illustre l’arborescence suivante :
Les noms des dossiers et sous-dossiers doivent correspondre à des dossiers du site actuel ou à des anciens dossiers à rediriger.
La profondeur et la largeur de l’arborescence ne sont pas limitées.
Faire des sous-répertoires n’est pas obligatoire et toutes les règles de redirection peuvent être dans /erreurs/404/404.txt.
Mais cette possibilité d’arborescence permet d’éviter d’avoir un seul gros fichier 404.txt, permet de mieux structurer des règles ensemble,
et permet d’augmenter les performances en évitant d’avoir trop de règles à lire.
Un sous-répertoire est typiquement créé lorsqu’il y a plusieurs règles de redirection le concernant.
Dans le nom des dossiers, les caractères spéciaux non-acceptables dans une URL (en gros tout ce qui n’est pas a-z_.0-9-
) doivent être %-encodés.
En particulier, une espace sera encodée %20 (voir exemple ci-dessus : dossier%20avec%20espaces/).
Attention : le script de redirection est configuré par défaut pour être dans un sous-dossier (niveau 1) du site Web, tel /erreurs/ comme dans l’exemple ci-dessus. Ceci est configurable dans la constante distanceToRoot en début de script.
Lorsqu’un sous-dossier existe (ce qui n’est pas obligatoire), toutes les règles de redirection le concernant doivent se trouver dans son
fichier 404.txt.
Au final, pour une requête de redirection donnée, un seul fichier 404.txt sera lu :
celui le plus précis possible dans l’arborescence de /erreurs/404//.
La syntaxe est expliquée juste après. Considérez déjà la règle de redirection suivante :
404.txt permanent /ancien-dossier/sous-dossier/fichier\.html /nouveau-dossier/fichier.html
Cette règle pourra être placée dans le fichier /erreurs/404/404.txt, ou bien /erreurs/404/ancien-dossier/404.txt, ou encore /erreurs/404/ancien-dossier/sous-dossier/404.txt. Mais si le dossier /erreurs/404/ancien-dossier/ existe, la règle ne sera pas lue si elle se trouve dans le dossier parent /erreurs/404/404.txt.
Notez que par défaut, et contrairement aux fichiers .htaccess de Apache, les fichiers 404.txt ne sont pas mélangés au reste du site (ils sont dans /erreurs/404/), et cela pour éviter d’avoir à conserver d’anciens dossiers (ou d’avoir un gros fichier à la racine). Néanmoins, vous pouvez personnaliser l’endroit où les fichiers 404.txt sont cherchés grâce à la variable path404 en début de script.
La syntaxe d’une règle de redirection est la suivante (une règle par ligne, et les trois parties de la règle sont séparées par des tabulations ou des espaces) :
404.txt <type de redirection> <ancienne adresse en expression régulière> <nouvelle adresse>
La syntaxe utilisée pour les règles de redirection est proche de celle utilisée par l’instruction RedirectMatch
de Apache ;
voici les différences majeures :
RedirectMatch
(c’est-à-dire à base d’expressions régulières), et non simplement Redirect
.^
du début et le $
de la fin du motif sont sous-entendus et ne doivent pas être écrits..*
en début et/ou en fin d’expression.Les différents types de redirection pris en charge sont :
permanent
HTTP/1.1 301 Moved Permanently
)temp
ou found
HTTP/1.1 302 Found
)seeother
HTTP/1.1 303 See Other
)temporary
HTTP/1.1 307 Temporary Redirect
)gone
HTTP/1.1 410 Gone
)Les anciennes adresses sont écrites sous formes d’expressions régulières. Si vous n’êtes pas familier avec cette notation, mettez simplement l’ancienne adresse en prenant soin de traiter certains caractères spéciaux comme suit :
/:a-z_.0-9-
) doivent être %-encodés.
\
.
^
du début et le $
de la fin du motif sont sous-entendus et ne doivent pas être écrits.
Les anciennes adresses sont entendues depuis la racine du site Web ; c’est-à-dire qu’elles ne contiennent pas http:// ou le nom du serveur.
Par exemple, pour rediriger http://example.net/ancien-dossier/, l’ancienne adresse à écrire sera /ancien-dossier/ seulement.
En particulier, si un site Web n’est pas à la racine de son serveur Web, comme http://example.net/~monSiteWeb/,
les redirections sont entendues par rapport à la racine du site Web et non depuis la racine du serveur
(ceci est configurable dans la constante distanceToRoot en début de script).
Par exemple, pour rediriger http://example.net/~monSiteWeb/ancien-dossier/, l’ancienne adresse à écrire sera /ancien-dossier/ seulement.
L’ancienne adresse peut optionnellement être abrégée en fonction de l’adresse du fichier 404.txt (comme par exemple abréger /ancien-dossier/fichier\.html en fichier\.html si l’instruction se trouve dans /erreurs/404/ancien-dossier/404.txt). Cependant, la syntaxe complète relative à la racine est recommandée pour sa flexibilité et sa robustesse.
Les nouvelles adresses ne sont pas des expressions régulières ; il ne faut donc pas utiliser d’anti-slash.
On peut utiliser $1
, $2
etc. pour référencer une capture ()
provenant de l’expression régulière de l’ancienne adresse.
Les adresses de redirection peuvent être absolues (http://example.net/nouveau-dossier/fichier.html), ou aussi relatives à la racine du site (/nouveau-dossier/fichier.html) [recommandé], ou relatives à l’ancien emplacement (nouveau-fichier.html)
/erreurs/404/404.txt #Redirige un fichier précis permanent /ancien-dossier/sous-dossier/fichier\.html /nouveau-dossier/fichier.html #Même exemple, avec un nom de répertoire et de fichier contenant une espace (codée %20) permanent /ancien%20dossier/mon%20fichier\.html /nouveau%20dossier/mon%20fichier.html #Redirige un site complet permanent /(.*) http://nouveau-site.fr/$1 #Redirige uniquement la racine du site permanent / /dossier/ #Redirige un répertoire et tous ses sous-dossiers et fichiers permanent /ancien-dossier/sous-dossier2/(.*) /nouveau-dossier2/$1 #Même effet, en permettant aussi la redirection lorsque le / final est omis permanent /ancien-dossier/sous-dossier2(.*) /nouveau-dossier2$1 #Redirige un répertoire mais pas ses sous-dossiers ni fichiers permanent /ancien-dossier/sous-dossier2/ /nouveau-dossier2/ #Redirige un répertoire et tous ses fichiers mais pas ses sous-dossiers permanent /ancien-dossier/sous-dossier2/([^/\\]*) /nouveau-dossier2/$1 #Redirige en masse des fichiers correspondant à un motif donné, #et réutilise une partie de leur nom (avec $n) pour former la nouvelle adresse permanent /images/image([0-9]+)\.(gif|jpg) /images/image$1.png #Même effet, syntaxe de destination allégée permanent /images/image([0-9]+)\.(gif|jpg) image$1.png #Redirection temporaire #(l’adresse d’origine doit continuer à être celle utilisée et référencée) temp /raccourci /longue-adresse/plus-compliquee/ #Indique qu’une ressource n’est plus disponible gone /dossier/ancien\.pdf
Un certain nombre d’options sont paramétrables sous forme de constantes en début de script :
distanceToRoot = 1
path404 = "./404/"
customRedirect = "/"
customRedirectTimeOut = 5
defaultNewServer = ""
"http://example.net:80"
allowASPmode = true
Sous Apache, l’ajout de cette page d’erreur personnalisée se fait via la directive ErrorDocument
,
qui peut en particulier être utilisée dans le fichier de configuration général ./conf/httpd.conf
dans le répertoire d’installation de Apache,
ou dans un fichier /.htaccess à la racine du site Web concerné, comme illustré ci-dessous :
/.htaccess ErrorDocument 404 /erreurs/redirection-404.php
Les redirections apparaissent dans les logs d’accès Apache, (codes 301, 302, 410…), et dans les logs d’erreur :
access.log 127.0.0.1 - - [05/Jan/2008:18:50:39 +0100] "GET /ancien-dossier/ HTTP/1.1" 301 651 "-" "Mozilla" 127.0.0.1 - - [05/Jan/2008:18:50:40 +0100] "GET /nouveau-dossier/ HTTP/1.1" 200 8397 "-" "Mozilla"
error.log [Sat Jan 05 18:50:39 2008] [error] [client 127.0.0.1] File does not exist: E:/www/ancien-dossier
(Facultatif et rare): Dans le cas où vous ne voulez ou ne pouvez pas traiter les redirections sur un serveur donné, la méthode suivante permet de déléguer cette tâche à un autre serveur, après avoir activé cette possibilité dans la variable allowASPmode, au début du script de redirection.
/.htaccess <IfModule mod_rewrite.c> RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ http://example.net/erreurs/redirection-404.php?aspxerrorpath=$1 [R=301,L] </IfModule>
Dans IIS, l’ajout de cette page d’erreur personnalisée se fait depuis sa console d’administration (accessible entre autres depuis les outils d’administration, “Gestion de l’ordinateur”, ou “Gestionnaire des services Internet”).
La configuration se fait ensuite dans les propriétés du site Web, dans l’onglet “Messages d’erreur personnalisés” (IIS 5 & 6) ou à partir de l’icone “Pages d’erreurs” (IIS 7). Après avoir sélectionné la ligne du code d’erreur 404, cliquer sur le bouton ou le lien “Modifier”, puis entrer une adresse de type URL indiquant le chemin du script redirection-404 à partir de la racine du site Web comme par exemple /erreurs/redirection-404.aspx ou /erreurs/redirection-404.php.
Dans IIS 7 (voir capture d’écran ci-dessus), le lien “Modifier les paramètres de fonction…” (à droite) permet entre autres de spécifier si l’on souhaite utiliser cette page d’erreur personnalisée y compris lorsqu’on se connecte en local (localhost), ce qui est désactivé par défaut.
Dans IIS 6 et plus récent, la configuration en mode graphique ci-dessus peut aussi s’effectuer manuellement, avec la directive httpErrors comme suit :
/Web.config <configuration> <system.webServer> <httpErrors errorMode="Custom"><!-- Pour les fichiers non-managés --> <remove statusCode="404" subStatusCode="-1" /> <error statusCode="404" path="/erreurs/redirection-404.aspx" responseMode="ExecuteURL" /> </httpErrors> </system.webServer> … </configuration>
Notez que pour avoir le droit de mettre cette configuration au niveau local dans un Web.config à la racine de votre site, plutôt que dans le applicationHost.config global, il faut que la ligne suivante soit activée dans applicationHost.config :
%windir%\System32\inetsrv\config\applicationHost.config <section name="httpErrors" overrideModeDefault="Allow" />
La gestion des redirections pour les fichier .aspx nécessite une configuration supplémentaire.
Jusqu’à IIS 6, il y avait la possibilité de vérifier l’existence des fichiers .aspx avant de les envoyer au moteur ASP.NET, dans [Propriétés / Répertoire de base / Configuration de l’application / Mappages / .aspx / Modifier] (capture d’écran de IIS 5) :
Si IIS 7.5+ est utilisé, ou si l’option ci-dessus ne peut pas être employée, alors une directive additionnelle customErrors doit être utilisée pour les fichiers ASP.NET comme .aspx. De plus, le script de redirection doit avoir la variable allowASPmode activée au début du script de redirection.
/Web.config <configuration> … <system.web> <customErrors mode="On" redirectMode="ResponseRewrite"> <error statusCode="404" redirect="/erreurs/redirection-404.aspx" /> </customErrors> </system.web> </configuration>
Notez que l’attribut redirectMode avec la valeur ResponseRewrite supprime la problématique redirection 302 intermédiaire par défaut, mais il n’est disponible que depuis ASP.NET 3.5 SP1 et IIS 7.5 (Windows 7).
Si vous ne pouvez pas utiliser les méthodes ci-dessus pour assurer la redirection des fichiers .aspx alors il reste la possibilité d’écrire un gestionnaire d’erreur avec un module personnalisé, qui a l’avantage de marcher avec IIS 6 et 7.0+ :
/App_Code/Redirection404Helper.cs using System; using System.Web; /// <summary>Module personnalisé assistant pour les erreurs 404. 2008-10-20</summary> /// <see cref="https://alexandre.alapetite.fr/doc-alex/redirection-404/"/> public class Redirection404Helper : IHttpModule { public void Dispose() {} public void Init(HttpApplication context) { context.Error += new EventHandler(On404Error); } void On404Error(object sender, EventArgs e) { HttpApplication httpApplication = sender as HttpApplication; if (httpApplication == null) return; HttpContext httpContext = httpApplication.Context; HttpException httpException = httpContext.Error as HttpException; if (httpException == null) return; if (httpException.GetHttpCode() == 404) httpContext.Server.Transfer(httpContext.Request.ApplicationPath + @"/erreurs/Redirection-404.aspx?aspxerrorpath=" + httpContext.Request.RawUrl); } }
Placez le fichier C# ci-dessus dans le répertoire /App_Code/ de votre site Web (en prenant soin de vérifier l’adresse du script de redirection à la dernière ligne du module), puis référencez-le comme suit (peut aussi être configuré graphiquement via l’icône “Modules”, puis lien “Ajouter un module managé…”) :
/Web.config <configuration> <system.webServer> <httpErrors errorMode="Custom"><!-- Pour les fichiers non-managés --> <remove statusCode="404" subStatusCode="-1" /> <error statusCode="404" path="/erreurs/redirection-404.aspx" responseMode="ExecuteURL" /> </httpErrors> <!-- Pour pouvoir avoir les sections IIS 6 et 7+ en même temps --> <validation validateIntegratedModeConfiguration="false" /> <modules><!-- Pour IIS 7+ --> <add name="Redirection404Helper" type="Redirection404Helper" preCondition="managedHandler" /> </modules> </system.webServer> <system.web> <httpModules><!-- Pour IIS 6 --> <add name="Redirection404Helper" type="Redirection404Helper" /> </httpModules> </system.web> </configuration>
En construction…
./conf/web.xml <error-page> <error-code>404</error-code> <location>/erreurs/redirection-404.jsp</location> </error-page>
redirection-404.php <?php /* Script PHP pour gérer les redirections HTTP. À utiliser en tant que page d’erreur 404 personnalisée. https://alexandre.alapetite.fr/doc-alex/redirection-404/ */ //--<Constantes>-- //Nombre de niveaux depuis la racine du site Web jusqu’à ce script, pour la gestion automatique des sites qui sont dans un sous-dossier du serveur Web. //Mettre à -1 pour désactiver cette fonctionnalité //Par exemple, mettre à 1 si ce script se trouve dans http://example.com/erreurs/ ou http://example.net/~monSiteWeb/erreurs/ $distanceToRoot=1; //Racine du répertoire contenant les redirections (fichiers 404.txt) $path404=(empty($_SERVER['SCRIPT_FILENAME']) ? '404/' : dirname($_SERVER['SCRIPT_FILENAME'])).'/404/'; $customRedirect='/'; //Pour spécifier une redirection HTML optionnelle à suivre après l’affichage du message d’erreur $customRedirectTimeOut=5; //Délais en secondes avant de suivre la redirection optionnelle ci-dessus $defaultNewServer=''; //Change le serveur pour les redirections utilisant des adresses relatives. Par exemple : 'http://example.net:80' $allowASPmode=true; //Permet aux anciennes adresses d’être passées en paramètre (pour ASP.NET ou pour recevoir des erreurs 404 d’un autre serveur //--</Constantes>-- //Ancienne adresse $oldUrl=''; if (!empty($_SERVER['REQUEST_URI'])) $oldUrl=substr($_SERVER['REQUEST_URI'],0,1024); //Apache, IIS6 elseif (!empty($_SERVER['QUERY_STRING'])) $oldUrl=substr($_SERVER['QUERY_STRING'],0,1024); //IIS5 else $oldUrl='/_unknown_'; if (($sc=strpos($oldUrl,';'))!==false) $oldUrl=trim(substr($oldUrl,++$sc)); //IIS $oldUrlParsed=strpos($oldUrl,'://')===false ? @parse_url($oldUrl) : ''; if (empty($oldUrlParsed)) { $oldUrl='/_unknown_'; $oldUrlParsed=parse_url($oldUrl); } if ($allowASPmode&&(!empty($oldUrlParsed['query']))) //Spécial ASP.NET { parse_str($oldUrlParsed['query'],$oldquery); if (!empty($oldquery['aspxerrorpath'])) $oldUrlParsed=parse_url($oldquery['aspxerrorpath']); } $oldPath=$oldUrlParsed['path']; $siteRoot=''; //Pour le cas où le site Web n’est pas à la racine du serveur Web, comme http://example.net/~monSiteWeb/ if (($distanceToRoot>=0)&&(!empty($_SERVER['SCRIPT_NAME']))) { $map404=$_SERVER['SCRIPT_NAME']; if (substr($map404,-1)!=='/') $map404=dirname($map404); $map404=trim($map404,'/\\'); $dirs=explode('/',$map404); $nbSubLevels=count($dirs)-$distanceToRoot; for ($i=0;$i<$nbSubLevels;$i++) $siteRoot.='/'.$dirs[$i]; if (!empty($siteRoot)) { if (strcasecmp(substr($oldPath,0,strlen($siteRoot)),$siteRoot)===0) $oldPath=substr($oldPath,strlen($siteRoot)); if ((!empty($customRedirect))&&($customRedirect[0]==='/')) $customRedirect=$siteRoot.$customRedirect; } } //Cherche le meilleur fichier 404.txt $absolute='/'; $dirs=explode('/',$oldPath); //Pas de urldecode(), donc les caractères spéciaux doivent être %-encodés : ./404/Bonjour%20Monde/404.txt foreach ($dirs as $dir) if (strlen($dir)>0) { if (($dir[0]!=='.')&&is_dir($path404.$dir)) { $path404.=$dir.'/'; $absolute.=$dir.'/'; } else break; } $path404.='404.txt'; //Cherche dans le fichier 404.txt la première correspondance pour $oldPath $newPath=''; $httpStatus=302; $found=false; if (is_file($path404)&&($handle=@fopen($path404,'r'))) { while (!feof($handle)) { $line=trim(fgets($handle,4096)); if ((strlen($line)<3)||($line[0]=='#')) continue; //Commentaire ou invalide $map=preg_split('"\s+"',$line,4); if (count($map)<2) continue; //invalide $mapOld=$map[1]; if ($mapOld[0]!='/') $mapOld=$absolute.$mapOld; if (@preg_match('"^'.$mapOld.'$"iD',$oldPath)&& ((($status=$map[0])==='gone')|| ((count($map)>2)&& (strlen($newPath=@preg_replace('"^'.$mapOld.'$"iD',$map[2],$oldPath))>0)))) { switch ($status) { case 'permanent': $httpStatus=301; break; case 'found': case 'temp': $httpStatus=302; break; case 'seeother': $httpStatus=303; break; case 'temporary': $httpStatus=307; break; case 'gone': $httpStatus=410; break; default: continue 2; } $found=true; if ($httpStatus!==410) { if (!empty($siteRoot)) $newPath=$siteRoot.$newPath; if (!preg_match('"^(?:(?:[a-z]{3,6}:)|(?:\.\./))"i',$newPath)) //Pas de protocole URI, et pas de ../ au début {//Quand c’est possible et que ce n’est pas déjà le cas, transforme en une URL absolue if (empty($defaultNewServer)&&isset($_SERVER['HTTP_HOST'])) { $defaultNewServer=(empty($_SERVER['HTTPS'])?'http':'https').'://'.$_SERVER['HTTP_HOST']; if (!empty($_SERVER['SERVER_PORT'])) { if (empty($_SERVER['HTTPS'])) { if ($_SERVER['SERVER_PORT']!='80') $defaultNewServer.=':'.$_SERVER['SERVER_PORT']; } elseif ($_SERVER['SERVER_PORT']!='443') $defaultNewServer.=':'.$_SERVER['SERVER_PORT']; } if (empty($newPath)||($newPath[0]!=='/')) //adresse relative $newPath=rtrim(substr($oldPath,-1)==='/' ? $oldPath : dirname($oldPath),'/\\').'/'.$newPath; } $newPath=$defaultNewServer.$newPath; } } break; } } fclose($handle); } if ($found) //Redirection si une nouvelle adresse a été trouvée { if ($httpStatus===410) { header('HTTP/1.1 410 Gone'); header('Status: 410 Gone'); echo '<!DOCTYPE html>'."\n", '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-GB" lang="en-GB">'."\n", '<head>'."\n", '<meta charset="UTF-8" />'."\n", empty($customRedirect) ? '' : '<meta http-equiv="Refresh" content="'.$customRedirectTimeOut.'; url='.$customRedirect.'" />'."\n", '<title>410 Gone</title>'."\n", '<meta name="robots" content="noindex,follow" />'."\n", '</head>'."\n", '<body>'."\n", '<h1>Gone</h1>'."\n", '<p>The requested resource <kbd>'.$oldPath.'</kbd> is no longer available on this server and there is no forwarding address. ', 'Please remove all references to this resource.</p>'."\n", '</body>'."\n", '</html>'."\n"; } else { if (isset($oldUrlParsed['query'])) $newPath.='?'.$oldUrlParsed['query']; $status=array(301=>'Moved Permanently',302=>'Found',303=>'See Other',307=>'Temporary Redirect'); header('Location: '.$newPath); header('HTTP/1.1 '.$httpStatus.' '.$status[$httpStatus]); header('Status: '.$httpStatus.' '.$status[$httpStatus]); echo '<!DOCTYPE html>'."\n", '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-GB" lang="en-GB">'."\n", '<head>'."\n", '<meta charset="UTF-8" />'."\n", '<meta http-equiv="Refresh" content="0; url='.$newPath.'" />'."\n", '<title>'.$httpStatus.' '.$status[$httpStatus].'</title>'."\n", '<meta name="robots" content="noindex,follow" />'."\n", '</head>'."\n", '<body>'."\n", '<h1>'.$status[$httpStatus].'</h1>'."\n", '<p>The document has moved <a href="'.$newPath.'">here</a>.</p>'."\n", '</body>'."\n", '</html>'."\n"; } } else //Message d’erreur 404 { header('HTTP/1.1 404 Not Found'); header('Status: 404 Not Found'); echo '<!DOCTYPE html>'."\n", '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-GB" lang="en-GB">'."\n", '<head>'."\n", '<meta charset="UTF-8" />'."\n", empty($customRedirect) ? '' : '<meta http-equiv="Refresh" content="'.$customRedirectTimeOut.'; url='.$customRedirect.'" />'."\n", '<title>404 Not Found</title>'."\n", '<meta name="robots" content="noindex,follow" />'."\n", '</head>'."\n", '<body>'."\n", '<h1>Not Found</h1>'."\n", '<p>The requested <abbr title="Uniform Resource Locator">URL</abbr> <kbd>'.$oldPath.'</kbd> was not found on this server.</p>'."\n", '</body>'."\n", '</html>'."\n"; } ?>
continue
dans PHP7.3+Redirection-404.aspx <%-- Script ASP.NET pour gérer les redirections HTTP. À utiliser en tant que page d’erreur 404 personnalisée. https://alexandre.alapetite.fr/doc-alex/redirection-404/ --%> <%@ Page Language="C#" %> <%@ Import Namespace="System.IO" %> <script runat="server"> protected void Page_Load(object sender, EventArgs e) { #region --Constantes-- //Nombre de niveaux depuis la racine du site Web jusqu’à ce script, pour la gestion automatique des sites qui sont dans un sous-dossier du serveur Web. //Mettre à -1 pour désactiver cette fonctionnalité //Par exemple, mettre à 1 si ce script se trouve dans http://example.com/erreurs/ ou http://example.net/~monSiteWeb/erreurs/ int distanceToRoot = 1; //Racine du répertoire contenant les redirections (fichiers 404.txt) string path = Path.GetDirectoryName(Server.MapPath(Request.CurrentExecutionFilePath)) + @"/404/"; string customRedirect = @"/"; //Pour spécifier une redirection HTML optionnelle à suivre après l’affichage du message d’erreur int customRedirectTimeOut = 5; //Délais en secondes avant de suivre la redirection optionnelle ci-dessus string defaultNewServer = @""; //Change le serveur pour les redirections utilisant des adresses relatives. Par exemple : 'http://example.net:80' bool allowASPmode = true; //Permet aux anciennes adresses d’être passées en paramètre (pour ASP.NET ou pour recevoir des erreurs 404 d’un autre serveur #endregion #region Ancienne adresse string oldUrl = "http://localhost" + Request.RawUrl; int sc = oldUrl.IndexOf("404;"); if (sc >= 0) oldUrl = oldUrl.Substring(sc + 4).Trim(); //IIS Uri oldUrlParsed = null; try { oldUrlParsed = new Uri(oldUrl); if (allowASPmode && (!String.IsNullOrEmpty(oldUrlParsed.Query))) //Spécial ASP.NET { NameValueCollection oldquery = HttpUtility.ParseQueryString(oldUrlParsed.Query); if (!String.IsNullOrEmpty(oldquery["aspxerrorpath"])) oldUrlParsed = new Uri("http://localhost" + oldquery["aspxerrorpath"]); } } catch { oldUrlParsed = new Uri(@"http://localhost/_unknown_"); } string oldPath = oldUrlParsed.AbsolutePath; string siteRoot = ""; //Pour le cas où le site Web n’est pas à la racine du serveur Web, comme http://example.net/~monSiteWeb/ string[] dirs; if (distanceToRoot >= 0) { string map404 = Request.CurrentExecutionFilePath; if (map404[map404.Length - 1] != '/') map404 = Path.GetDirectoryName(map404); map404 = map404.Trim(new char[] { '/', '\\' }).Replace('\\', '/'); dirs = map404.Split('/'); int nbSubLevels = dirs.Length - distanceToRoot; for (int i = 0; i < nbSubLevels; i++) siteRoot += '/' + dirs[i]; if (!String.IsNullOrEmpty(siteRoot)) { if (oldPath.StartsWith(siteRoot, StringComparison.InvariantCultureIgnoreCase)) oldPath = oldPath.Substring(siteRoot.Length); if ((!String.IsNullOrEmpty(customRedirect)) && (customRedirect[0] == '/')) customRedirect = siteRoot + customRedirect; } } #endregion #region Cherche le meilleur fichier 404.txt string absolute = "/"; dirs = oldPath.Split('/'); //Pas de Server.UrlDecode(), donc les caractères spéciaux doivent être %-encodés : ./404/Bonjour%20Monde/404.txt foreach (string dir in dirs) if (dir.Length > 0) { if ((dir[0] != '.') && Directory.Exists(path + dir)) { path += dir + '/'; absolute += dir + '/'; } else break; } path += "404.txt"; #endregion #region Cherche dans le fichier 404.txt la première correspondance pour oldPath string newPath = ""; int httpStatus = 302; bool found = false; FileInfo fileInfo = new FileInfo(path); StreamReader streamReader = null; try { if (fileInfo.Exists && ((streamReader = fileInfo.OpenText()) != null)) { string line; while ((line = streamReader.ReadLine()) != null) { if (line.Length > 4096) line = line.Substring(0, 4096); line = line.Trim(); if ((line.Length < 3) || (line[0] == '#')) continue; //Commentaire ou invalide string[] map = Regex.Split(line, @"\s+"); if (map.Length < 2) continue; //invalide string status = map[0]; string mapOld = map[1]; if (mapOld[0] != '/') mapOld = absolute + mapOld; if (Regex.Match(oldPath, "^" + mapOld + '$', RegexOptions.IgnoreCase | RegexOptions.CultureInvariant).Success && ((status == "gone") || ((map.Length > 2) && ((newPath = Regex.Replace(oldPath, "^" + mapOld + '$', map[2], RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)).Length > 0)) )) { switch (status) { case "permanent": httpStatus=301; break; case "found": case "temp": httpStatus=302; break; case "seeother": httpStatus=303; break; case "temporary": httpStatus=307; break; case "gone": httpStatus=410; break; default: found = false; break; } found = true; if (httpStatus != 410) { if (!String.IsNullOrEmpty(siteRoot)) newPath = siteRoot + newPath; if (!Regex.Match(newPath, @"^(?:(?:[a-z]{3,6}:)|(?:\.\./))", RegexOptions.IgnoreCase).Success) //Pas de protocole URI, et pas de ../ au début {//Quand c’est possible et que ce n’est pas déjà le cas, transforme en une URL absolue if (defaultNewServer.Length < 8) { defaultNewServer = "http://" + Request.ServerVariables["HTTP_HOST"]; if (String.IsNullOrEmpty(newPath) || (newPath[0] != '/')) //adresse relative newPath = (oldPath[oldPath.Length - 1] == '/' ? oldPath : Path.GetDirectoryName(oldPath).Replace('\\', '/')).TrimEnd(new char[] { '/', '\\' }) + '/' + newPath; } newPath = defaultNewServer + newPath; } } break; } } } } catch (Exception ex) { Trace.Write("Redirection-404", "Error while reading a 404.txt file", ex); } finally { if (streamReader != null) streamReader.Close(); fileInfo = null; } #endregion #region Response if (found) //Redirection si une nouvelle adresse a été trouvée { if (httpStatus == 410) { Response.Status = "410 Gone"; Response.Write(@"<!DOCTYPE html> <html xmlns=""http://www.w3.org/1999/xhtml"" xml:lang=""en-GB"" lang=""en-GB""> <head> <meta charset=""UTF-8"" /> "); if (customRedirect.Length > 0) Response.Write(string.Format("<meta http-equiv=\"Refresh\" content=\"{0}; url={1}\" />\n", customRedirectTimeOut, customRedirect)); Response.Write(@"<title>410 Gone</title> <meta name=""robots"" content=""noindex,follow"" /> </head> <body> <h1>Gone</h1> <p>The requested resource <kbd>" + oldPath + @"</kbd> is no longer available on this server and there is no forwarding address. Please remove all references to this resource.</p> </body> </html> "); } else { if (!String.IsNullOrEmpty(oldUrlParsed.Query)) newPath += oldUrlParsed.Query; Response.RedirectLocation = newPath; switch (httpStatus) { case 301: Response.Status = "301 Moved Permanently"; break; case 302: Response.Status = "302 Found"; break; case 303: Response.Status = "303 See Other"; break; case 307: Response.Status = "307 Temporary Redirect"; break; default: Response.Status = "302 Found"; break; } Response.Write(@"<!DOCTYPE html> <head> <meta charset=""UTF-8"" /> <meta http-equiv=""Refresh"" content=""0; url=" + newPath + @""" /> <title>" + Response.Status + @"</title> <meta name=""robots"" content=""noindex,follow"" /> </head> <body> <h1>" + Response.StatusDescription + @"</h1> <p>The document has moved <a href=""" + newPath + @""">here</a>.</p> </body> </html> "); } } else //Message d’erreur 404 { Response.Status = "404 Not Found"; Response.Write(@"<!DOCTYPE html> <html xmlns=""http://www.w3.org/1999/xhtml"" xml:lang=""en-GB"" lang=""en-GB""> <head> <meta charset=""UTF-8"" /> "); if (customRedirect.Length > 0) Response.Write(string.Format("<meta http-equiv=\"Refresh\" content=\"{0}; url={1}\" />\n", customRedirectTimeOut, customRedirect)); Response.Write(@"<title>404 Not Found</title> <meta name=""robots"" content=""noindex,follow"" /> </head> <body> <h1>Not Found</h1> <p>The requested <abbr title=""Uniform Resource Locator"">URL</abbr> <kbd>" + oldPath + @"</kbd> was not found on this server.</p> </body> </html> "); } #endregion } </script>
Ce contenu est protégé par une licence
Creative Commons Paternité - Partage des Conditions Initiales à l’Identique 2.0 France “BY-SA (FR)”
Si vous utilisez et aimez ce logiciel (surtout pour un usage professionnel), merci de considérer faire un don.
Si vous souhaitez une réponse ou rapporter un problème avec ce script, merci de me contacter par courriel.