web-dev-qa-db-fra.com

Fonctionnalités cachées de mod_rewrite

Il semble y avoir un nombre décent de mod_rewrite des fils flottant récemment avec un peu de confusion sur le fonctionnement de certains aspects. En conséquence, j'ai compilé quelques notes sur les fonctionnalités communes, et peut-être quelques nuances gênantes.

Quelles autres fonctionnalités/problèmes courants avez-vous rencontrés en utilisant mod_rewrite?

119
Owen

Où placer les règles mod_rewrite

mod_rewrite des règles peuvent être placées dans le httpd.conf fichier, ou dans le .htaccess fichier. si vous avez accès à httpd.conf, placer des règles ici offrira un avantage en termes de performances (car les règles sont traitées une fois, par opposition à chaque fois que .htaccess le fichier est appelé).

Journalisation des requêtes mod_rewrite

La journalisation peut être activée à partir du httpd.conf fichier (y compris <Virtual Host>):

# logs can't be enabled from .htaccess
# loglevel > 2 is really spammy!
RewriteLog /path/to/rewrite.log
RewriteLogLevel 2

Cas d'utilisation courants

  1. Pour acheminer toutes les demandes vers un seul point:

    RewriteEngine on
    # ignore existing files
    RewriteCond %{REQUEST_FILENAME} !-f   
    # ignore existing directories
    RewriteCond %{REQUEST_FILENAME} !-d   
    # map requests to index.php and append as a query string
    RewriteRule ^(.*)$ index.php?query=$1 
    

    Depuis Apache 2.2.16, vous pouvez également utiliser FallbackResource .

  2. Gestion des redirections 301/302:

    RewriteEngine on
    # 302 Temporary Redirect (302 is the default, but can be specified for clarity)
    RewriteRule ^oldpage\.html$ /newpage.html [R=302]  
    # 301 Permanent Redirect
    RewriteRule ^oldpage2\.html$ /newpage.html [R=301] 
    

    Remarque: les redirections externes sont implicitement 302 redirections:

    # this rule:
    RewriteRule ^somepage\.html$ http://google.com
    # is equivalent to:
    RewriteRule ^somepage\.html$ http://google.com [R]
    # and:
    RewriteRule ^somepage\.html$ http://google.com [R=302]
    
  3. Forcer SSL

    RewriteEngine on
    RewriteCond %{HTTPS} off
    RewriteRule ^(.*)$ https://example.com/$1 [R,L]
    
  4. Drapeaux communs:

    • [R] ou [redirect] - force une redirection (par défaut une redirection 302 temporaire)
    • [R=301] ou [redirect=301] - force une redirection permanente 301
    • [L] ou [last] - arrête le processus de réécriture (voir la note ci-dessous dans les pièges courants)
    • [NC] ou [nocase] - spécifiez que la correspondance ne doit pas respecter la casse


    L'utilisation de la forme longue des drapeaux est souvent plus lisible et aidera ceux qui viendront lire votre code plus tard.

    Vous pouvez séparer plusieurs indicateurs par une virgule:

    RewriteRule ^olddir(.*)$ /newdir$1 [L,NC]
    

