Fonctionnalités cachées de mod_rewrite

119

Il semble y avoir un nombre décent de mod_rewritethreads ces derniers temps avec un peu de confusion sur la façon dont certains aspects fonctionnent. En conséquence, j'ai compilé quelques notes sur les fonctionnalités communes, et peut-être quelques nuances ennuyeuses.

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

Owen
la source
5
Voir aussi serverfault.com/questions/214512/…
Michael Myers

Réponses:

203

Où placer les règles mod_rewrite

mod_rewriteles règles peuvent être placées dans le httpd.conffichier ou dans le .htaccessfichier. si vous y 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 le .htaccessfichier est appelé).

Journalisation des requêtes mod_rewrite

La journalisation peut être activée à partir du httpd.conffichier (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 canaliser 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]- forcer une redirection (par défaut une redirection temporaire 302)
    • [R=301]ou [redirect=301]- forcer une redirection permanente 301
    • [L]ou [last]- arrêter le processus de réécriture (voir la note ci-dessous dans les pièges courants)
    • [NC]ou [nocase]- spécifier que la correspondance doit être insensible à la casse


    L'utilisation de la forme longue des indicateurs est souvent plus lisible et aidera les autres 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élanger les mod_aliasredirections de style avecmod_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_aliasavec 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 les .htaccessfichiers, une barre oblique de début 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! (quelquefois)

    L' [L]indicateur arrête de traiter toutes les règles de réécriture supplémentaires pour ce passage dans l'ensemble de règles . Cependant, si l'URL a été modifiée lors de cette passe et que vous êtes dans le .htaccesscontexte ou la <Directory>section, votre demande modifiée sera à nouveau renvoyée via le moteur d'analyse d'URL. Et au prochain passage, cela peut correspondre à une règle différente cette fois. Si vous ne comprenez pas cela, il semble souvent que votre [L]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 façon de contourner cela est d'utiliser l' [END]indicateur ( voir la documentation Apache ) au lieu de l' [L]indicateur, si vous voulez vraiment arrêter tout traitement ultérieur des règles (et les passes ultérieures). Cependant, l' [END]indicateur n'est disponible que pour Apache v2.3.9 + , donc si vous avez v2.2 ou une version inférieure, vous êtes coincé avec seulement l' [L]indicateur.

    Pour les versions antérieures, vous devez vous fier aux RewriteCondinstructions pour empêcher la correspondance des règles lors des passages suivants 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 RewriteRule sont dans un contexte (c'est-à-dire httpd.conf) qui ne provoquera pas une nouvelle analyse de votre demande.

Owen
la source
10
Mec, totalement le meilleur article sur Internet maintenant sur la réécriture de mod. Je déteste cette chose. Je suis un hérétique lighttpd à cause de combien je déteste mod_rewrite.
Kent Fredric
3
Ceci a été LE guide le plus utile que j'ai trouvé sur mod_rewrite jusqu'à présent. Le simple fait de découvrir RewriteLog a aidé à résoudre tant de problèmes que ce qui me prenait des jours à dépister s'est transformé en quelques minutes. (Je veux dire que les règles ont été écrites mais je ne pouvais pas comprendre pourquoi elles ne fonctionnaient pas)
Joe Chin
Message vieux de 1 an, mais l'une des choses les plus utiles que j'ai trouvées sur SO - pour moi.
Erik
3
Le [L]drapeau signifie qu'une règle est la dernière du traitement en cours, cela n'empêchera pas la réécriture, car ce sont des redirections internes, donc vous dirBappliquez au dirCprochain traitement htaccess. Seul RewriteRule ^(.*)$ index.php?query=$1sera une boucle infinie de redirections internes (en pratique, il se termine après 10 itérations). -1 parce que vous suggérez que [L] n'est pas le dernier . Ce n'est pas la fin du processus de réécriture, mais c'est le dernier .
kbec
3
Je pense que RewriteCond %{HTTPS} offc'est le moyen préféré de vérifier une connexion HTTPS (dans votre exemple de forcer le trafic non SSL vers HTTPS)
Madbreaks
22

si vous avez besoin de `` bloquer '' les redirections / réécritures internes dans le .htaccess, jetez un œil à la

RewriteCond %{ENV:REDIRECT_STATUS} ^$

condition, comme discuté ici .

mromaine
la source
Merci, cela vient de régler mon problème!
Matthew le
Merci pour moi aussi, sauveur de vie!
Benjamin
C'est en effet une bouée de sauvetage! Les gens devraient être plus conscients de cela. En fait, je vais proposer cela à toutes les questions sur .*avec le [L]drapeau , j'ai lu avant que je suis arrivé ici.
Qwerty
Je l' ai vu plusieurs modifications à ce 200, !=200, ^., ^$. Apparemment, la variable est définie sur 200pour une redirection, mais d'autres pages (erreurs et trucs) la définissent sur une certaine valeur. Maintenant , cela signifie que vous vérifiez soit si is empty, is not empty, is 200ou is not 200, en fonction de ce que vous avez besoin.
Qwerty
18

L'accord avec RewriteBase:

Vous devez presque toujours définir RewriteBase. Si vous ne le faites pas, apache devine que votre base est le chemin du disque physique vers votre répertoire. Alors commencez par ceci:

RewriteBase /
Sean McMillan
la source
Ah. Cela a totalement résolu le problème que j'avais. Merci pour ça!
Tom Savage
3
Une façon de dire RewriteBase ., ou quelque chose pour indiquer que l'URL doit rester la même, en changeant simplement ce que vous avez spécifié?
Jay K le
Merci, c'était une information inestimable. :)
AturSams
2
Vous devez uniquement définir RewriteBasesi vous utilisez la substitution de chemin relatif dans la RewriteRuledirective. Il vaut mieux éviter d'utiliser des chemins relatifs.
MrWhite
2
Je ne suis pas d'accord avec cette réponse. Dans notre équipe de développement, nous évitons RewriteBasecomplètement car presque tous les développeurs comprennent mal ce qu'il fait. Comme @ w3d l'a dit, vous n'en avez besoin que si vous voulez enregistrer des caractères et que vous voulez appliquer la même base à toutes vos RewriteRules dans un seul fichier. Votre code sera probablement plus clair pour les autres si vous l'évitez.
Simon East
13

Autres pièges:

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

Options -MultiViews

Je ne connais pas bien toutes les fonctionnalités de MultiViews, mais je sais que cela gâche 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 recherche .

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ègle à 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 automatiquement deviné que l'URL que vous vouliez réellement était http: //myhost/file2.php/somepath et vous y a volontiers emmené.

Maintenant, vous n'avez aucune idée de ce qui vient de se passer et vous remettez en question tout ce que vous pensiez savoir sur mod_rewrite. Vous commencez alors à 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- Activer FollowSymlinks

Options +FollowSymLinks 

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

Michael Ekoka
la source
Merci :) J'ai remarqué des surprises inattendues comme / log / activity se transformant en /log.txt/activity .. Merci pour le conseil :) .. dommage que les ordinateurs ne se moquent jamais de choses inattendues comme séduire accidentellement toutes vos collègues féminines sur facebook :)
AturSams
1
+FollowSymLinksest mentionné dans la documentation comme étant obligatoire pour mod_rewritetravailler du tout, pour de vagues raisons de sécurité.
Joey
Deux déclarations ici m'inquiètent énormément: `` Je ne suis pas bien versé sur toutes les capacités de MultiViews, mais je sais que cela gâche mes règles mod_rewrite lorsqu'il est actif '' et celui-ci `` Celui-là, je ne connais pas vraiment les détails de , mais je l'ai vu plusieurs fois mentionné, alors faites-le. J'aimerais que les gens comme vous n'écrivent pas de réponses sur SO sur des choses dont vous n'êtes pas sûr.
TheCarver
1
@PaparazzoKid: Je pense que vous prenez SO pour une encyclopédie. C'est une communauté de personnes qui se réunissent pour comprendre la technologie avec laquelle elles travaillent. Contrairement à AW White et Joey avant vous, votre commentaire est presque vide de valeur. MV et FSL sont deux des nombreuses options d'Apache. Ma réponse concerne les pièges lorsque vous travaillez avec mod_rw en particulier, un module séparé, qui entre en conflit avec certaines options et fonctionne avec d'autres. J'ai expliqué comment MV affecte mod_rw et mentionné que + FSL est une recommandation populaire. Joey a confirmé que c'était en fait obligatoire. Qu'apportez-vous à la table?
Michael Ekoka
Merci. Je viens de passer la meilleure partie d'une heure à faire fonctionner un ancien site et à essayer de déboguer les règles de réécriture, seulement pour constater que MultiViews remplaçait tout.
Andrew McCombe
5

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]
DrDol
la source
4

