Réécriture d’URL, alias et plusieurs développeurs sur Apache

Lorsque l’on travaille à plusieurs sur un projet, c’est toujours intéressant. Malheureusement, cela peut aussi entrainer divers problèmes. Je vais tenter de vous expliquer un obstacle qui peut vite devenir très chi*nt…

Pour présenter cette problématique, je vais prendre exemple sur ce que je développe actuellement. Le site en cours de création se base sur zend framework et nécessite une réécriture d’URL. Il faut savoir qu’Apache utilise le chemin physique1 comme base pour calculer le chemin vers le fichier réécrit sauf si on lui précise une directive RewriteBase différente. Le problème survient à cet endroit, plusieurs développeurs entrainent plusieurs machines et donc plusieurs configurations différentes!

La solution de base est que chaque personne utilisant un alias Apache définit un RewriteBase. Cependant, cela veut dire qu’a chaque nouvelle version du fichier .htaccess il faut redéfinir celui-ci.

La réponse la plus simple consiste à utiliser un RewriteCond sur l’hostname du serveur et bien entendu à l’utiliser lors de l’accès aux tests locaux ou non …

Exemple de configuration :

  • nom du serveur : grummfy
  • URL appelée : http://grummfy/serveur/dev/projet/example.com/… (Si vous utilise http://localhost/ la directive HTTP_HOST vaudra localhost)
  • ALIAS : /serveur/ => /media/data/serveur/

Le fichier .htacccess contiendra ceci :
SetEnv APPLICATION_ENV development
php_value session.auto_start 0
php_flag magic_quotes_gpc off
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteCond %{HTTP_HOST} grummfy
RewriteRule ^.*$ /serveur/dev/projet/example.com/index.php [NC,L]
RewriteRule ^.*$ index.php [NC,L]

Le fait d’utiliser le rewrite flag « L » permet de sortir de la réécriture d’URL. Si le hostname du serveur n’est pas grummfy il appliquera la règle par défaut, à savoir tenter de trouver index.php dans /media/data/serveur/dev/projet/example.com/ comme si on avait effectuer un appel depuis http://media/data/serveur/dev/projet/example.com/

  1. physical-directory-path

Réécriture d’url et erreur interne à Apache

Il arrive que lors de l’écriture d’un nombre complexe de règle de réécriture d’URL (URL rewriting), des erreurs se produisent sans pour autant être compréhensibles. Je vous propose donc de regarder comment déboguer cela à travers un exemple pratique.

Exemple

Prenons une règles qui vérifierait que seul certains type de caractère sont autorisés.
La réécriture quant à elle se fait sur toute URL.
Ajoutons un document 403 personnalisé
RewriteEngine on
#
RewriteCond %{THE_REQUEST} !^[A-Z]{3,9}\ [a-zA-Z0-9\.\+_/\-\?\=\&]+\ HTTP/ [NC]
RewriteRule .* - [F,NS,L]
#
#if not a dir or a file
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
#
RewriteRule ^(.*)$ index.php?p=$1 [QSA,L]
ErrorDocument 403 /erreur/403

Testons l’URL http://localhost/test:s et on obtient un beau :

Forbidden

You don’t have permission to access /test:s on this server.

Additionally, a 500 Internal Server Error error was encountered while trying to use an ErrorDocument to handle the request.

Les logs d’erreurs d’Apache vous diront certainement quelque chose du genre :

[error] [client 127.0.0.1] Request exceeded the limit of 10 internal redirects due to probable configuration error. Use ‘LimitInternalRecursion’ to increase the limit if necessary. Use ‘LogLevel debug’ to get a backtrace.

Passage en mode debug de Apache

Modifions la règle de log du site courant (soit dans /etc/apache2/sites-available/default soit dans /etc/apache2/apache.conf ou l’équivalent suivant votre configuration ou OS) et modifions la directive « LogLevel warn » en « LogLevel debug ». Ceci ne change rien dans notre cas, mais parfois cela s’avère utile…