Pièges courants

  1. Mélange mod_alias style redirige avec mod_rewrite

    # Bad
    Redirect 302 /somepage.html http://example.com/otherpage.html
    RewriteEngine on
    RewriteRule ^(.*)$ index.php?query=$1
    
    # Good (use mod_rewrite for both)
    RewriteEngine on
    # 302 redirect and stop processing
    RewriteRule ^somepage.html$ /otherpage.html [R=302,L] 
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    # handle other redirects
    RewriteRule ^(.*)$ index.php?query=$1                 
    

    Remarque: vous pouvez mélanger mod_alias avec mod_rewrite, mais cela implique plus de travail que la simple gestion des redirections de base comme ci-dessus.

  2. Le contexte affecte la syntaxe

    Dans .htaccess fichiers, une barre oblique n'est pas utilisée dans le modèle RewriteRule:

    # given: GET /directory/file.html
    
    # .htaccess
    # result: /newdirectory/file.html
    RewriteRule ^directory(.*)$ /newdirectory$1
    
    # .htaccess
    # result: no match!
    RewriteRule ^/directory(.*)$ /newdirectory$1
    
    # httpd.conf
    # result: /newdirectory/file.html
    RewriteRule ^/directory(.*)$ /newdirectory$1
    
    # Putting a "?" after the slash will allow it to work in both contexts:
    RewriteRule ^/?directory(.*)$ /newdirectory$1
    
  3. [L] n'est pas le dernier! (parfois)

    Le [L] flag arrête de traiter toutes les règles de réécriture supplémentaires pour cela passer par l'ensemble de règles. Cependant, si l'URL a été modifiée dans cette passe et que vous êtes dans le .htaccess le contexte ou le <Directory>, votre demande modifiée sera à nouveau transmise via le moteur d'analyse d'URL. Et lors de la prochaine passe, cela peut correspondre à une règle différente cette fois. Si vous ne comprenez pas cela, cela ressemble souvent à votre [L] le drapeau n'a eu aucun effet.

    # processing does not stop here
    RewriteRule ^dirA$ /dirB [L] 
    # /dirC will be the final result
    RewriteRule ^dirB$ /dirC     
    

    Notre journal de réécriture montre que les règles sont exécutées deux fois et que l'URL est mise à jour deux fois:

    rewrite 'dirA' -> '/dirB'
    internal redirect with /dirB [INTERNAL REDIRECT]
    rewrite 'dirB' -> '/dirC'
    

    La meilleure solution consiste à utiliser le [END] flag ( voir documentation Apache ) au lieu de [L] flag, si vous voulez vraiment arrêter tout traitement ultérieur des règles (et des passes suivantes). Cependant, le [END] L'indicateur n'est disponible que pour Apache v2.3.9 + , donc si vous avez la version 2.2 ou inférieure, vous êtes bloqué avec juste le [L] drapeau.

    Pour les versions antérieures, vous devez vous fier aux instructions RewriteCond pour empêcher la correspondance des règles lors des passages ultérieurs du moteur d'analyse d'URL.

    # Only process the following RewriteRule if on the first pass
    RewriteCond %{ENV:REDIRECT_STATUS} ^$
    RewriteRule ...
    

    Ou vous devez vous assurer que vos règles de réécriture sont dans un contexte (c'est-à-dire httpd.conf) qui n'entraînera pas une nouvelle analyse de votre demande.

203
Owen

si vous avez besoin de "bloquer" les redirections/réécritures internes de se produire dans le .htaccess, jetez un oeil à la

RewriteCond %{ENV:REDIRECT_STATUS} ^$

condition, comme discuté ici .

21
mromaine

L'accord avec RewriteBase:

Vous devez presque toujours définir RewriteBase. Si vous ne le faites pas, Apache suppose que votre base est le chemin du disque physique vers votre répertoire. Commencez donc par ceci:

RewriteBase /
18
Sean McMillan

Autres pièges:

1- Parfois, c'est une bonne idée de désactiver MultiViews

Options -MultiViews

Je ne connais pas bien toutes les capacités de MultiViews, mais je sais que cela perturbe mes règles mod_rewrite lorsqu'il est actif, car l'une de ses propriétés est d'essayer de `` deviner '' une extension d'un fichier qu'il pense que je cherche .

Je vais vous expliquer: Supposons que vous ayez 2 fichiers php dans votre répertoire web, file1.php et file2.php et que vous ajoutez ces conditions et règles à votre .htaccess:

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ file1.php/$1 

Vous supposez que toutes les URL qui ne correspondent pas à un fichier ou à un répertoire seront récupérées par file1.php. Surprise! Cette règle n'est pas respectée pour l'url http: // myhost/file2/somepath . Au lieu de cela, vous êtes pris dans file2.php.

Ce qui se passe, c'est que MultiViews a deviné automatiquement que l'URL que vous vouliez réellement était http: //myhost/file2.php/somepath et vous y a conduit avec plaisir.

Maintenant, vous n'avez aucune idée de ce qui vient de se passer et vous vous interrogez à ce stade sur tout ce que vous pensiez savoir sur mod_rewrite. Vous commencez ensuite à jouer avec les règles pour essayer de donner un sens à la logique derrière cette nouvelle situation, mais plus vous testez, moins cela a de sens.

Ok, en bref, si vous voulez que mod_rewrite fonctionne d'une manière qui se rapproche de la logique, désactiver MultiViews est un pas dans la bonne direction.

2- Activez FollowSymlinks

Options +FollowSymLinks 

Celui-là, je ne connais pas vraiment les détails, mais je l'ai vu mentionné à plusieurs reprises, alors faites-le.

13
Michael Ekoka

L'équation peut être faite avec l'exemple suivant:

RewriteCond %{REQUEST_URI} ^/(server0|server1).*$ [NC]
# %1 is the string that was found above
# %1<>%{HTTP_COOKIE} concatenates first macht with mod_rewrite variable -> "test0<>foo=bar;"
#RewriteCond search for a (.*) in the second part -> \1 is a reference to (.*)
# <> is used as an string separator/indicator, can be replaced by any other character
RewriteCond %1<>%{HTTP_COOKIE} !^(.*)<>.*stickysession=\1.*$ [NC]
RewriteRule ^(.*)$ https://notmatch.domain.com/ [R=301,L]

Équilibrage de charge dynamique:

Si vous utilisez le mod_proxy pour équilibrer votre système, il est possible d'ajouter une plage dynamique de serveur de travail.

RewriteCond %{HTTP_COOKIE} ^.*stickysession=route\.server([0-9]{1,2}).*$ [NC]
RewriteRule (.*) https://worker%1.internal.com/$1 [P,L]
5
DrDol

Une meilleure compréhension du drapeau [L] s'impose. Le drapeau [L] est en dernier, il vous suffit de comprendre ce qui provoquera le routage de votre demande via le moteur d'analyse d'URL. À partir des documents ( http://httpd.Apache.org/docs/2.2/rewrite/flags.html#flag_l ) (c'est moi qui souligne):

