1 <?xml version="1.0" encoding="ISO-8859-1" ?>
2 <!DOCTYPE manualpage SYSTEM "../style/manualpage.dtd">
3 <?xml-stylesheet type="text/xsl" href="../style/manual.fr.xsl"?>
4 <!-- English Revision : 728400 -->
5 <!-- French translation : Lucien GENTIS -->
6 <!-- Reviewed by : Vincent Deffontaines -->
9 Licensed to the Apache Software Foundation (ASF) under one or more
10 contributor license agreements. See the NOTICE file distributed with
11 this work for additional information regarding copyright ownership.
12 The ASF licenses this file to You under the Apache License, Version 2.0
13 (the "License"); you may not use this file except in compliance with
14 the License. You may obtain a copy of the License at
16 http://www.apache.org/licenses/LICENSE-2.0
18 Unless required by applicable law or agreed to in writing, software
19 distributed under the License is distributed on an "AS IS" BASIS,
20 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21 See the License for the specific language governing permissions and
22 limitations under the License.
25 <manualpage metafile="rewrite_guide_advanced.xml.meta">
26 <parentdocument href="./">Rewrite</parentdocument>
28 <title>Guide de réécriture des URLs - Sujets avancés</title>
32 <p>Ce document complémente la
33 <a href="../mod/mod_rewrite.html">documentation de référence</a> du
34 module <module>mod_rewrite</module>. Il décrit les différentes
35 manières d'utiliser le module d'Apache <module>mod_rewrite</module>
36 pour résoudre les problèmes d'URLs typiques auxquels sont souvent
37 confrontés les webmasters. Nous fournissons une description
38 détaillée de la résolution de chaque problème par la configuration
39 d'un jeu de règles de réécriture.</p>
41 <note type="warning">ATTENTION: il pourra s'avérer nécessaire de
42 modifier les exemples en fonction de la
43 configuration de votre serveur, par exemple en ajoutant le drapeau
44 <code>[PT]</code> si les modules <module>mod_alias</module> et
45 <module>mod_userdir</module> sont utilisés, etc... Les jeux de
46 règles devront également être adaptés pour passer d'un contexte de
47 serveur à un contexte de répertoire (fichiers
48 <code>.htaccess</code>). Essayez de toujours bien comprendre ce que
49 fait un jeu de règles avant de l'utiliser, ce qui pourra vous éviter
50 bien des problèmes.</note>
53 <seealso><a href="../mod/mod_rewrite.html">Documentation du
55 <seealso><a href="rewrite_intro.html">Introduction à
56 mod_rewrite</a></seealso>
57 <seealso><a href="rewrite_guide.html">Guide de réécriture - exemples
59 <seealso><a href="rewrite_tech.html">Détails techniques</a></seealso>
62 <section id="cluster">
64 <title>Accès à une grappe de serveurs via un espace d'adressage
68 <dt>Description :</dt>
71 <p>Comment créer un espace d'adressage homogène et compatible
73 tous les serveurs WWW d'une grappe de serveurs d'un intranet ?
74 C'est à dire que toutes les URLs (par définition
76 serveur et dépendant donc de celui-ci) deviennent
77 véritablement <em>indépendantes</em> du serveur ! Nous voulons
78 disposer, pour accéder à l'espace de nommage WWW, d'un seul
79 espace d'adressage compatible : aucune URL ne
80 doit inclure d'information quelconque à propos du serveur
81 cible physique. La grappe de serveurs doit elle-même nous
82 diriger automatiquement vers le bon serveur cible physique,
83 selon les besoins, et ceci de manière transparente.</p>
89 <p>Tout d'abord, la connaissance des serveurs cibles est issue
90 de tables de correspondances externes (distribuées) qui
91 contiennent des informations sur la localisation de nos
92 utilisateurs, groupes et entités. Elles se présentent sous la
96 utilisateur1 serveur_utilisateur1
97 utilisateur2 serveur_utilisateur2
101 <p>On les enregistre sous forme de fichiers
102 <code>map.xxx-vers-serveur</code>. On doit ensuite faire
103 rediriger à tous les serveurs les URLs de la forme :</p>
106 /u/utilisateur/chemin
108 /e/entité/chemin
114 http://serveur-physique/u/utilisateur/chemin
115 http://serveur-physique/g/groupe/chemin
116 http://serveur-physique/e/entité/chemin
119 <p>si il n'est pas nécessaire que chaque chemin d'URL être valide sur chaque
121 de règles suivant le fait pour nous à l'aide des fichiers de
122 correspondance (en supposant que serveur0 soit un serveur par
123 défaut qui sera choisi si l'utilisateur ne possède aucune
124 entrée dans la table) :</p>
129 RewriteMap utilisateur-vers-serveur txt:/chemin/vers/map.utilisateur-vers-serveur
130 RewriteMap groupe-vers-serveur txt:/chemin/vers/map.groupe-vers-serveur
131 RewriteMap entité-vers-serveur txt:/chemin/vers/map.entité-vers-serveur
133 RewriteRule ^/u/<strong>([^/]+)</strong>/?(.*)
134 http://<strong>${utilisateur-vers-serveur:$1|serveur0}</strong>/u/$1/$2
135 RewriteRule ^/g/<strong>([^/]+)</strong>/?(.*)
136 http://<strong>${groupe-vers-serveur:$1|serveur0}</strong>/g/$1/$2
137 RewriteRule ^/e/<strong>([^/]+)</strong>/?(.*)
138 http://<strong>${entité-vers-serveur:$1|serveur0}</strong>/e/$1/$2
140 RewriteRule ^/([uge])/([^/]+)/?$ /$1/$2/.www/
141 RewriteRule ^/([uge])/([^/]+)/([^.]+.+) /$1/$2/.www/$3\
148 <section id="structuredhomedirs">
150 <title>Répertoires utilisateurs structurés</title>
153 <dt>Description :</dt>
156 <p>Certains sites possédant des milliers d'utilisateurs
157 organisent les répertoires home de manière
158 structurée, <em>c'est à dire</em> que chaque répertoire home
159 se situe dans un sous-répertoire dont le nom commence (par
160 exemple) par le premier caractère du nom de l'utilisateur.
161 Ainsi, <code>/~foo/chemin</code> est dans
162 <code>/home/<strong>f</strong>/foo/.www/chemin</code>, tandis
163 que <code>/~bar/chemin</code> est dans
164 <code>/home/<strong>b</strong>/bar/.www/chemin</code>.</p>
170 <p>Le jeu de règles suivant permet de développer les URLs avec
171 tilde selon la représentation ci-dessus.</p>
175 RewriteRule ^/~(<strong>([a-z])</strong>[a-z0-9]+)(.*) /home/<strong>$2</strong>/$1/.www$3
182 <section id="filereorg">
184 <title>Réorganisation du système de fichiers</title>
187 <dt>Description :</dt>
190 <p>Voici un cas d'espèce : une application très efficace qui
191 fait un usage intensif de règles <code>RewriteRule</code>
192 dans le contexte du répertoire pour présenter un aspect
193 compréhensible sur le Web sans modifier la structure des
195 Les coulisses de l'affaire : <strong><em>net.sw</em></strong>
196 rassemble mes archives de paquetages de logiciels Unix
197 librement accessibles, que j'ai commencé à collectionner en
198 1992. Pour moi, c'est un passe-temps, mais aussi un travail,
199 car alors que j'étudie la science informatique, j'ai aussi
200 travaillé depuis de nombreuses années comme administrateur
201 système et réseau à mes heures perdues. Chaque semaine j'ai
202 besoin de tel ou tel logiciel, et j'ai donc créé une
203 arborescence très ramifiée de répertoires où je stocke les
207 drwxrwxr-x 2 netsw users 512 Aug 3 18:39 Audio/
208 drwxrwxr-x 2 netsw users 512 Jul 9 14:37 Benchmark/
209 drwxrwxr-x 12 netsw users 512 Jul 9 00:34 Crypto/
210 drwxrwxr-x 5 netsw users 512 Jul 9 00:41 Database/
211 drwxrwxr-x 4 netsw users 512 Jul 30 19:25 Dicts/
212 drwxrwxr-x 10 netsw users 512 Jul 9 01:54 Graphic/
213 drwxrwxr-x 5 netsw users 512 Jul 9 01:58 Hackers/
214 drwxrwxr-x 8 netsw users 512 Jul 9 03:19 InfoSys/
215 drwxrwxr-x 3 netsw users 512 Jul 9 03:21 Math/
216 drwxrwxr-x 3 netsw users 512 Jul 9 03:24 Misc/
217 drwxrwxr-x 9 netsw users 512 Aug 1 16:33 Network/
218 drwxrwxr-x 2 netsw users 512 Jul 9 05:53 Office/
219 drwxrwxr-x 7 netsw users 512 Jul 9 09:24 SoftEng/
220 drwxrwxr-x 7 netsw users 512 Jul 9 12:17 System/
221 drwxrwxr-x 12 netsw users 512 Aug 3 20:15 Typesetting/
222 drwxrwxr-x 10 netsw users 512 Jul 9 14:08 X11/
225 <p>J'ai décidé en 1996 de rendre cette archive disponible pour
226 le monde via une interface web agréable. "Agréable" signifie
227 que je voulais vous offrir une interface où vous pourriez
228 naviguer directement à travers la hiérarchie des archives.
229 Mais "agréable" signifie aussi que je ne voulais rien changer
230 dans cette hiérarchie - même pas en ajoutant queques scripts
231 CGI à son sommet. Pourquoi ? Parceque j'avais prévu de rendre
232 ultérieurement la structure ci-dessus accessible aussi via
233 FTP, et je ne voulais pas voir de fichiers CGI ou Web à ce
240 <p>La solution comporte deux parties : la première consiste en
241 un ensemble de scripts CGI qui créent toutes les pages à tous
242 les niveaux de répertoires à la volée. Je les ai placés dans
243 <code>/e/netsw/.www/</code> comme suit :</p>
246 -rw-r--r-- 1 netsw users 1318 Aug 1 18:10 .wwwacl
247 drwxr-xr-x 18 netsw users 512 Aug 5 15:51 DATA/
248 -rw-rw-rw- 1 netsw users 372982 Aug 5 16:35 LOGFILE
249 -rw-r--r-- 1 netsw users 659 Aug 4 09:27 TODO
250 -rw-r--r-- 1 netsw users 5697 Aug 1 18:01 netsw-about.html
251 -rwxr-xr-x 1 netsw users 579 Aug 2 10:33 netsw-access.pl
252 -rwxr-xr-x 1 netsw users 1532 Aug 1 17:35 netsw-changes.cgi
253 -rwxr-xr-x 1 netsw users 2866 Aug 5 14:49 netsw-home.cgi
254 drwxr-xr-x 2 netsw users 512 Jul 8 23:47 netsw-img/
255 -rwxr-xr-x 1 netsw users 24050 Aug 5 15:49 netsw-lsdir.cgi
256 -rwxr-xr-x 1 netsw users 1589 Aug 3 18:43 netsw-search.cgi
257 -rwxr-xr-x 1 netsw users 1885 Aug 1 17:41 netsw-tree.cgi
258 -rw-r--r-- 1 netsw users 234 Jul 30 16:35 netsw-unlimit.lst
261 <p>Le sous-répertoire <code>DATA/</code> contient la structure
262 de répertoires proprement dite mentionnée plus haut, <em>c'est
263 à dire</em> les véritables ressources
264 <strong><em>net.sw</em></strong> et est mis à jour
265 automatiquement via <code>rdist</code> à intervalles de temps
266 réguliers. Reste la seconde partie du problème : comment
267 relier ces deux structures selon une arborescence d'URL
268 facile d'accès ? Il nous faut cacher le répertoire
269 <code>DATA/</code> à l'utilisateur durant l'exécution des
270 scripts CGI appropriés aux différentes URLs. Voici comment :
271 tout d'abord, j'ajoute ces deux règles dans le fichier de
272 configuration du répertoire racine <directive
273 module="core">DocumentRoot</directive> du serveur afin de
274 réécrire le chemin d'URL public <code>/net.sw/</code> vers le
275 chemin interne <code>/e/netsw</code> :</p>
278 RewriteRule ^net.sw$ net.sw/ [R]
279 RewriteRule ^net.sw/(.*)$ e/netsw/$1
282 <p>La première règle concerne les requêtes qui ne comportent
283 pas de slash de fin ! C'est la seconde règle qui fait le
284 véritable travail. Et maintenant vient la super configuration
285 qui se trouve dans le fichier de configuration de répertoire
286 <code>/e/netsw/.www/.wwwacl</code> :</p>
289 Options ExecCGI FollowSymLinks Includes MultiViews
293 # l'accès s'effectue via le préfixe /net.sw/
296 # tout d'abord, on réécrit le répertoire racine vers
297 # le script CGI qui lui est associé
298 RewriteRule ^$ netsw-home.cgi [L]
299 RewriteRule ^index\.html$ netsw-home.cgi [L]
301 # on supprime les sous-répertoires lorsque
302 # le navigateur nous atteint depuis des pages de répertoire
303 RewriteRule ^.+/(netsw-[^/]+/.+)$ $1 [L]
305 # on stoppe maintenant la réécriture pour les fichiers locaux
306 RewriteRule ^netsw-home\.cgi.* - [L]
307 RewriteRule ^netsw-changes\.cgi.* - [L]
308 RewriteRule ^netsw-search\.cgi.* - [L]
309 RewriteRule ^netsw-tree\.cgi$ - [L]
310 RewriteRule ^netsw-about\.html$ - [L]
311 RewriteRule ^netsw-img/.*$ - [L]
313 # ce qui reste est un sous-répertoire qui peut être traité
314 # par un autre script CGI
315 RewriteRule !^netsw-lsdir\.cgi.* - [C]
316 RewriteRule (.*) netsw-lsdir.cgi/$1
319 <p>Quelques indices pour l'interprétation :</p>
322 <li>Remarquez le drapeau <code>L</code> (last) et l'absence
323 de chaîne de substitution ('<code>-</code>') dans la
324 quatrième partie.</li>
326 <li>Remarquez le caractère <code>!</code> (not) et le
327 drapeau <code>C</code> (chain) dans la première règle de la
328 dernière partie.</li>
330 <li>Remarquez le modèle qui correspond à tout dans la
331 dernière règle.</li>
338 <section id="redirect404">
340 <title>Rediriger les URLs erronées vers un autre serveur Web</title>
343 <dt>Description :</dt>
346 <p>Une question typique de la FAQ à propos de la réécriture
347 revient souvent : comment rediriger vers un serveur B les
348 requêtes qui échouent sur un serveur A ? On s'acquitte en
349 général de cette tâche via des scripts CGI <directive
350 module="core">ErrorDocument</directive> en Perl, mais il
351 existe aussi une solution avec <module>mod_rewrite</module>.
352 Notez cependant que les performances sont moindres qu'avec
353 l'utilisation d'un script CGI <directive
354 module="core">ErrorDocument</directive> !</p>
360 <p>La première solution possède des performances supérieures
361 mais moins de souplesse, et est moins sure :</p>
365 RewriteCond %{DOCUMENT_ROOT/%{REQUEST_URI} <strong>!-f</strong>
366 RewriteRule ^(.+) http://<strong>serveurB</strong>.dom/$1
369 <p>Le problème réside dans le fait que seules les pages
370 situées dans la racine <directive
371 module="core">DocumentRoot</directive> seront redirigées. Mais
372 même si vous pouvez ajouter des conditions supplémentaires (par
373 exemple pour traiter aussi les répertoires home, etc...), il
374 existe une meilleure solution :</p>
378 RewriteCond %{REQUEST_URI} <strong>!-U</strong>
379 RewriteRule ^(.+) http://<strong>serveurB</strong>.dom/$1
382 <p>On utilise ici la fonctionnalité de prévision des URLs
383 futures de <module>mod_rewrite</module>. Et cette solution
384 fonctionne pour tous les types d'URLs et de manière sûre. Par
385 contre, cette méthode a un impact sur les performances du
386 serveur web, car chaque requête entraîne le traitement d'une
387 sous-requête interne supplémentaire. Par conséquent, vous
388 pouvez l'utiliser si votre serveur web s'exécute sur un CPU
389 puissant. Dans le cas d'une machine plus lente, utilisez la
390 première approche, ou mieux, un script CGI <directive
391 module="core">ErrorDocument</directive>.</p>
397 <section id="archive-access-multiplexer">
399 <title>Multiplexeur d'accès aux archives</title>
402 <dt>Description :</dt>
405 <p>Connaissez-vous la grande archive CPAN (Comprehensive Perl Archive
406 Network) située à <a href="http://www.perl.com/CPAN"
407 >http://www.perl.com/CPAN</a> ?
408 CPAN redirige automatiquement les navigateurs vers un des
409 nombreux serveurs FTP répartis à travers le monde
410 (généralement un serveur assez proche du client) ; chaque
411 serveur héberge l'intégralité d'un miroir CPAN. Il s'agit ni
412 plus ni moins qu'un service d'accès FTP multiplexé. Alors que
413 le fonctionnement de l'archive CPAN repose sur des scripts
414 CGI, comment implémenter une approche similaire avec
415 <module>mod_rewrite</module> ?</p>
421 <p>Premièrement, remarquons que depuis la version 3.0.0,
422 <module>mod_rewrite</module> accepte aussi le préfixe
423 "<code>ftp:</code>" dans les redirections. Et deuxièmement,
424 l'approximation de la localisation peut être effectuée par une
425 table de correspondances <directive
426 module="mod_rewrite">RewriteMap</directive>, en se basant sur
427 la racine du domaine du client. Un jeu de règles chaînées
428 astucieux nous permet d'utiliser cette racine du domaine comme
429 clé de recherche dans notre table de correspondances de
434 RewriteMap multiplex txt:/chemin/vers/map.cxan
435 RewriteRule ^/CxAN/(.*) %{REMOTE_HOST}::$1 [C]
436 RewriteRule ^.+\.<strong>([a-zA-Z]+)</strong>::(.*)$
437 ${multiplex:<strong>$1</strong>|ftp.défaut.dom}$2 [R,L]
442 ## map.cxan -- Multiplexing Map for CxAN%{DOCUMENT_ROOT/%{REQUEST_URI}
445 de ftp://ftp.cxan.de/CxAN/
446 uk ftp://ftp.cxan.uk/CxAN/
447 com ftp://ftp.cxan.com/CxAN/
456 <section id="browser-dependent-content">
458 <title>Contenu dépendant du navigateur</title>
461 <dt>Description :</dt>
464 <p>Il est parfois nécessaire, au moins pour les pages
465 principales, de fournir un contenu optimum adapté à chaque
466 type de navigateur, c'est à dire que l'on doit
467 fournir une version pour les navigateurs courants, une version
468 différente pour les navigateurs en mode texte du style de
469 Lynx, et une autre pour les autres navigateurs.</p>
472 <dt>Solution :</dt>%{DOCUMENT_ROOT/%{REQUEST_URI}
475 <p>On ne peut pas utiliser la négociation de contenu car les
476 navigateurs ne fournissent pas leur type dans cette forme.
477 Nous devons nous baser sur l'en-tête HTTP "User-Agent". La
478 configuration ci-dessous effectue les actions suivantes : si
479 l'en-tête HTTP "User-Agent" commence par "Mozilla/3", la page
480 <code>foo.html</code> est réécrite en <code>foo.NS.html</code>
481 et la réécriture s'arrête. Si le navigateur est "Lynx" ou
482 "Mozilla" version 1 ou 2, la page
483 <code>foo.html</code> est réécrite en
484 <code>foo.20.html</code>. Tous les autres navigateurs
485 reçoivent la page <code>foo.32.html</code>. Voici le jeu de
489 RewriteCond %{HTTP_USER_AGENT} ^<strong>Mozilla/3</strong>.*
490 RewriteRule ^foo\.html$ foo.<strong>NS</strong>.html [<strong>L</strong>]
492 RewriteCond %{HTTP_USER_AGENT} ^<strong>Lynx/</strong>.* [OR]
493 RewriteCond %{HTTP_USER_AGENT} ^<strong>Mozilla/[12]</strong>.*
494 RewriteRule ^foo\.html$ foo.<strong>20</strong>.html [<strong>L</strong>]
496 RewriteRule ^foo\.html$ foo.<strong>32</strong>.html [<strong>L</strong>]
503 <section id="dynamic-mirror">
505 <title>Miroir dynamique</title>
508 <dt>Description :</dt>
511 <p>Supposons que nous voulions intégrer dans notre espace de
512 nommage de belles pages web situées sur un serveur distant.
513 Dans le cas d'un serveur FTP, nous aurions utilisé le
514 programme <code>mirror</code> qui maintient vraiment une copie
515 des données distantes mise à jour explicitement sur le serveur
516 local. Pour un serveur web, nous pourrions utiliser le
517 programme <code>webcopy</code> qui utilise le protocole HTTP.
518 Ces deux techniques présentent cependant un
519 inconvénient majeur : la copie locale n'est véritablement à
520 jour qu'au moment où nous avons lancé le programme. Plutôt qu'
521 un miroir statique devant être défini explicitement, il serait
522 préférable d'avoir un miroir dynamique dont le contenu serait
523 mis à jour automatiquement, à la demande, sur le(s) serveur(s)
530 <p>Pour y parvenir, on fait
531 correspondre la page web ou même l'ensemble du
532 répertoire web distants à notre espace de nommage en utilisant
533 la fonctionnalité <dfn>Mandataire</dfn> (drapeau
534 <code>[P]</code> ou <code>[proxy]</code>) :</p>
539 RewriteRule ^<strong>page-convoitée/</strong>(.*)$ <strong>http://www.tstimpreso.com/page-convoitée/</strong>$1 [<strong>P</strong>]
545 RewriteRule ^<strong>usa-news\.html</strong>$ <strong>http://www.quux-corp.com/news/index.html</strong> [<strong>P</strong>]
552 <section id="reverse-dynamic-mirror">
554 <title>Miroir dynamique inverse</title>
557 <dt>Description :</dt>
566 RewriteCond /miroir/du/site-distant/$1 -U
567 RewriteRule ^http://www\.site-distant\.com/(.*)$ /miroir/du/site-distant/$1
574 <section id="retrieve-missing-data">
576 <title>Récupérer des données manquantes depuis l'Intranet</title>
579 <dt>Description :</dt>
582 <p>C'est une méthode astucieuse permettant de faire
583 fonctionner virtuellement un serveur web d'entreprise
584 (<code>www.quux-corp.dom</code>) sur
585 l'Internet (extérieur à l'entreprise), tout en maintenant et
586 conservant dans la réalité ses données sur un serveur web
587 (<code>www2.quux-corp.dom</code>) de l'Intranet (interne à
588 l'entreprise) protégé par un pare-feu. L'astuce consiste, sur
589 le serveur web externe, à récupérer à la volée sur le serveur interne
590 les données demandées.</p>
596 <p>Tout d'abord, nous devons nous assurer que notre pare-feu
597 protège bien le serveur web interne, et que seul le serveur
598 web externe est autorisé à y récupérer des données. Dans le
599 cas d'un filtrage par paquets, nous pourrions par exemple
600 définir un jeu de règles du pare-feu du style :</p>
603 <strong>ALLOW</strong> serveur www.quux-corp.dom Port >1024 -->
604 serveur www2.quux-corp.dom Port <strong>80</strong>
605 <strong>DENY</strong> serveur * Port * -->
606 serveur www2.quux-corp.dom Port <strong>80</strong>
609 <p>Il vous suffit d'adapter ces règles à la syntaxe de votre
610 pare-feu. Nous pouvons maintenant définir les règles de
611 <module>mod_rewrite</module> qui serviront à récupérer les
612 données manquantes en arrière-plan via la fonctionnalité de
616 RewriteRule ^/~([^/]+)/?(.*) /home/$1/.www/$2 [C]
617 # L'utilisation de REQUEST_FILENAME ci dessous est correcte dans cet
618 # exemple de contexte au niveau serveur car la règle qui fait référence
619 # à REQUEST_FILENAME est chaînée à une règle qui définit
621 RewriteCond %{REQUEST_FILENAME} <strong>!-f</strong>
622 RewriteCond %{REQUEST_FILENAME} <strong>!-d</strong>
623 RewriteRule ^/home/([^/]+)/.www/?(.*) http://<strong>www2</strong>.quux-corp.dom/~$1/pub/$2 [<strong>P</strong>]
630 <section id="load-balancing">
632 <title>Répartition de charge</title>
635 <dt>Description :</dt>
638 <p>Supposons que nous voulions répartir la charge du trafic
639 vers <code>www.example.com</code> entre les serveurs
640 <code>www[0-5].example.com</code> (un total de 6 serveurs).
641 Comment y parvenir ?</p>
647 <p>Il existe de nombreuses solutions à ce problème. Nous
648 décrirons tout d'abord une variante assez connue basée sur
649 DNS, puis une autre basée sur <module>mod_rewrite</module>
654 <strong>Round-Robin (tourniquet) DNS</strong>
656 <p>La méthode de répartition de charge la plus simple
657 consiste à utiliser le "DNS round-robin"
658 (rotation d'adresses) de
659 <code>BIND</code>. Vous devez seulement enregistrer les
660 serveurs <code>www[0-9].example.com</code> de manière
661 habituelle dans votre DNS à l'aide d'enregistrements de
662 type A (adresse), comme suit :</p>
673 <p>Puis vous ajoutez les entrées suivantes :</p>
683 <p>Maintenant, lors de la résolution de
684 <code>www.example.com</code>, <code>BIND</code> renvoie
685 <code>www0-www5</code> - mais selon une permutation
686 différente à chaque fois. De cette façon, les clients sont
687 répartis entre les différents serveurs. Notez cependant
688 que cette méthode de répartition de charge n'est pas
689 parfaite, car les résolutions DNS sont mises en cache par
690 les clients et les autres serveurs DNS du réseau, si
691 bien que lorsqu'un client s'est vu résoudre
692 <code>www.example.com</code> en un des
693 <code>wwwN.example.com</code>, toutes ses requêtes ultérieures
694 continueront d'aller vers la même adresse IP (et donc le
695 même serveur), au lieu d'être réparties entre les autres
696 serveurs. Le résultat est cependant globalement
697 satisfaisant car les requêtes sont réparties
698 collectivement entre chacun des serveurs web.</p>
702 <strong>Répartition de charge basée sur DNS</strong>
704 <p>Une méthode de répartition de charge sophistiquée basée
705 sur DNS consiste à utiliser le programme
706 <code>lbnamed</code> que l'on peut trouver à <a
707 href="http://www.stanford.edu/~schemers/docs/lbnamed/lbnamed.html">
708 http://www.stanford.edu/~schemers/docs/lbnamed/lbnamed.html</a>.
709 Associé à des outils auxiliaires, il s'agit d'un programme
710 en Perl 5 qui permet d'effectuer une véritable répartition
711 de charge basée sur DNS.</p>
715 <strong>Round-Robin basé sur la fonctionnalité de
718 <p>Dans cette variante, nous utilisons
719 <module>mod_rewrite</module> et sa fonctionnalité de
720 mandataire. Tout d'abord, nous définissons
721 <code>www0.example.com</code> comme un autre nom de
722 <code>www.example.com</code> en ajoutant l'entrée</p>
725 www IN CNAME www0.example.com.
728 <p>dans le DNS. Puis nous définissons
729 <code>www0.example.com</code> comme serveur mandataire
730 seulement, c'est à dire que nous configurons cette machine
731 de telle sorte que toutes les URLs qui lui arrivent soient
732 simplement transmises, via le mandataire interne, vers un
733 des 5 autres serveurs (<code>www1-www5</code>). Pour y
734 parvenir, nous définissons tout d'abord un jeu de règles
735 qui contacte un script de répartition de charge
736 <code>lb.pl</code> pour toutes les URLs.</p>
740 RewriteMap lb prg:/chemin/vers/lb.pl
741 RewriteRule ^/(.+)$ ${lb:$1} [P,L]
744 <p>Puis nous écrivons <code>lb.pl</code> :</p>
749 ## lb.pl -- script de répartition de charge
754 $name = "www"; # la base du nom du serveur
755 $first = 1; # le premier serveur (pas 0 ici, car 0 correspond à
757 $last = 5; # le dernier serveur du tourniquet
758 $domain = "foo.dom"; # le nom de domaine
761 while (<STDIN>) {
762 $cnt = (($cnt+1) % ($last+1-$first));
763 $server = sprintf("%s%d.%s", $name, $cnt+$first, $domain);
764 print "http://$server/$_";
770 <note>Une dernière remarque : à quoi cela sert-il ?
771 <code>www0.example.com</code>, quant à lui, n'est-il pas
772 toujours surchargé ? La réponse est oui, il est surchargé,
773 mais seulement avec des requêtes de mandataire ! Tous les
774 traitements SSI, CGI, ePerl, etc... sont entièrement
775 effectués sur les autres machines. Ceci peut fonctionner
776 correctement pour un site complexe. Le plus grand risque
777 réside ici dans le fait que www0 est un passage obligé et
778 que s'il est hors service, les autres serveurs deviennent
779 inaccessibles.</note>
783 <strong>Répartiteur de charge dédié</strong>
785 <p>Il existe aussi des solutions plus sophistiquées.
786 Cisco, F5, et de nombreuses autres sociétés proposent
787 des répartiteurs de charge matériels (utilisés en général
788 en mode doublé à des fins de redondance), qui offrent une
789 répartition de charge sophistiquée et des fonctionnalités
790 de passage automatique en mode de fonctionnement par défaut
791 en cas de problème. Cependant, des solutions logicielles
792 offrent aussi des fonctionnalités similaires avec du
793 matériel standard. Si vos besoins correspondent et si vous
794 êtes assez riche, vous pouvez envisager ces solutions. La
795 <a href="http://vegan.net/lb/">liste de diffusion lb-l</a>
796 est un bon point de départ pour vos recherches.</p>
804 <section id="new-mime-type">
806 <title>Nouveau type MIME, nouveau service</title>
809 <dt>Description :</dt>
812 <p>On trouve de nombreux programmes CGI attractifs sur le
813 réseau. Mais leur emploi est souvent rébarbatif, si bien que
814 de nombreux webmasters ne les utilisent pas. Même la
815 fonctionnalité de gestionnaire Action d'Apache pour les types
816 MIME ne convient que lorsque les programmes CGI ne nécessitent
817 pas d'URLs spéciales (réellement <code>PATH_INFO</code> et
818 <code>QUERY_STRINGS</code>) en entrée. Tout d'abord,
819 définissons un nouveau type de fichier ayant pour extension
820 <code>.scgi</code> (pour CGI sécurisé) qui sera associé pour
821 traitement au programme populaire <code>cgiwrap</code>. Le
822 problème est le suivant : par exemple, si on utilise un style
823 d'URL bien défini (voir ci-dessus), un fichier situé dans le
824 répertoire home de l'utilisateur pourra correspondre à l'URL
825 <code>/u/user/foo/bar.scgi</code>. Mais <code>cgiwrap</code>
826 nécessite des URLs de la forme
827 <code>/~user/foo/bar.scgi/</code>. La règle suivante apporte
831 RewriteRule ^/[uge]/<strong>([^/]+)</strong>/\.www/(.+)\.scgi(.*) ...
832 ... /interne/cgi/utilisateur/cgiwrap/~<strong>$1</strong>/$2.scgi$3 [NS,<strong>T=application/x-http-cgi</strong>]
835 <p>Ou considérons ces autres programmes attractifs :
836 <code>wwwlog</code> (qui affiche le journal des accès
837 <code>access.log</code> pour un sous répertoire correspondant
838 à une URL) et <code>wwwidx</code> (qui exécute Glimpse sur un
839 sous répertoire correspondant à une URL). Nous devons fournir
840 l'URL correspondante à ces programmes afin qu'ils sachent sur
841 quel répertoire ils doivent agir. Mais c'est en général
842 compliqué, car ils peuvent être appelés à nouveau
843 par la forme d'URL alternative, c'est à dire que typiquement,
844 nous exécuterions le programme <code>swwidx</code> depuis
845 <code>/u/user/foo/</code> via un hyperlien vers</p>
848 /internal/cgi/user/swwidx?i=/u/user/foo/
851 <p>ce qui n'est pas satisfaisant, car nous devons expliciter
852 <strong>à la fois</strong> la localisation du répertoire
853 <strong>et</strong> la localisation du programme CGI dans
854 l'hyperlien. Si nous devons nous réorganiser, il nous faudra
855 beaucoup de temps pour modifier tous les hyperliens.</p>
861 <p>La solution consiste ici à fournir un nouveau format d'URL
862 qui redirige automatiquement vers la requête CGI appropriée.
863 Pour cela, on définit les règles suivantes :</p>
866 RewriteRule ^/([uge])/([^/]+)(/?.*)/\* /interne/cgi/utilisateur/wwwidx?i=/$1/$2$3/
867 RewriteRule ^/([uge])/([^/]+)(/?.*):log /interne/cgi/utilisateur/wwwlog?f=/$1/$2$3
870 <p>Et maintenant l'hyperlien qui renvoie vers
871 <code>/u/user/foo/</code> se réduit à</p>
877 <p>qui est automatiquement transformé en interne en</p>
880 /internal/cgi/user/wwwidx?i=/u/user/foo/
883 <p>Une approche similaire permet d'invoquer le programme CGI
884 du journal des accès lorsque l'hyperlien <code>:log</code> est
891 <section id="on-the-fly-content">
893 <title>Régéneration de contenu à la volée</title>
896 <dt>Description :</dt>
899 <p>Voici une fonctionnalité vraiment ésotérique : des pages
900 générées dynamiquement mais servies statiquement, c'est à
901 dire que les pages doivent être servies comme des pages
902 purement statiques (lues depuis le système de fichiers et
903 servies en l'état), mais doivent être générées dynamiquement
904 par le serveur web si elles sont absentes. Ainsi, vous pouvez
905 avoir des pages générées par CGI qui sont servies statiquement
906 à moins qu'un administrateur (ou une tâche de
907 <code>cron</code>) ne supprime les
908 contenus statiques. Les contenus sont ensuite actualisés.</p>
914 A cet effet, on utilise le jeu de règles suivant :
917 # Cet exemple n'est valable que dans un contexte de répertoire
918 RewriteCond %{REQUEST_FILENAME} <strong>!-s</strong>
919 RewriteRule ^page\.<strong>html</strong>$ page.<strong>cgi</strong> [T=application/x-httpd-cgi,L]
922 <p>Ainsi, une requête pour <code>page.html</code> entraîne
923 l'exécution interne de la page <code>page.cgi</code>
924 correspondante si <code>page.html</code> n'existe pas
925 ou possède une taille de fichier nulle. L'astuce réside ici
926 dans le fait que <code>page.cgi</code> est un script CGI
927 qui (en plus de <code>STDOUT</code>) écrit sa sortie dans le
928 fichier <code>page.html</code>. Une fois le script exécuté, le
929 serveur sert la page <code>page.html</code> fraîchement
930 générée. Si le webmaster
931 veut actualiser les contenus, il lui suffit de supprimer le
932 fichier <code>page.html</code> (le plus souvent via une tâche
933 de <code>cron</code>).</p>
939 <section id="autorefresh">
941 <title>Actualisation automatique d'un document</title>
944 <dt>Description :</dt>
947 <p>Lorsque nous créons une page web complexe, ne serait-il pas
948 souhaitable que le navigateur web actualise automatiquement la
949 page chaque fois que nous en sauvegardons une nouvelle version
950 à partir de notre éditeur ? Impossible ?</p>
956 <p>Non ! Nous allons pour cela combiner la fonctionnalité MIME
957 multipart, la fonctionnalité NPH du serveur web et la
958 puissance de <module>mod_rewrite</module> pour la manipulation
959 d'URLs. Tout d'abord, nous définissons une nouvelle
960 fonctionnalité pour les URLs : l'ajout de
961 <code>:refresh</code> à toute URL fait que la 'page' est
962 actualisée chaque fois que la ressource est mise à jour dans
963 le système de fichiers.</p>
966 RewriteRule ^(/[uge]/[^/]+/?.*):refresh /interne/cgi/apache/nph-refresh?f=$1
969 <p>Nous appelons maintenant cette URL</p>
972 /u/foo/bar/page.html:refresh
975 <p>ce qui entraîne en interne l'invocation de l'URL</p>
978 /interne/cgi/apache/nph-refresh?f=/u/foo/bar/page.html
981 <p>Il ne reste plus qu'à écrire le script CGI. Bien que l'on
982 écrive habituellement dans ces cas "laissé à la charge du
983 lecteur à titre d'exercice", ;-) je vous l'offre, aussi.</p>
988 ## nph-refresh -- script NPH/CGI pour l'actualisation automatique de
990 ## Copyright (c) 1997 Ralf S. Engelschall, All Rights Reserved.
994 # éclate la variable QUERY_STRING
995 @pairs = split(/&/, $ENV{'QUERY_STRING'});
996 foreach $pair (@pairs) {
997 ($name, $value) = split(/=/, $pair);
998 $name =~ tr/A-Z/a-z/;
999 $name = 'QS_' . $name;
1000 $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
1001 eval "\$$name = \"$value\"";
1003 $QS_s = 1 if ($QS_s eq '');
1004 $QS_n = 3600 if ($QS_n eq '');
1006 print "HTTP/1.0 200 OK\n";
1007 print "Content-type: text/html\n\n";
1008 print "&lt;b&gt;ERREUR&lt;/b&gt;: Aucun fichier fourni\n";
1012 print "HTTP/1.0 200 OK\n";
1013 print "Content-type: text/html\n\n";
1014 print "&lt;b&gt;ERREUR&lt;/b&gt;: Fichier $QS_f non trouvé\n";
1018 sub print_http_headers_multipart_begin {
1019 print "HTTP/1.0 200 OK\n";
1020 $bound = "ThisRandomString12345";
1021 print "Content-type: multipart/x-mixed-replace;boundary=$bound\n";
1022 &print_http_headers_multipart_next;
1025 sub print_http_headers_multipart_next {
1026 print "\n--$bound\n";
1029 sub print_http_headers_multipart_end {
1030 print "\n--$bound--\n";
1034 local($buffer) = @_;
1035 $len = length($buffer);
1036 print "Content-type: text/html\n";
1037 print "Content-length: $len\n\n";
1043 local(*FP, $size, $buffer, $bytes);
1044 ($x, $x, $x, $x, $x, $x, $x, $size) = stat($file);
1045 $size = sprintf("%d", $size);
1046 open(FP, "&lt;$file");
1047 $bytes = sysread(FP, $buffer, $size);
1052 $buffer = &readfile($QS_f);
1053 &print_http_headers_multipart_begin;
1054 &displayhtml($buffer);
1057 local($file) = $_[0];
1060 ($x, $x, $x, $x, $x, $x, $x, $x, $x, $mtime) = stat($file);
1064 $mtimeL = &mystat($QS_f);
1066 for ($n = 0; $n &lt; $QS_n; $n++) {
1068 $mtime = &mystat($QS_f);
1069 if ($mtime ne $mtimeL) {
1072 $buffer = &readfile($QS_f);
1073 &print_http_headers_multipart_next;
1074 &displayhtml($buffer);
1076 $mtimeL = &mystat($QS_f);
1083 &print_http_headers_multipart_end;
1094 <section id="mass-virtual-hosting">
1096 <title>Hébergement virtuel de masse</title>
1099 <dt>Description :</dt>
1102 <p>La fonctionnalité <directive type="section" module="core"
1103 >VirtualHost</directive> d'Apache est intéressante et
1104 fonctionne de manière satisfaisante jusqu'à quelques
1105 douzaines de serveurs virtuels. Par contre, si vous êtes un
1106 FAI et devez héberger des centaines de serveurs virtuels,
1107 cette méthode n'est pas optimale.</p>
1113 <p>Pour fournir cette fonctionnalité avec
1114 <module>mod_rewrite</module>, on fait correspondre à notre espace de
1115 nommage la page web ou même le répertoire complet distants en
1116 utilisant la fonctionnalité <dfn>Mandataire</dfn>
1117 (drapeau <code>[P]</code>) :</p>
1123 www.vhost1.dom:80 /chemin/vers/racine-doc/vhost1
1124 www.vhost2.dom:80 /chemin/vers/racine-doc/vhost2
1126 www.vhostN.dom:80 /chemin/vers/racine-doc/vhostN
1134 # utilisation du nom d'hôte canonique pour les redirections, etc...
1138 # ajout du serveur virtuel en tête du format CLF
1139 CustomLog /chemin/vers/access_log "%{VHOST}e %h %l %u %t \"%r\" %>s %b"
1142 # activation du moteur de réécriture pour le serveur principal
1145 # définition de deux tables de correspondances : une première pour
1146 # corriger les URLs et une seconde qui associe les serveurs virtuels
1147 # disponibles avec leurs racines des documents correspondantes.
1148 RewriteMap lowercase int:tolower
1149 RewriteMap vhost txt:/chemin/vers/vhost.map
1151 # et enfin sélection proprement dite du serveur virtuel approprié via
1152 # une seule règle longue et complexe :
1154 # 1. on s'assure de ne pas sélectionner un hôte virtuel pour les
1157 RewriteCond %{REQUEST_URI} !^/adresse-commune1/.*
1158 RewriteCond %{REQUEST_URI} !^/adresse-commune2/.*
1160 RewriteCond %{REQUEST_URI} !^/adresse-communeN/.*
1162 # 2. on vérifie que l'on dispose bien d'un en-tête Host, car
1163 # actuellement, cette méthode ne peut faire de l'hébergement virtuel
1164 # qu'avec cet en-tête
1165 RewriteCond %{HTTP_HOST} !^$
1167 # 3. mise en minuscules du nom d'hôte
1168 RewriteCond ${lowercase:%{HTTP_HOST}|NONE} ^(.+)$
1170 # 4. recherche ce ce nom d'hôte dans vhost.map et
1171 # enregistrement de celui-ci seulement s'il s'agit d'un chemin
1172 # (et non "NONE" en provenance de la condition précédente)
1173 RewriteCond ${vhost:%1} ^(/.*)$
1175 # 5. nous pouvons enfin faire correspondre l'URL avec la racine des
1176 # documents correspondant au serveur virtuel approprié et enregistrer
1177 # ce dernier à des fins de journalisation
1178 RewriteRule ^/(.*)$ %1/$1 [E=VHOST:${lowercase:%{HTTP_HOST}}]
1186 <section id="host-deny">
1188 <title>Interdiction d'hôtes</title>
1191 <dt>Description :</dt>
1194 <p>Comment interdire l'accès à notre serveur à une liste
1201 <p>Pour Apache >= 1.3b6 :</p>
1205 RewriteMap hôtes-interdits txt:/chemin/vers/hôtes-interdits
1206 RewriteCond ${hôtes-interdits:%{REMOTE_HOST}|NOT-FOUND} !=NOT-FOUND [OR]
1207 RewriteCond ${hôtes-interdits:%{REMOTE_ADDR}|NOT-FOUND} !=NOT-FOUND
1208 RewriteRule ^/.* - [F]
1211 <p>Pour Apache <= 1.3b6 :</p>
1215 RewriteMap hôtes-interdits txt:/chemin/vers/hôtes-interdits
1216 RewriteRule ^/(.*)$ ${hôtes-interdits:%{REMOTE_HOST}|NOT-FOUND}/$1
1217 RewriteRule !^NOT-FOUND/.* - [F]
1218 RewriteRule ^NOT-FOUND/(.*)$ ${hôtes-interdits:%{REMOTE_ADDR}|NOT-FOUND}/$1
1219 RewriteRule !^NOT-FOUND/.* - [F]
1220 RewriteRule ^NOT-FOUND/(.*)$ /$1
1227 ## ATTENTION! Ceci est une table de correspondances, pas une liste,
1228 ## même si on l'utilise en tant que telle. mod_rewrite l'interprète
1229 ## comme un ensemble de paires clé/valeur ; chaque entrée doit donc
1230 ## au moins posséder une valeur fictive "-".
1242 <section id="proxy-deny">
1244 <title>Interdiction du mandataire</title>
1247 <dt>Description :</dt>
1250 <p>Comment interdire l'utilisation du mandataire d'Apache pour
1251 un certain hôte, ou même seulement pour un utilisateur
1252 de cet hôte ?</p>
1258 <p>Nous devons tout d'abord nous assurer que
1259 <module>mod_rewrite</module> arrive après(!)
1260 <module>mod_proxy</module> dans le fichier de configuration
1261 lors de la compilation du serveur web Apache. De cette façon,
1262 il est appelé <em>avant</em> <module>mod_proxy</module>. Nous
1263 pouvons ensuite définir cette règle pour une interdiction
1264 dépendant de l'hôte :</p>
1267 RewriteCond %{REMOTE_HOST} <strong>^hôte-à-rejeter\.mon-domaine\.com$</strong>
1268 RewriteRule !^http://[^/.]\.mon-domaine.com.* - [F]
1271 <p>...et celle-ci pour une interdiction dépendant de
1272 utilisateur@hôte :</p>
1275 RewriteCond %{REMOTE_IDENT}@%{REMOTE_HOST} <strong>^utilisateur-à-
1276 rejeter@hôte-à-rejeter\.mon-domaine\.com$</strong>
1277 RewriteRule !^http://[^/.]\.mon-domaine.com.* - [F]
1284 <section id="special-authentication">
1286 <title>Variante particulière d'authentification</title>
1289 <dt>Description :</dt>
1292 <p>On a parfois besoin d'une authentification très
1293 particulière, par exemple une authentification qui vérifie la
1294 présence d'un utilisateur dans une liste explicitement
1295 définie. Seuls ceux qui sont présents dans la liste se voient
1296 accorder un accès, et ceci sans avoir à
1297 s'identifier/authentifier (comme c'est le cas avec une
1298 authentification de base via <module>mod_auth</module>).</p>
1304 <p>On définit une liste de conditions de réécriture pour
1305 interdire l'accès à tout le monde, sauf aux utilisateurs
1306 autorisés :</p>
1309 RewriteCond %{REMOTE_IDENT}@%{REMOTE_HOST} <strong>!^ami1@client1.quux-corp\.com$</strong>
1310 RewriteCond %{REMOTE_IDENT}@%{REMOTE_HOST} <strong>!^ami2</strong>@client2.quux-corp\.com$
1311 RewriteCond %{REMOTE_IDENT}@%{REMOTE_HOST} <strong>!^ami3</strong>@client3.quux-corp\.com$
1312 RewriteRule ^/~quux/seulement-pour-les-amis/ - [F]
1319 <section id="referer-deflector">
1321 <title>Redirection basée sur le référent</title>
1324 <dt>Description :</dt>
1327 <p>Comment écrire un programme souple qui redirige certaines
1328 URLs en se basant sur l'en-tête HTTP "Referer", et peut être
1329 configuré avec autant de pages de référence
1336 <p>On utilise le jeu de règles vraiment astucieux suivant :</p>
1339 RewriteMap deflector txt:/chemin/vers/deflector.map
1341 RewriteCond %{HTTP_REFERER} !=""
1342 RewriteCond ${deflector:%{HTTP_REFERER}} ^-$
1343 RewriteRule ^.* %{HTTP_REFERER} [R,L]
1345 RewriteCond %{HTTP_REFERER} !=""
1346 RewriteCond ${deflector:%{HTTP_REFERER}|NOT-FOUND} !=NOT-FOUND
1347 RewriteRule ^.* ${deflector:%{HTTP_REFERER}} [R,L]
1350 <p>... en association avec la table de réécriture
1351 correspondante :</p>
1358 http://www.mauvais-sujets.com/mauvais/index.html -
1359 http://www.mauvais-sujets.com/mauvais/index2.html -
1360 http://www.mauvais-sujets.com/mauvais/index3.html http://quelque-part.com/
1363 <p>Les requêtes sont redirigées vers la page de référence
1364 (lorsque la valeur correspondant à la clé extraite de la table
1365 de correspondances est égale à "<code>-</code>"), ou vers une
1366 URL spécifique (lorsqu'une URL est définie dans la table de
1367 correspondances comme second argument).</p>