Une meilleure compréhension de l'indicateur [L] s'impose. Le drapeau [L] est le dernier, il vous suffit de comprendre ce qui provoquera le routage de votre requête via le moteur d'analyse d'URL. À partir de la documentation ( 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 des fichiers .htaccess ou dans des <Directory>sections , il est important de comprendre comment les règles sont traitées. La forme simplifiée de ceci est qu'une fois que les règles ont été traitées, la requête réécrite est renvoyée au moteur d'analyse d'URL pour faire ce qu'il peut avec. Il est possible que lorsque la demande réécrite est traitée, le fichier ou la<Directory> section.htaccesspeut être rencontré à nouveau, et ainsi le jeu de règles peut être exécuté à nouveau depuis le début. Le plus souvent, cela se produit si l'une des règles provoque une redirection - interne ou externe - provoquant le redémarrage du processus de demande.

Ainsi , le [L] indicateur ne Arrêter le traitement des règles de réécriture supplémentaires pour ceux qui passent à 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 dans la <Directory>section, votre demande modifiée sera à nouveau renvoyée via le moteur d'analyse d'URL. Et au prochain passage, 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 analyse ultérieure). Cependant, l'indicateur [END] n'est disponible que pour Apache v2.3.9 +, donc si vous avez v2.2 ou une version antérieure, vous êtes coincé avec seulement l'indicateur [L]. Dans ce cas, vous devez vous fier aux instructions RewriteCond pour empêcher la mise en correspondance des règles lors des passages ultérieurs du moteur d'analyse d'URL. Ou vous devez vous assurer que vos RewriteRule sont dans un contexte (ie httpd.conf) qui ne provoquera pas une nouvelle analyse de votre requête.