L'indicateur [L] oblige mod_rewrite à arrêter le traitement de l'ensemble de règles. Dans la plupart des contextes, cela signifie que si la règle correspond, aucune autre règle ne sera traitée. Cela correspond à la dernière commande en Perl ou à la commande break en C. Utilisez cet indicateur pour indiquer que la règle actuelle doit être appliquée immédiatement sans prendre en compte d'autres règles.

Si vous utilisez RewriteRule dans les fichiers .htaccess ou dans <Directory> sections, il est important de comprendre comment les règles sont traitées. La forme simplifiée de ceci est qu'une fois les règles traitées, la demande réécrite est renvoyée à l'URL moteur d'analyse pour faire ce qu'il peut avec. Il est possible que lors du traitement de la demande réécrite, le fichier .htaccess ou <Directory> la section peut être rencontrée à nouveau, et ainsi l'ensemble de règles peut être exécuté à nouveau depuis le début. Le plus souvent, cela se produit si l'une des règles entraîne une redirection - interne ou externe - provoquant le redémarrage du processus de demande.

Ainsi, le drapeau [L] le fait arrête de traiter toute autre règle de réécriture pour ça passe à travers l'ensemble de règles. Cependant, si votre règle marquée avec [L] a modifié la demande et que vous êtes dans le contexte .htaccess ou <Directory>, votre demande modifiée sera à nouveau transmise via le moteur d'analyse d'URL. Et lors de la prochaine passe, cela peut correspondre à une règle différente cette fois. Si vous ne comprenez pas ce qui s'est passé, il semble que votre première règle de réécriture avec l'indicateur [L] n'a eu aucun effet.

La meilleure façon de contourner cela est d'utiliser l'indicateur [END] ( http://httpd.Apache.org/docs/current/rewrite/flags.html#flag_end ) au lieu de l'indicateur [L] , si vous voulez vraiment arrêter tout traitement ultérieur des règles (et la nouvelle analyse ultérieure). Cependant, l'indicateur [END] n'est disponible que pour Apache v2.3.9 +, donc si vous disposez de la version 2.2 ou inférieure, vous êtes bloqué uniquement avec l'indicateur [L]. Dans ce cas, vous devez vous fier aux instructions RewriteCond pour empêcher la correspondance des règles lors des passages ultérieurs du moteur d'analyse d'URL. Ou vous devez vous assurer que vos règles de réécriture sont dans un contexte (c'est-à-dire httpd.conf) qui n'entraînera pas une nouvelle analyse de votre demande.

4
JaredC

Une autre fonctionnalité intéressante est la réécriture des extensions de carte. Ils sont particulièrement utiles si vous avez un grand nombre d'hôtes/réécritures à gérer:

Ils sont comme un remplacement clé-valeur:

RewriteMap examplemap txt:/path/to/file/map.txt

Ensuite, vous pouvez utiliser un mappage dans vos règles comme:

RewriteRule ^/ex/(.*) ${examplemap:$1}

Vous trouverez plus d'informations sur ce sujet ici:

http://httpd.Apache.org/docs/2.0/mod/mod_rewrite.html#mapfunc

3
B.E.

mod_rewrite peut modifier certains aspects de la gestion des demandes sans modifier l'URL, par exemple définir des variables d'environnement, définir des cookies, etc. Ceci est incroyablement utile.

Définissez conditionnellement une variable d'environnement:

RewriteCond %{HTTP_COOKIE} myCookie=(a|b) [NC]
RewriteRule .* - [E=MY_ENV_VAR:%b]

Retourne une réponse 503: RewriteRule's [R] L'indicateur peut prendre une valeur non 3xx et renvoyer une réponse non redirigeante, par exemple pour les temps d'arrêt/maintenance gérés:

RewriteRule .* - [R=503,L]

renverra une réponse 503 (pas un redirection en soi).

De plus, mod_rewrite peut agir comme une interface surpuissante pour mod_proxy, vous pouvez donc le faire au lieu d'écrire des directives ProxyPass:

RewriteRule ^/(.*)$ balancer://cluster%{REQUEST_URI} [P,QSA,L]

Opinion: L'utilisation de RewriteRules et RewriteConds pour acheminer les demandes vers différentes applications ou équilibreurs de charge sur la base de pratiquement tous les aspects imaginables de la demande est tout simplement extrêmement puissante. Le contrôle des requêtes sur leur chemin vers le backend et la possibilité de modifier les réponses sur leur chemin de retour font de mod_rewrite l'endroit idéal pour centraliser toutes les configurations liées au routage.

Prenez le temps de l'apprendre, ça vaut le coup! :)

2
cweekly