rewrite_guide_advanced.xml.fr revision 3d2cae5ab1a8737402fe32dfd04128a3a9b83c04
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!-- English Revision : 805049 -->
<!-- French translation : Lucien GENTIS -->
<!-- Reviewed by : Vincent Deffontaines -->
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<manualpage metafile="rewrite_guide_advanced.xml.meta">
<parentdocument href="./">Rewrite</parentdocument>
<title>Guide de réécriture des URLs - Sujets avancés</title>
<summary>
<p>Ce document complémente la
module <module>mod_rewrite</module>. Il décrit les différentes
manières d'utiliser le module d'Apache <module>mod_rewrite</module>
pour résoudre les problèmes d'URLs typiques auxquels sont souvent
confrontés les webmasters. Nous fournissons une description
détaillée de la résolution de chaque problème par la configuration
d'un jeu de règles de réécriture.</p>
<note type="warning">ATTENTION: il pourra s'avérer nécessaire de
modifier les exemples en fonction de la
configuration de votre serveur, par exemple en ajoutant le drapeau
<code>[PT]</code> si les modules <module>mod_alias</module> et
<module>mod_userdir</module> sont utilisés, etc... Les jeux de
règles devront également être adaptés pour passer d'un contexte de
serveur à un contexte de répertoire (fichiers
<code>.htaccess</code>). Essayez de toujours bien comprendre ce que
fait un jeu de règles avant de l'utiliser, ce qui pourra vous éviter
bien des problèmes.</note>
</summary>
module</a></seealso>
mod_rewrite</a></seealso>
utiles</a></seealso>
<section id="cluster">
<title>Accès à une grappe de serveurs via un espace d'adressage
compatible</title>
<dl>
<dt>Description :</dt>
<dd>
<p>Comment créer un espace d'adressage homogène et compatible
avec
tous les serveurs WWW d'une grappe de serveurs d'un intranet ?
C'est à dire que toutes les URLs (par définition
locales à un
serveur et dépendant donc de celui-ci) deviennent
véritablement <em>indépendantes</em> du serveur ! Nous voulons
disposer, pour accéder à l'espace de nommage WWW, d'un seul
espace d'adressage compatible : aucune URL ne
doit inclure d'information quelconque à propos du serveur
cible physique. La grappe de serveurs doit elle-même nous
diriger automatiquement vers le bon serveur cible physique,
selon les besoins, et ceci de manière transparente.</p>
</dd>
<dt>Solution :</dt>
<dd>
<p>Tout d'abord, la connaissance des serveurs cibles est issue
de tables de correspondances externes (distribuées) qui
contiennent des informations sur la localisation de nos
utilisateurs, groupes et entités. Elles se présentent sous la
forme :</p>
<example><pre>
utilisateur1 serveur_utilisateur1
utilisateur2 serveur_utilisateur2
: :
</pre></example>
<p>On les enregistre sous forme de fichiers
rediriger à tous les serveurs les URLs de la forme :</p>
<example><pre>
</pre></example>
<p>vers</p>
<example><pre>
</pre></example>
<p>si il n'est pas nécessaire que chaque chemin d'URL être valide sur chaque
serveur. Le jeu
de règles suivant le fait pour nous à l'aide des fichiers de
correspondance (en supposant que serveur0 soit un serveur par
défaut qui sera choisi si l'utilisateur ne possède aucune
entrée dans la table) :</p>
<example><pre>
RewriteEngine on
RewriteRule ^/u/<strong>([^/]+)</strong>/?(.*)
http://<strong>${utilisateur-vers-serveur:$1|serveur0}</strong>/u/$1/$2
RewriteRule ^/g/<strong>([^/]+)</strong>/?(.*)
http://<strong>${groupe-vers-serveur:$1|serveur0}</strong>/g/$1/$2
RewriteRule ^/e/<strong>([^/]+)</strong>/?(.*)
http://<strong>${entité-vers-serveur:$1|serveur0}</strong>/e/$1/$2
</pre></example>
</dd>
</dl>
</section>
<section id="structuredhomedirs">
<title>Répertoires utilisateurs structurés</title>
<dl>
<dt>Description :</dt>
<dd>
<p>Certains sites possédant des milliers d'utilisateurs
organisent les répertoires home de manière
structurée, <em>c'est à dire</em> que chaque répertoire home
se situe dans un sous-répertoire dont le nom commence (par
exemple) par le premier caractère du nom de l'utilisateur.
</dd>
<dt>Solution :</dt>
<dd>
<p>Le jeu de règles suivant permet de développer les URLs avec
tilde selon la représentation ci-dessus.</p>
<example><pre>
RewriteEngine on
</pre></example>
</dd>
</dl>
</section>
<section id="filereorg">
<title>Réorganisation du système de fichiers</title>
<dl>
<dt>Description :</dt>
<dd>
<p>Voici un cas d'espèce : une application très efficace qui
fait un usage intensif de règles <code>RewriteRule</code>
dans le contexte du répertoire pour présenter un aspect
compréhensible sur le Web sans modifier la structure des
données.
rassemble mes archives de paquetages de logiciels Unix
librement accessibles, que j'ai commencé à collectionner en
1992. Pour moi, c'est un passe-temps, mais aussi un travail,
car alors que j'étudie la science informatique, j'ai aussi
travaillé depuis de nombreuses années comme administrateur
système et réseau à mes heures perdues. Chaque semaine j'ai
besoin de tel ou tel logiciel, et j'ai donc créé une
arborescence très ramifiée de répertoires où je stocke les
paquetages :</p>
<example><pre>
drwxrwxr-x 2 netsw users 512 Aug 3 18:39 Audio/
drwxrwxr-x 2 netsw users 512 Jul 9 14:37 Benchmark/
drwxrwxr-x 12 netsw users 512 Jul 9 00:34 Crypto/
drwxrwxr-x 5 netsw users 512 Jul 9 00:41 Database/
drwxrwxr-x 4 netsw users 512 Jul 30 19:25 Dicts/
drwxrwxr-x 10 netsw users 512 Jul 9 01:54 Graphic/
drwxrwxr-x 5 netsw users 512 Jul 9 01:58 Hackers/
drwxrwxr-x 8 netsw users 512 Jul 9 03:19 InfoSys/
drwxrwxr-x 3 netsw users 512 Jul 9 03:21 Math/
drwxrwxr-x 3 netsw users 512 Jul 9 03:24 Misc/
drwxrwxr-x 9 netsw users 512 Aug 1 16:33 Network/
drwxrwxr-x 2 netsw users 512 Jul 9 05:53 Office/
drwxrwxr-x 7 netsw users 512 Jul 9 09:24 SoftEng/
drwxrwxr-x 7 netsw users 512 Jul 9 12:17 System/
drwxrwxr-x 12 netsw users 512 Aug 3 20:15 Typesetting/
drwxrwxr-x 10 netsw users 512 Jul 9 14:08 X11/
</pre></example>
<p>J'ai décidé en 1996 de rendre cette archive disponible pour
le monde via une interface web agréable. "Agréable" signifie
que je voulais vous offrir une interface où vous pourriez
naviguer directement à travers la hiérarchie des archives.
Mais "agréable" signifie aussi que je ne voulais rien changer
dans cette hiérarchie - même pas en ajoutant queques scripts
CGI à son sommet. Pourquoi ? Parceque j'avais prévu de rendre
ultérieurement la structure ci-dessus accessible aussi via
FTP, et je ne voulais pas voir de fichiers CGI ou Web à ce
niveau.</p>
</dd>
<dt>Solution :</dt>
<dd>
<p>La solution comporte deux parties : la première consiste en
un ensemble de scripts CGI qui créent toutes les pages à tous
les niveaux de répertoires à la volée. Je les ai placés dans
<example><pre>
-rw-r--r-- 1 netsw users 1318 Aug 1 18:10 .wwwacl
drwxr-xr-x 18 netsw users 512 Aug 5 15:51 DATA/
-rw-rw-rw- 1 netsw users 372982 Aug 5 16:35 LOGFILE
-rw-r--r-- 1 netsw users 659 Aug 4 09:27 TODO
-rw-r--r-- 1 netsw users 5697 Aug 1 18:01 netsw-about.html
-rwxr-xr-x 1 netsw users 579 Aug 2 10:33 netsw-access.pl
-rwxr-xr-x 1 netsw users 1532 Aug 1 17:35 netsw-changes.cgi
-rwxr-xr-x 1 netsw users 2866 Aug 5 14:49 netsw-home.cgi
drwxr-xr-x 2 netsw users 512 Jul 8 23:47 netsw-img/
-rwxr-xr-x 1 netsw users 24050 Aug 5 15:49 netsw-lsdir.cgi
-rwxr-xr-x 1 netsw users 1589 Aug 3 18:43 netsw-search.cgi
-rwxr-xr-x 1 netsw users 1885 Aug 1 17:41 netsw-tree.cgi
-rw-r--r-- 1 netsw users 234 Jul 30 16:35 netsw-unlimit.lst
</pre></example>
<p>Le sous-répertoire <code>DATA/</code> contient la structure
de répertoires proprement dite mentionnée plus haut, <em>c'est
à dire</em> les véritables ressources
automatiquement via <code>rdist</code> à intervalles de temps
réguliers. Reste la seconde partie du problème : comment
relier ces deux structures selon une arborescence d'URL
facile d'accès ? Il nous faut cacher le répertoire
<code>DATA/</code> à l'utilisateur durant l'exécution des
scripts CGI appropriés aux différentes URLs. Voici comment :
tout d'abord, j'ajoute ces deux règles dans le fichier de
configuration du répertoire racine <directive
module="core">DocumentRoot</directive> du serveur afin de
<example><pre>
</pre></example>
<p>La première règle concerne les requêtes qui ne comportent
pas de slash de fin ! C'est la seconde règle qui fait le
véritable travail. Et maintenant vient la super configuration
qui se trouve dans le fichier de configuration de répertoire
<example><pre>
Options ExecCGI FollowSymLinks Includes MultiViews
RewriteEngine on
# l'accès s'effectue via le préfixe /net.sw/
RewriteBase /net.sw/
# tout d'abord, on réécrit le répertoire racine vers
# le script CGI qui lui est associé
RewriteRule ^$ netsw-home.cgi [L]
RewriteRule ^index\.html$ netsw-home.cgi [L]
# on supprime les sous-répertoires lorsque
# le navigateur nous atteint depuis des pages de répertoire
RewriteRule ^.+/(netsw-[^/]+/.+)$ $1 [L]
# on stoppe maintenant la réécriture pour les fichiers locaux
RewriteRule ^netsw-home\.cgi.* - [L]
RewriteRule ^netsw-changes\.cgi.* - [L]
RewriteRule ^netsw-search\.cgi.* - [L]
RewriteRule ^netsw-tree\.cgi$ - [L]
RewriteRule ^netsw-about\.html$ - [L]
RewriteRule ^netsw-img/.*$ - [L]
# ce qui reste est un sous-répertoire qui peut être traité
# par un autre script CGI
RewriteRule !^netsw-lsdir\.cgi.* - [C]
RewriteRule (.*) netsw-lsdir.cgi/$1
</pre></example>
<p>Quelques indices pour l'interprétation :</p>
<ol>
<li>Remarquez le drapeau <code>L</code> (last) et l'absence
de chaîne de substitution ('<code>-</code>') dans la
quatrième partie.</li>
<li>Remarquez le caractère <code>!</code> (not) et le
drapeau <code>C</code> (chain) dans la première règle de la
dernière partie.</li>
<li>Remarquez le modèle qui correspond à tout dans la
dernière règle.</li>
</ol>
</dd>
</dl>
</section>
<section id="redirect404">
<title>Rediriger les URLs erronées vers un autre serveur Web</title>
<dl>
<dt>Description :</dt>
<dd>
<p>Une question typique de la FAQ à propos de la réécriture
revient souvent : comment rediriger vers un serveur B les
requêtes qui échouent sur un serveur A ? On s'acquitte en
général de cette tâche via des scripts CGI <directive
module="core">ErrorDocument</directive> en Perl, mais il
existe aussi une solution avec <module>mod_rewrite</module>.
Notez cependant que les performances sont moindres qu'avec
l'utilisation d'un script CGI <directive
module="core">ErrorDocument</directive> !</p>
</dd>
<dt>Solution :</dt>
<dd>
<p>La première solution possède des performances supérieures
mais moins de souplesse, et est moins sure :</p>
<example><pre>
RewriteEngine on
RewriteCond %{DOCUMENT_ROOT/%{REQUEST_URI} <strong>!-f</strong>
RewriteRule ^(.+) http://<strong>serveurB</strong>.dom/$1
</pre></example>
<p>Le problème réside dans le fait que seules les pages
situées dans la racine <directive
module="core">DocumentRoot</directive> seront redirigées. Mais
même si vous pouvez ajouter des conditions supplémentaires (par
exemple pour traiter aussi les répertoires home, etc...), il
existe une meilleure solution :</p>
<example><pre>
RewriteEngine on
RewriteCond %{REQUEST_URI} <strong>!-U</strong>
RewriteRule ^(.+) http://<strong>serveurB</strong>.dom/$1
</pre></example>
reprendre ici
<p>On utilise ici la fonctionnalité de prévision des URLs
futures de <module>mod_rewrite</module>. Et cette solution
fonctionne pour tous les types d'URLs et de manière sûre. Par
contre, cette méthode a un impact sur les performances du
serveur web, car chaque requête entraîne le traitement d'une
sous-requête interne supplémentaire. Par conséquent, vous
pouvez l'utiliser si votre serveur web s'exécute sur un CPU
puissant. Dans le cas d'une machine plus lente, utilisez la
première approche, ou mieux, un script CGI <directive
module="core">ErrorDocument</directive>.</p>
</dd>
</dl>
</section>
<section id="archive-access-multiplexer">
<title>Multiplexeur d'accès aux archives</title>
<dl>
<dt>Description :</dt>
<dd>
<p>Connaissez-vous la grande archive CPAN (Comprehensive Perl Archive
Network) située à <a href="http://www.perl.com/CPAN"
>http://www.perl.com/CPAN</a> ?
CPAN redirige automatiquement les navigateurs vers un des
nombreux serveurs FTP répartis à travers le monde
(généralement un serveur assez proche du client) ; chaque
serveur héberge l'intégralité d'un miroir CPAN. Il s'agit ni
plus ni moins qu'un service d'accès FTP multiplexé. Alors que
le fonctionnement de l'archive CPAN repose sur des scripts
CGI, comment implémenter une approche similaire avec
<module>mod_rewrite</module> ?</p>
</dd>
<dt>Solution :</dt>
<dd>
<p>Premièrement, remarquons que depuis la version 3.0.0,
<module>mod_rewrite</module> accepte aussi le préfixe
"<code>ftp:</code>" dans les redirections. Et deuxièmement,
l'approximation de la localisation peut être effectuée par une
table de correspondances <directive
module="mod_rewrite">RewriteMap</directive>, en se basant sur
la racine du domaine du client. Un jeu de règles chaînées
astucieux nous permet d'utiliser cette racine du domaine comme
clé de recherche dans notre table de correspondances de
multiplexage.</p>
<example><pre>
RewriteEngine on
RewriteRule ^/CxAN/(.*) %{REMOTE_HOST}::$1 [C]
RewriteRule ^.+\.<strong>([a-zA-Z]+)</strong>::(.*)$
</pre></example>
<example><pre>
##
## map.cxan -- Multiplexing Map for CxAN%{DOCUMENT_ROOT/%{REQUEST_URI}
##
:
##EOF##
</pre></example>
</dd>
</dl>
</section>
<section id="browser-dependent-content">
<title>Contenu dépendant du navigateur</title>
<dl>
<dt>Description :</dt>
<dd>
<p>Il est parfois nécessaire, au moins pour les pages
principales, de fournir un contenu optimum adapté à chaque
type de navigateur, c'est à dire que l'on doit
fournir une version pour les navigateurs courants, une version
différente pour les navigateurs en mode texte du style de
Lynx, et une autre pour les autres navigateurs.</p>
</dd>
<dt>Solution :</dt>%{DOCUMENT_ROOT/%{REQUEST_URI}
<dd>
<p>On ne peut pas utiliser la négociation de contenu car les
navigateurs ne fournissent pas leur type dans cette forme.
Nous devons nous baser sur l'en-tête HTTP "User-Agent". La
configuration ci-dessous effectue les actions suivantes : si
l'en-tête HTTP "User-Agent" commence par "Mozilla/3", la page
et la réécriture s'arrête. Si le navigateur est "Lynx" ou
"Mozilla" version 1 ou 2, la page
règles :</p>
<example><pre>
RewriteCond %{HTTP_USER_AGENT} ^<strong>Mozilla/3</strong>.*
RewriteRule ^foo\.html$ foo.<strong>NS</strong>.html [<strong>L</strong>]
RewriteCond %{HTTP_USER_AGENT} ^<strong>Lynx/</strong>.* [OR]
RewriteCond %{HTTP_USER_AGENT} ^<strong>Mozilla/[12]</strong>.*
RewriteRule ^foo\.html$ foo.<strong>20</strong>.html [<strong>L</strong>]
RewriteRule ^foo\.html$ foo.<strong>32</strong>.html [<strong>L</strong>]
</pre></example>
</dd>
</dl>
</section>
<section id="dynamic-mirror">
<title>Miroir dynamique</title>
<dl>
<dt>Description :</dt>
<dd>
<p>Supposons que nous voulions intégrer dans notre espace de
nommage de belles pages web situées sur un serveur distant.
Dans le cas d'un serveur FTP, nous aurions utilisé le
programme <code>mirror</code> qui maintient vraiment une copie
des données distantes mise à jour explicitement sur le serveur
local. Pour un serveur web, nous pourrions utiliser le
programme <code>webcopy</code> qui utilise le protocole HTTP.
Ces deux techniques présentent cependant un
inconvénient majeur : la copie locale n'est véritablement à
jour qu'au moment où nous avons lancé le programme. Plutôt qu'
un miroir statique devant être défini explicitement, il serait
préférable d'avoir un miroir dynamique dont le contenu serait
mis à jour automatiquement, à la demande, sur le(s) serveur(s)
distant(s).</p>
</dd>
<dt>Solution :</dt>
<dd>
<p>Pour y parvenir, on fait
correspondre la page web ou même l'ensemble du
répertoire web distants à notre espace de nommage en utilisant
la fonctionnalité <dfn>Mandataire</dfn> (drapeau
<code>[P]</code> ou <code>[proxy]</code>) :</p>
<example><pre>
RewriteEngine on
RewriteBase /~quux/
RewriteRule ^<strong>page-convoitée/</strong>(.*)$ <strong>http://www.tstimpreso.com/page-convoitée/</strong>$1 [<strong>P</strong>]
</pre></example>
<example><pre>
RewriteEngine on
RewriteBase /~quux/
RewriteRule ^<strong>usa-news\.html</strong>$ <strong>http://www.quux-corp.com/news/index.html</strong> [<strong>P</strong>]
</pre></example>
</dd>
</dl>
</section>
<section id="reverse-dynamic-mirror">
<title>Miroir dynamique inverse</title>
<dl>
<dt>Description :</dt>
<dd>...</dd>
<dt>Solution :</dt>
<dd>
<example><pre>
RewriteEngine on
</pre></example>
</dd>
</dl>
</section>
<section id="retrieve-missing-data">
<title>Récupérer des données manquantes depuis l'Intranet</title>
<dl>
<dt>Description :</dt>
<dd>
<p>C'est une méthode astucieuse permettant de faire
fonctionner virtuellement un serveur web d'entreprise
l'Internet (extérieur à l'entreprise), tout en maintenant et
conservant dans la réalité ses données sur un serveur web
l'entreprise) protégé par un pare-feu. L'astuce consiste, sur
le serveur web externe, à récupérer à la volée sur le serveur interne
les données demandées.</p>
</dd>
<dt>Solution :</dt>
<dd>
<p>Tout d'abord, nous devons nous assurer que notre pare-feu
protège bien le serveur web interne, et que seul le serveur
web externe est autorisé à y récupérer des données. Dans le
cas d'un filtrage par paquets, nous pourrions par exemple
définir un jeu de règles du pare-feu du style :</p>
<example><pre>
<strong>DENY</strong> serveur * Port * -->
</pre></example>
<p>Il vous suffit d'adapter ces règles à la syntaxe de votre
pare-feu. Nous pouvons maintenant définir les règles de
<module>mod_rewrite</module> qui serviront à récupérer les
données manquantes en arrière-plan via la fonctionnalité de
mandataire :</p>
<example><pre>
# L'utilisation de REQUEST_FILENAME ci dessous est correcte dans cet
# exemple de contexte au niveau serveur car la règle qui fait référence
# à REQUEST_FILENAME est chaînée à une règle qui définit
# REQUEST_FILENAME.
RewriteCond %{REQUEST_FILENAME} <strong>!-f</strong>
RewriteCond %{REQUEST_FILENAME} <strong>!-d</strong>
RewriteRule ^/home/([^/]+)/.www/?(.*) http://<strong>www2</strong>.quux-corp.dom/~$1/pub/$2 [<strong>P</strong>]
</pre></example>
</dd>
</dl>
</section>
<section id="load-balancing">
<title>Répartition de charge</title>
<dl>
<dt>Description :</dt>
<dd>
<p>Supposons que nous voulions répartir la charge du trafic
Comment y parvenir ?</p>
</dd>
<dt>Solution :</dt>
<dd>
<p>Il existe de nombreuses solutions à ce problème. Nous
décrirons tout d'abord une variante assez connue basée sur
DNS, puis une autre basée sur <module>mod_rewrite</module>
:</p>
<ol>
<li>
<strong>Round-Robin (tourniquet) DNS</strong>
<p>La méthode de répartition de charge la plus simple
consiste à utiliser le "DNS round-robin"
(rotation d'adresses) de
<code>BIND</code>. Vous devez seulement enregistrer les
habituelle dans votre DNS à l'aide d'enregistrements de
type A (adresse), comme suit :</p>
<example><pre>
www0 IN A 1.2.3.1
www1 IN A 1.2.3.2
www2 IN A 1.2.3.3
www3 IN A 1.2.3.4
www4 IN A 1.2.3.5
www5 IN A 1.2.3.6
</pre></example>
<p>Puis vous ajoutez les entrées suivantes :</p>
<example><pre>
www IN A 1.2.3.1
www IN A 1.2.3.2
www IN A 1.2.3.3
www IN A 1.2.3.4
www IN A 1.2.3.5
</pre></example>
<p>Maintenant, lors de la résolution de
<code>www0-www5</code> - mais selon une permutation
différente à chaque fois. De cette façon, les clients sont
répartis entre les différents serveurs. Notez cependant
que cette méthode de répartition de charge n'est pas
parfaite, car les résolutions DNS sont mises en cache par
les clients et les autres serveurs DNS du réseau, si
bien que lorsqu'un client s'est vu résoudre
continueront d'aller vers la même adresse IP (et donc le
même serveur), au lieu d'être réparties entre les autres
serveurs. Le résultat est cependant globalement
satisfaisant car les requêtes sont réparties
collectivement entre chacun des serveurs web.</p>
</li>
<li>
<strong>Répartition de charge basée sur DNS</strong>
<p>Une méthode de répartition de charge sophistiquée basée
sur DNS consiste à utiliser le programme
<code>lbnamed</code> que l'on peut trouver à <a
Associé à des outils auxiliaires, il s'agit d'un programme
en Perl 5 qui permet d'effectuer une véritable répartition
de charge basée sur DNS.</p>
</li>
<li>
<strong>Round-Robin basé sur la fonctionnalité de
mandataire</strong>
<p>Dans cette variante, nous utilisons
<module>mod_rewrite</module> et sa fonctionnalité de
mandataire. Tout d'abord, nous définissons
<example><pre>
www IN CNAME www0.example.com.
</pre></example>
<p>dans le DNS. Puis nous définissons
seulement, c'est à dire que nous configurons cette machine
de telle sorte que toutes les URLs qui lui arrivent soient
simplement transmises, via le mandataire interne, vers un
des 5 autres serveurs (<code>www1-www5</code>). Pour y
parvenir, nous définissons tout d'abord un jeu de règles
qui contacte un script de répartition de charge
<example><pre>
RewriteEngine on
RewriteRule ^/(.+)$ ${lb:$1} [P,L]
</pre></example>
<example><pre>
##
## lb.pl -- script de répartition de charge
##
$| = 1;
$name = "www"; # la base du nom du serveur
$first = 1; # le premier serveur (pas 0 ici, car 0 correspond à
# moi-même)
$last = 5; # le dernier serveur du tourniquet
$domain = "foo.dom"; # le nom de domaine
$cnt = 0;
while (<STDIN>) {
$cnt = (($cnt+1) % ($last+1-$first));
$server = sprintf("%s%d.%s", $name, $cnt+$first, $domain);
print "http://$server/$_";
}
##EOF##
</pre></example>
<note>Une dernière remarque : à quoi cela sert-il ?
toujours surchargé ? La réponse est oui, il est surchargé,
mais seulement avec des requêtes de mandataire ! Tous les
traitements SSI, CGI, ePerl, etc... sont entièrement
effectués sur les autres machines. Ceci peut fonctionner
correctement pour un site complexe. Le plus grand risque
réside ici dans le fait que www0 est un passage obligé et
que s'il est hors service, les autres serveurs deviennent
inaccessibles.</note>
</li>
<li>
<strong>Répartiteur de charge dédié</strong>
<p>Il existe aussi des solutions plus sophistiquées.
Cisco, F5, et de nombreuses autres sociétés proposent
des répartiteurs de charge matériels (utilisés en général
en mode doublé à des fins de redondance), qui offrent une
répartition de charge sophistiquée et des fonctionnalités
de passage automatique en mode de fonctionnement par défaut
en cas de problème. Cependant, des solutions logicielles
offrent aussi des fonctionnalités similaires avec du
matériel standard. Si vos besoins correspondent et si vous
êtes assez riche, vous pouvez envisager ces solutions. La
est un bon point de départ pour vos recherches.</p>
</li>
</ol>
</dd>
</dl>
</section>
<section id="new-mime-type">
<title>Nouveau type MIME, nouveau service</title>
<dl>
<dt>Description :</dt>
<dd>
<p>On trouve de nombreux programmes CGI attractifs sur le
réseau. Mais leur emploi est souvent rébarbatif, si bien que
de nombreux webmasters ne les utilisent pas. Même la
fonctionnalité de gestionnaire Action d'Apache pour les types
MIME ne convient que lorsque les programmes CGI ne nécessitent
pas d'URLs spéciales (réellement <code>PATH_INFO</code> et
<code>QUERY_STRINGS</code>) en entrée. Tout d'abord,
définissons un nouveau type de fichier ayant pour extension
<code>.scgi</code> (pour CGI sécurisé) qui sera associé pour
traitement au programme populaire <code>cgiwrap</code>. Le
problème est le suivant : par exemple, si on utilise un style
d'URL bien défini (voir ci-dessus), un fichier situé dans le
répertoire home de l'utilisateur pourra correspondre à l'URL
nécessite des URLs de la forme
la solution :</p>
<example><pre>
RewriteRule ^/[uge]/<strong>([^/]+)</strong>/\.www/(.+)\.scgi(.*) ...
... /interne/cgi/utilisateur/cgiwrap/~<strong>$1</strong>/$2.scgi$3 [NS,<strong>T=application/x-http-cgi</strong>]
</pre></example>
<p>Ou considérons ces autres programmes attractifs :
<code>wwwlog</code> (qui affiche le journal des accès
à une URL) et <code>wwwidx</code> (qui exécute Glimpse sur un
sous répertoire correspondant à une URL). Nous devons fournir
l'URL correspondante à ces programmes afin qu'ils sachent sur
quel répertoire ils doivent agir. Mais c'est en général
compliqué, car ils peuvent être appelés à nouveau
par la forme d'URL alternative, c'est à dire que typiquement,
nous exécuterions le programme <code>swwidx</code> depuis
<example><pre>
</pre></example>
<p>ce qui n'est pas satisfaisant, car nous devons expliciter
<strong>à la fois</strong> la localisation du répertoire
<strong>et</strong> la localisation du programme CGI dans
l'hyperlien. Si nous devons nous réorganiser, il nous faudra
beaucoup de temps pour modifier tous les hyperliens.</p>
</dd>
<dt>Solution :</dt>
<dd>
<p>La solution consiste ici à fournir un nouveau format d'URL
qui redirige automatiquement vers la requête CGI appropriée.
Pour cela, on définit les règles suivantes :</p>
<example><pre>
</pre></example>
<p>Et maintenant l'hyperlien qui renvoie vers
<example><pre>
HREF="*"
</pre></example>
<p>qui est automatiquement transformé en interne en</p>
<example><pre>
</pre></example>
<p>Une approche similaire permet d'invoquer le programme CGI
du journal des accès lorsque l'hyperlien <code>:log</code> est
utilisé.</p>
</dd>
</dl>
</section>
<section id="on-the-fly-content">
<title>Régéneration de contenu à la volée</title>
<dl>
<dt>Description :</dt>
<dd>
<p>Voici une fonctionnalité vraiment ésotérique : des pages
générées dynamiquement mais servies statiquement, c'est à
dire que les pages doivent être servies comme des pages
purement statiques (lues depuis le système de fichiers et
servies en l'état), mais doivent être générées dynamiquement
par le serveur web si elles sont absentes. Ainsi, vous pouvez
avoir des pages générées par CGI qui sont servies statiquement
à moins qu'un administrateur (ou une tâche de
<code>cron</code>) ne supprime les
contenus statiques. Les contenus sont ensuite actualisés.</p>
</dd>
<dt>Solution :</dt>
<dd>
A cet effet, on utilise le jeu de règles suivant :
<example><pre>
# Cet exemple n'est valable que dans un contexte de répertoire
RewriteCond %{REQUEST_FILENAME} <strong>!-s</strong>
</pre></example>
ou possède une taille de fichier nulle. L'astuce réside ici
qui (en plus de <code>STDOUT</code>) écrit sa sortie dans le
générée. Si le webmaster
veut actualiser les contenus, il lui suffit de supprimer le
de <code>cron</code>).</p>
</dd>
</dl>
</section>
<section id="autorefresh">
<title>Actualisation automatique d'un document</title>
<dl>
<dt>Description :</dt>
<dd>
<p>Lorsque nous créons une page web complexe, ne serait-il pas
souhaitable que le navigateur web actualise automatiquement la
page chaque fois que nous en sauvegardons une nouvelle version
à partir de notre éditeur ? Impossible ?</p>
</dd>
<dt>Solution :</dt>
<dd>
<p>Non ! Nous allons pour cela combiner la fonctionnalité MIME
multipart, la fonctionnalité NPH du serveur web et la
puissance de <module>mod_rewrite</module> pour la manipulation
d'URLs. Tout d'abord, nous définissons une nouvelle
fonctionnalité pour les URLs : l'ajout de
<code>:refresh</code> à toute URL fait que la 'page' est
actualisée chaque fois que la ressource est mise à jour dans
le système de fichiers.</p>
<example><pre>
</pre></example>
<p>Nous appelons maintenant cette URL</p>
<example><pre>
</pre></example>
<p>ce qui entraîne en interne l'invocation de l'URL</p>
<example><pre>
</pre></example>
<p>Il ne reste plus qu'à écrire le script CGI. Bien que l'on
écrive habituellement dans ces cas "laissé à la charge du
lecteur à titre d'exercice", ;-) je vous l'offre, aussi.</p>
<example><pre>
##
## pages
## Copyright (c) 1997 Ralf S. Engelschall, All Rights Reserved.
##
$| = 1;
# éclate la variable QUERY_STRING
@pairs = split(/&/, $ENV{'QUERY_STRING'});
foreach $pair (@pairs) {
($name, $value) = split(/=/, $pair);
$name = 'QS_' . $name;
$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
eval "\$$name = \"$value\"";
}
$QS_s = 1 if ($QS_s eq '');
$QS_n = 3600 if ($QS_n eq '');
if ($QS_f eq '') {
print "&lt;b&gt;ERREUR&lt;/b&gt;: Aucun fichier fourni\n";
exit(0);
}
if (! -f $QS_f) {
print "&lt;b&gt;ERREUR&lt;/b&gt;: Fichier $QS_f non trouvé\n";
exit(0);
}
sub print_http_headers_multipart_begin {
$bound = "ThisRandomString12345";
print "Content-type: multipart/x-mixed-replace;boundary=$bound\n";
&print_http_headers_multipart_next;
}
sub print_http_headers_multipart_next {
print "\n--$bound\n";
}
sub print_http_headers_multipart_end {
print "\n--$bound--\n";
}
sub displayhtml {
local($buffer) = @_;
$len = length($buffer);
print "Content-length: $len\n\n";
print $buffer;
}
sub readfile {
local($file) = @_;
local(*FP, $size, $buffer, $bytes);
($x, $x, $x, $x, $x, $x, $x, $size) = stat($file);
$size = sprintf("%d", $size);
open(FP, "&lt;$file");
$bytes = sysread(FP, $buffer, $size);
close(FP);
return $buffer;
}
$buffer = &readfile($QS_f);
&print_http_headers_multipart_begin;
&displayhtml($buffer);
sub mystat {
local($file) = $_[0];
local($time);
($x, $x, $x, $x, $x, $x, $x, $x, $x, $mtime) = stat($file);
return $mtime;
}
$mtimeL = &mystat($QS_f);
$mtime = $mtime;
for ($n = 0; $n &lt; $QS_n; $n++) {
while (1) {
$mtime = &mystat($QS_f);
if ($mtime ne $mtimeL) {
$mtimeL = $mtime;
sleep(2);
$buffer = &readfile($QS_f);
&print_http_headers_multipart_next;
&displayhtml($buffer);
sleep(5);
$mtimeL = &mystat($QS_f);
last;
}
sleep($QS_s);
}
}
&print_http_headers_multipart_end;
exit(0);
##EOF##
</pre></example>
</dd>
</dl>
</section>
<section id="mass-virtual-hosting">
<title>Hébergement virtuel de masse</title>
<dl>
<dt>Description :</dt>
<dd>
<p>La fonctionnalité <directive type="section" module="core"
>VirtualHost</directive> d'Apache est intéressante et
fonctionne de manière satisfaisante jusqu'à quelques
douzaines de serveurs virtuels. Par contre, si vous êtes un
FAI et devez héberger des centaines de serveurs virtuels,
cette méthode n'est pas optimale.</p>
</dd>
<dt>Solution :</dt>
<dd>
<p>Pour fournir cette fonctionnalité avec
<module>mod_rewrite</module>, on fait correspondre à notre espace de
nommage la page web ou même le répertoire complet distants en
utilisant la fonctionnalité <dfn>Mandataire</dfn>
(drapeau <code>[P]</code>) :</p>
<example><pre>
##
## vhost.map
##
:
</pre></example>
<example><pre>
##
## httpd.conf
##
:
# utilisation du nom d'hôte canonique pour les redirections, etc...
UseCanonicalName on
:
# ajout du serveur virtuel en tête du format CLF
:
# activation du moteur de réécriture pour le serveur principal
RewriteEngine on
# définition de deux tables de correspondances : une première pour
# corriger les URLs et une seconde qui associe les serveurs virtuels
# disponibles avec leurs racines des documents correspondantes.
RewriteMap lowercase int:tolower
# et enfin sélection proprement dite du serveur virtuel approprié via
# une seule règle longue et complexe :
#
# 1. on s'assure de ne pas sélectionner un hôte virtuel pour les
# adresses communes
RewriteCond %{REQUEST_URI} !^/adresse-commune1/.*
RewriteCond %{REQUEST_URI} !^/adresse-commune2/.*
:
RewriteCond %{REQUEST_URI} !^/adresse-communeN/.*
#
# 2. on vérifie que l'on dispose bien d'un en-tête Host, car
# actuellement, cette méthode ne peut faire de l'hébergement virtuel
# qu'avec cet en-tête
RewriteCond %{HTTP_HOST} !^$
#
# 3. mise en minuscules du nom d'hôte
RewriteCond ${lowercase:%{HTTP_HOST}|NONE} ^(.+)$
#
# 4. recherche ce ce nom d'hôte dans vhost.map et
# enregistrement de celui-ci seulement s'il s'agit d'un chemin
# (et non "NONE" en provenance de la condition précédente)
RewriteCond ${vhost:%1} ^(/.*)$
#
# 5. nous pouvons enfin faire correspondre l'URL avec la racine des
# documents correspondant au serveur virtuel approprié et enregistrer
# ce dernier à des fins de journalisation
RewriteRule ^/(.*)$ %1/$1 [E=VHOST:${lowercase:%{HTTP_HOST}}]
:
</pre></example>
</dd>
</dl>
</section>
<section id="host-deny">
<title>Interdiction d'hôtes</title>
<dl>
<dt>Description :</dt>
<dd>
<p>Comment interdire l'accès à notre serveur à une liste
d'hôtes ?</p>
</dd>
<dt>Solution :</dt>
<dd>
<p>Pour Apache >= 1.3b6 :</p>
<example><pre>
RewriteEngine on
RewriteCond ${hôtes-interdits:%{REMOTE_HOST}|NOT-FOUND} !=NOT-FOUND [OR]
RewriteCond ${hôtes-interdits:%{REMOTE_ADDR}|NOT-FOUND} !=NOT-FOUND
RewriteRule ^/.* - [F]
</pre></example>
<p>Pour Apache <= 1.3b6 :</p>
<example><pre>
RewriteEngine on
RewriteRule ^/(.*)$ ${hôtes-interdits:%{REMOTE_HOST}|NOT-FOUND}/$1
RewriteRule !^NOT-FOUND/.* - [F]
RewriteRule ^NOT-FOUND/(.*)$ ${hôtes-interdits:%{REMOTE_ADDR}|NOT-FOUND}/$1
RewriteRule !^NOT-FOUND/.* - [F]
RewriteRule ^NOT-FOUND/(.*)$ /$1
</pre></example>
<example><pre>
##
## hosts.deny
##
## ATTENTION! Ceci est une table de correspondances, pas une liste,
## même si on l'utilise en tant que telle. mod_rewrite l'interprète
## comme un ensemble de paires clé/valeur ; chaque entrée doit donc
## au moins posséder une valeur fictive "-".
##
193.102.180.41 -
192.76.162.40 -
</pre></example>
</dd>
</dl>
</section>
<section id="proxy-deny">
<title>Interdiction du mandataire</title>
<dl>
<dt>Description :</dt>
<dd>
<p>Comment interdire l'utilisation du mandataire d'Apache pour
un certain hôte, ou même seulement pour un utilisateur
de cet hôte ?</p>
</dd>
<dt>Solution :</dt>
<dd>
<p>Nous devons tout d'abord nous assurer que
<module>mod_rewrite</module> arrive après(!)
<module>mod_proxy</module> dans le fichier de configuration
lors de la compilation du serveur web Apache. De cette façon,
il est appelé <em>avant</em> <module>mod_proxy</module>. Nous
pouvons ensuite définir cette règle pour une interdiction
dépendant de l'hôte :</p>
<example><pre>
RewriteCond %{REMOTE_HOST} <strong>^hôte-à-rejeter\.mon-domaine\.com$</strong>
RewriteRule !^http://[^/.]\.mon-domaine.com.* - [F]
</pre></example>
<p>...et celle-ci pour une interdiction dépendant de
utilisateur@hôte :</p>
<example><pre>
RewriteCond %{REMOTE_IDENT}@%{REMOTE_HOST} <strong>^utilisateur-à-
rejeter@hôte-à-rejeter\.mon-domaine\.com$</strong>
RewriteRule !^http://[^/.]\.mon-domaine.com.* - [F]
</pre></example>
</dd>
</dl>
</section>
<section id="special-authentication">
<title>Variante particulière d'authentification</title>
<dl>
<dt>Description :</dt>
<dd>
<p>On a parfois besoin d'une authentification très
particulière, par exemple une authentification qui vérifie la
présence d'un utilisateur dans une liste explicitement
définie. Seuls ceux qui sont présents dans la liste se voient
accorder un accès, et ceci sans avoir à
s'identifier/authentifier (comme c'est le cas avec une
authentification de base via <module>mod_auth</module>).</p>
</dd>
<dt>Solution :</dt>
<dd>
<p>On définit une liste de conditions de réécriture pour
interdire l'accès à tout le monde, sauf aux utilisateurs
autorisés :</p>
<example><pre>
RewriteCond %{REMOTE_IDENT}@%{REMOTE_HOST} <strong>!^ami1@client1.quux-corp\.com$</strong>
RewriteRule ^/~quux/seulement-pour-les-amis/ - [F]
</pre></example>
</dd>
</dl>
</section>
<section id="referer-deflector">
<title>Redirection basée sur le référent</title>
<dl>
<dt>Description :</dt>
<dd>
<p>Comment écrire un programme souple qui redirige certaines
URLs en se basant sur l'en-tête HTTP "Referer", et peut être
configuré avec autant de pages de référence
que l'on veut ?</p>
</dd>
<dt>Solution :</dt>
<dd>
<p>On utilise le jeu de règles vraiment astucieux suivant :</p>
<example><pre>
RewriteCond %{HTTP_REFERER} !=""
RewriteCond ${deflector:%{HTTP_REFERER}} ^-$
RewriteRule ^.* %{HTTP_REFERER} [R,L]
RewriteCond %{HTTP_REFERER} !=""
RewriteCond ${deflector:%{HTTP_REFERER}|NOT-FOUND} !=NOT-FOUND
RewriteRule ^.* ${deflector:%{HTTP_REFERER}} [R,L]
</pre></example>
<p>... en association avec la table de réécriture
correspondante :</p>
<example><pre>
##
##
</pre></example>
<p>Les requêtes sont redirigées vers la page de référence
(lorsque la valeur correspondant à la clé extraite de la table
de correspondances est égale à "<code>-</code>"), ou vers une
URL spécifique (lorsqu'une URL est définie dans la table de
correspondances comme second argument).</p>
</dd>
</dl>
</section>
</manualpage>