JaredC
la source
3

Les expansions de réécriture de la carte sont une autre fonctionnalité intéressante. Ils sont particulièrement utiles si vous avez une énorme quantité d'hôtes / réécritures à gérer:

Ils sont comme un remplacement de valeur-clé:

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

ÊTRE
la source
Ignorez cette fonctionnalité si vous utilisez des .htaccessréécritures basées sur des réécritures. Cela ne fonctionne pas dans ce contexte.
TerryE
2
La directive RewriteMap doit être utilisée dans le contexte du serveur (httpd.conf), mais une fois définie, vous pouvez utiliser la carte via RewriteRule dans un fichier .htaccess.
JaredC
2

mod_rewrite peut modifier certains aspects de la gestion des requêtes sans modifier l'URL, par exemple en définissant des variables d'environnement, en définissant 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]

Renvoyer une réponse 503: RewriteRulel' [R]indicateur de s peut prendre une valeur non-3xx et renvoyer une réponse non redirigante, par exemple pour les temps d'arrêt / maintenance gérés:

RewriteRule .* - [R=503,L]

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

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

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

Opinion: L'utilisation de RewriteRules et RewriteConds pour acheminer des requêtes vers différentes applications ou équilibreurs de charge basés sur pratiquement tous les aspects imaginables de la requête est tout simplement extrêmement puissante. Le contrôle des requêtes en route vers le backend et la possibilité de modifier les réponses en revenant 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 vraiment le coup! :)

semaine
la source