Ajoutons ensuite les log1 de réécriture d’url (moi je l’ai ajouter dans /etc/apache2/mods-available/rewrite.conf puis j’ai fait un a2enmod rewrite (car le fichier .conf n’existait pas) et enfin j’ai relancer Apache) :
<IfModule mod_rewrite.c>
RewriteLog "/var/log/apache2/rewrite.log"
RewriteLogLevel 9
</IfModule>

Maintenant si vous aller voir dans error.log vous aurez :

[error] [client 127.0.0.1] Request exceeded the limit of 10 internal redirects due to probable configuration error. Use ‘LimitInternalRecursion’ to increase the limit if necessary. Use ‘LogLevel debug’ to get a backtrace.
[debug] core.c(3063): [client 127.0.0.1] r->uri = /…/erreur/403
[debug] core.c(3069): [client 127.0.0.1] redirected from r->uri = /…/erreur/403
[debug] core.c(3069): [client 127.0.0.1] redirected from r->uri = /…/erreur/403
[debug] core.c(3069): [client 127.0.0.1] redirected from r->uri = /…/erreur/403
[debug] core.c(3069): [client 127.0.0.1] redirected from r->uri = /…/erreur/403
[debug] core.c(3069): [client 127.0.0.1] redirected from r->uri = /…/erreur/403
[debug] core.c(3069): [client 127.0.0.1] redirected from r->uri = /…/erreur/403
[debug] core.c(3069): [client 127.0.0.1] redirected from r->uri = /…/erreur/403
[debug] core.c(3069): [client 127.0.0.1] redirected from r->uri = /…/erreur/403
[debug] core.c(3069): [client 127.0.0.1] redirected from r->uri = /…/erreur/403
[debug] core.c(3069): [client 127.0.0.1] redirected from r->uri = /…/test:s

Donc on voit que la redirection s’effectue correctement mais qu’il « n’accroche » pas. Regardons donc les logs de la réécriture d’URL (rewrite.log) :

/initial] (3) [perdir /…/] strip per-dir prefix: /…/test:s -> test:s
/initial] (3) [perdir /…/] applying pattern ‘.*’ to uri ‘test:s’
/initial] (4) [perdir /…/] RewriteCond: input= » pattern=’200′ => not-matched
/initial] (3) [perdir /…/] strip per-dir prefix: /…/test:s -> test:s
/initial] (3) [perdir /…/] applying pattern ‘.*’ to uri ‘test:s’
/initial] (4) [perdir /…/] RewriteCond: input=’GET /…/test:s HTTP/1.1′ pattern=’!^[A-Z]{3,9}\ [a-zA-Z0-9\.\+_/\-\?\=\&]+\ HTTP/’ [NC] => matched
/initial] (2) [perdir /…/] forcing responsecode 403 for /…/test:s
/initial/redir#1] (3) [perdir /…/] add path info postfix: /…/erreur -> /…/erreur/403
/initial/redir#1] (3) [perdir /…/] strip per-dir prefix: /…/erreur/403 -> erreur/403
/initial/redir#1] (3) [perdir /…/] applying pattern ‘.*’ to uri ‘erreur/403′
/initial/redir#1] (4) [perdir /…/] RewriteCond: input=’403′ pattern=’200’ => not-matched
/initial/redir#1] (3) [perdir /…/] add path info postfix: /…/erreur -> /…/erreur/403
/initial/redir#1] (3) [perdir /…/] strip per-dir prefix: /…/erreur/403 -> erreur/403
/initial/redir#1] (3) [perdir /…/] applying pattern ‘.*’ to uri ‘erreur/403′
/initial/redir#1] (4) [perdir /…/] RewriteCond: input=’GET /…/test:s HTTP/1.1′ pattern=’!^[A-Z]{3,9}\ [a-zA-Z0-9\.\+_/\-\?\=\&]+\ HTTP/’ [NC] => matched
/initial/redir#1] (2) [perdir /…/] forcing responsecode 403 for /…/erreur
/initial/redir#2] (3) [perdir /…/] add path info postfix: /…/erreur -> /…/erreur/403
/initial/redir#2] (3) [perdir /…/] strip per-dir prefix: /…/erreur/403 -> erreur/403
/initial/redir#2] (3) [perdir /…/] applying pattern ‘.*’ to uri ‘erreur/403′
/initial/redir#2] (4) [perdir /…/] RewriteCond: input=’403′ pattern=’200’ => not-matched
/initial/redir#2] (3) [perdir /…/] add path info postfix: /…/erreur -> /…/erreur/403
/initial/redir#2] (3) [perdir /…/] strip per-dir prefix: /…/erreur/403 -> erreur/403
/initial/redir#2] (3) [perdir /…/] applying pattern ‘.*’ to uri ‘erreur/403′
/initial/redir#2] (4) [perdir /…/] RewriteCond: input=’GET /…/test:s HTTP/1.1′ pattern=’!^[A-Z]{3,9}\ [a-zA-Z0-9\.\+_/\-\?\=\&]+\ HTTP/’ [NC] => matched
/initial/redir#2] (2) [perdir /…/] forcing responsecode 403 for /…/erreur
/initial/redir#3] (3) [perdir /…/] add path info postfix: /…/erreur -> /…/erreur/403
/initial/redir#3] (3) [perdir /…/] strip per-dir prefix: /…/erreur/403 -> erreur/403
/initial/redir#3] (3) [perdir /…/] applying pattern ‘.*’ to uri ‘erreur/403′
/initial/redir#3] (4) [perdir /…/] RewriteCond: input=’403′ pattern=’200’ => not-matched
/initial/redir#3] (3) [perdir /…/] add path info postfix: /…/erreur -> /…/erreur/403
/initial/redir#3] (3) [perdir /…/] strip per-dir prefix: /…/erreur/403 -> erreur/403
/initial/redir#3] (3) [perdir /…/] applying pattern ‘.*’ to uri ‘erreur/403′
/initial/redir#3] (4) [perdir /…/] RewriteCond: input=’GET /…/test:s HTTP/1.1′ pattern=’!^[A-Z]{3,9}\ [a-zA-Z0-9\.\+_/\-\?\=\&]+\ HTTP/’ [NC] => matched
/initial/redir#3] (2) [perdir /…/] forcing responsecode 403 for /…/erreur
/initial/redir#4] (3) [perdir /…/] add path info postfix: /…/erreur -> /…/erreur/403
/initial/redir#4] (3) [perdir /…/] strip per-dir prefix: /…/erreur/403 -> erreur/403
/initial/redir#4] (3) [perdir /…/] applying pattern ‘.*’ to uri ‘erreur/403′
/initial/redir#4] (4) [perdir /…/] RewriteCond: input=’403′ pattern=’200’ => not-matched
/initial/redir#4] (3) [perdir /…/] add path info postfix: /…/erreur -> /…/erreur/403
/initial/redir#4] (3) [perdir /…/] strip per-dir prefix: /…/erreur/403 -> erreur/403
/initial/redir#4] (3) [perdir /…/] applying pattern ‘.*’ to uri ‘erreur/403′
/initial/redir#4] (4) [perdir /…/] RewriteCond: input=’GET /…/test:s HTTP/1.1′ pattern=’!^[A-Z]{3,9}\ [a-zA-Z0-9\.\+_/\-\?\=\&]+\ HTTP/’ [NC] => matched
/initial/redir#4] (2) [perdir /…/] forcing responsecode 403 for /…/erreur
/initial/redir#5] (3) [perdir /…/] add path info postfix: /…/erreur -> /…/erreur/403
/initial/redir#5] (3) [perdir /…/] strip per-dir prefix: /…/erreur/403 -> erreur/403
/initial/redir#5] (3) [perdir /…/] applying pattern ‘.*’ to uri ‘erreur/403′
/initial/redir#5] (4) [perdir /…/] RewriteCond: input=’403′ pattern=’200’ => not-matched
/initial/redir#5] (3) [perdir /…/] add path info postfix: /…/erreur -> /…/erreur/403
/initial/redir#5] (3) [perdir /…/] strip per-dir prefix: /…/erreur/403 -> erreur/403
/initial/redir#5] (3) [perdir /…/] applying pattern ‘.*’ to uri ‘erreur/403′
/initial/redir#5] (4) [perdir /…/] RewriteCond: input=’GET /…/test:s HTTP/1.1′ pattern=’!^[A-Z]{3,9}\ [a-zA-Z0-9\.\+_/\-\?\=\&]+\ HTTP/’ [NC] => matched
/initial/redir#5] (2) [perdir /…/] forcing responsecode 403 for /…/erreur
/initial/redir#6] (3) [perdir /…/] add path info postfix: /…/erreur -> /…/erreur/403
/initial/redir#6] (3) [perdir /…/] strip per-dir prefix: /…/erreur/403 -> erreur/403
/initial/redir#6] (3) [perdir /…/] applying pattern ‘.*’ to uri ‘erreur/403′
/initial/redir#6] (4) [perdir /…/] RewriteCond: input=’403′ pattern=’200’ => not-matched
/initial/redir#6] (3) [perdir /…/] add path info postfix: /…/erreur -> /…/erreur/403
/initial/redir#6] (3) [perdir /…/] strip per-dir prefix: /…/erreur/403 -> erreur/403
/initial/redir#6] (3) [perdir /…/] applying pattern ‘.*’ to uri ‘erreur/403′
/initial/redir#6] (4) [perdir /…/] RewriteCond: input=’GET /…/test:s HTTP/1.1′ pattern=’!^[A-Z]{3,9}\ [a-zA-Z0-9\.\+_/\-\?\=\&]+\ HTTP/’ [NC] => matched
/initial/redir#6] (2) [perdir /…/] forcing responsecode 403 for /…/erreur
/initial/redir#7] (3) [perdir /…/] add path info postfix: /…/erreur -> /…/erreur/403
/initial/redir#7] (3) [perdir /…/] strip per-dir prefix: /…/erreur/403 -> erreur/403
/initial/redir#7] (3) [perdir /…/] applying pattern ‘.*’ to uri ‘erreur/403′
/initial/redir#7] (4) [perdir /…/] RewriteCond: input=’403′ pattern=’200’ => not-matched
/initial/redir#7] (3) [perdir /…/] add path info postfix: /…/erreur -> /…/erreur/403
/initial/redir#7] (3) [perdir /…/] strip per-dir prefix: /…/erreur/403 -> erreur/403
/initial/redir#7] (3) [perdir /…/] applying pattern ‘.*’ to uri ‘erreur/403′
/initial/redir#7] (4) [perdir /…/] RewriteCond: input=’GET /…/test:s HTTP/1.1′ pattern=’!^[A-Z]{3,9}\ [a-zA-Z0-9\.\+_/\-\?\=\&]+\ HTTP/’ [NC] => matched
/initial/redir#7] (2) [perdir /…/] forcing responsecode 403 for /…/erreur
/initial/redir#8] (3) [perdir /…/] add path info postfix: /…/erreur -> /…/erreur/403
/initial/redir#8] (3) [perdir /…/] strip per-dir prefix: /…/erreur/403 -> erreur/403
/initial/redir#8] (3) [perdir /…/] applying pattern ‘.*’ to uri ‘erreur/403′
/initial/redir#8] (4) [perdir /…/] RewriteCond: input=’403′ pattern=’200’ => not-matched
/initial/redir#8] (3) [perdir /…/] add path info postfix: /…/erreur -> /…/erreur/403
/initial/redir#8] (3) [perdir /…/] strip per-dir prefix: /…/erreur/403 -> erreur/403
/initial/redir#8] (3) [perdir /…/] applying pattern ‘.*’ to uri ‘erreur/403′
/initial/redir#8] (4) [perdir /…/] RewriteCond: input=’GET /…/test:s HTTP/1.1′ pattern=’!^[A-Z]{3,9}\ [a-zA-Z0-9\.\+_/\-\?\=\&]+\ HTTP/’ [NC] => matched
/initial/redir#8] (2) [perdir /…/] forcing responsecode 403 for /…/erreur
/initial/redir#9] (3) [perdir /…/] add path info postfix: /…/erreur -> /…/erreur/403
/initial/redir#9] (3) [perdir /…/] strip per-dir prefix: /…/erreur/403 -> erreur/403
/initial/redir#9] (3) [perdir /…/] applying pattern ‘.*’ to uri ‘erreur/403′
/initial/redir#9] (4) [perdir /…/] RewriteCond: input=’403′ pattern=’200’ => not-matched
/initial/redir#9] (3) [perdir /…/] add path info postfix: /…/erreur -> /…/erreur/403
/initial/redir#9] (3) [perdir /…/] strip per-dir prefix: /…/erreur/403 -> erreur/403
/initial/redir#9] (3) [perdir /…/] applying pattern ‘.*’ to uri ‘erreur/403′
/initial/redir#9] (4) [perdir /…/] RewriteCond: input=’GET /…/test:s HTTP/1.1′ pattern=’!^[A-Z]{3,9}\ [a-zA-Z0-9\.\+_/\-\?\=\&]+\ HTTP/’ [NC] => matched
/initial/redir#9] (2) [perdir /…/] forcing responsecode 403 for /…/erreur
/initial/redir#10] (3) [perdir /…/] add path info postfix: /…/erreur -> /…/erreur/403
/initial/redir#10] (3) [perdir /…/] strip per-dir prefix: /…/erreur/403 -> erreur/403
/initial/redir#10] (3) [perdir /…/] applying pattern ‘.*’ to uri ‘erreur/403′
/initial/redir#10] (4) [perdir /…/] RewriteCond: input=’403′ pattern=’200’ => not-matched
/initial/redir#10] (3) [perdir /…/] add path info postfix: /…/erreur -> /…/erreur/403
/initial/redir#10] (3) [perdir /…/] strip per-dir prefix: /…/erreur/403 -> erreur/403
/initial/redir#10] (3) [perdir /…/] applying pattern ‘.*’ to uri ‘erreur/403′
/initial/redir#10] (4) [perdir /…/] RewriteCond: input=’GET /…/test:s HTTP/1.1′ pattern=’!^[A-Z]{3,9}\ [a-zA-Z0-9\.\+_/\-\?\=\&]+\ HTTP/’ [NC] => matched
/initial/redir#10] (2) [perdir /…/] forcing responsecode 403 for /…/erreur

En gros, nous avions une boucle de redirection. Pour l’éviter ajoutons,d ans le .htaccess, une condition qui dira de ne pas rentrer dans le bloc, s’il y a une erreur 403 :
RewriteEngine on
#
RewriteCond %{ENV:REDIRECT_STATUS} !403
RewriteCond %{THE_REQUEST} !^[A-Z]{3,9}\ [a-zA-Z0-9\.\+_/\-\?\=\&]+\ HTTP/ [NC]
RewriteRule .* - [F,NS,L]
#
#if not a dir or a file
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
#
RewriteRule ^(.*)$ index.php?p=$1 [QSA,L]
ErrorDocument 403 /erreur/403

Maintenant tout fonctionne comme prévu!

La fin

Pour finir, ceci montre encore l’importance des logs lors de la rencontre de problème. S’il y a moyen penser a les activer, cela simplifie franchement la tâche de débogage que cela soit pour Apache mais aussi pour tout projet informatique.

Un dernier conseil, pensez a désactiver le mode debug de Apache une fois fini!

S’il y avait un site a conseiller sur ce sujet (en dehors du manuel Apache) je vous renverrait vers askapache

  1. Si le fichier ne se créer pas tout seul penser à le créer