Référence: mod_rewrite, réécriture d'URL et «jolis liens» expliqués

142

«Jolis liens» est un sujet souvent demandé, mais rarement expliqué en détail. mod_rewrite est une façon de créer de "jolis liens", mais c'est complexe et sa syntaxe est très laconique, difficile à comprendre, et la documentation suppose un certain niveau de maîtrise de HTTP. Quelqu'un peut-il expliquer en termes simples comment fonctionnent les "jolis liens" et comment mod_rewrite peut être utilisé pour les créer?

Autres noms communs, alias, termes pour les URL propres: URL RESTful, URL conviviales, URL optimisées pour le référencement , slugging et URL MVC (probablement un abus de langage)

déceler
la source
2
Slug ou Slugging est un autre alias / terme commun pour les jolies URL.
Mike B
2
@Mike En quelque sorte, mais les slugs font souvent partie de jolies URL. Un slug est assez spécifique lorsque, par exemple, le titre d'un article est transformé en une forme conviviale pour les URL qui agit ensuite comme l'identifiant de cet article. Ainsi reference-mod-rewrite-url-rewriting-explainedest le slug, /questions/20563772/reference-mod-rewrite-url-rewriting-explainedest la jolie URL.
deceze
2
Je pense que les balises .htaccesset mod-rewritedevraient être mises à jour pour inclure un lien vers cette question, car elles couvrent une grande partie de ce qui est demandé régulièrement. Pensées?
Mike Rockétt

Réponses:

110

Pour comprendre ce qu'est mod_rewrite, vous devez d'abord comprendre le fonctionnement d'un serveur Web. Un serveur Web répond aux requêtes HTTP . Une requête HTTP à son niveau le plus basique ressemble à ceci:

GET /foo/bar.html HTTP/1.1

Il s'agit de la simple demande d'un navigateur à un serveur Web qui lui demande l' URL /foo/bar.html . Il est important de souligner qu'il ne demande pas de fichier , il ne demande qu'une URL arbitraire. La demande peut également ressembler à ceci:

GET /foo/bar?baz=42 HTTP/1.1

C'est tout aussi valide une demande d'URL, et cela n'a plus évidemment rien à voir avec les fichiers.

Le serveur Web est une application écoutant sur un port, acceptant les requêtes HTTP arrivant sur ce port et renvoyant une réponse. Un serveur Web est entièrement libre de répondre à toute demande de la manière qu'il juge appropriée / de la manière dont vous l'avez configuré pour répondre. Cette réponse n'est pas un fichier, c'est une réponse HTTP qui peut ou non avoir quelque chose à voir avec des fichiers physiques sur n'importe quel disque. Un serveur Web n'a pas besoin d'être Apache, il existe de nombreux autres serveurs Web qui ne sont que des programmes qui s'exécutent de manière persistante et sont attachés à un port qui répondent aux requêtes HTTP. Vous pouvez en écrire un vous-même. Ce paragraphe visait à vous dissocier de toute notion selon laquelle les URL sont directement égales à des fichiers, ce qui est vraiment important à comprendre. :)

La configuration par défaut de la plupart des serveurs Web consiste à rechercher un fichier qui correspond à l'URL sur le disque dur. Si la racine du document du serveur est définie sur, par exemple /var/www, il peut rechercher si le fichier /var/www/foo/bar.htmlexiste et le servir si tel est le cas. Si les extrémités de fichier dans « .php » il appellera l'interpréteur PHP et puis retourner le résultat. Toute cette association est entièrement configurable; un fichier n'a pas besoin de se terminer par ".php" pour que le serveur Web l'exécute via l'interpréteur PHP, et l'URL n'a pas besoin de correspondre à un fichier particulier sur le disque pour que quelque chose se produise.

mod_rewrite est un moyen de réécrire la gestion des requêtes internes. Lorsque le serveur Web reçoit une demande pour l'URL /foo/bar, vous pouvez réécrire cette URL dans quelque chose d'autre avant que le serveur Web ne recherche un fichier sur le disque correspondant. Exemple simple:

RewriteEngine On
RewriteRule   /foo/bar /foo/baz

Cette règle dit chaque fois qu'une requête correspond à "/ foo / bar", réécrivez-la dans "/ foo / baz". La demande sera alors traitée comme si elle /foo/bazavait été demandée à la place. Cela peut être utilisé pour différents effets, par exemple:

RewriteRule (.*) $1.html

Cette règle correspond à tout ( .*) et le capture ( (..)), puis la réécrit pour ajouter ".html". En d'autres termes, s'il /foo/bars'agissait de l'URL demandée, elle sera traitée comme si elle /foo/bar.htmlavait été demandée. Voir http://regular-expressions.info pour plus d'informations sur la correspondance, la capture et les remplacements d'expressions régulières.

Une autre règle souvent rencontrée est la suivante:

RewriteRule (.*) index.php?url=$1

Ceci, encore une fois, correspond à tout et le réécrit dans le fichier index.php avec l'URL demandée à l'origine ajoutée dans le urlparamètre de requête. C'est-à-dire que pour toutes les demandes entrantes, le fichier index.php est exécuté et ce fichier aura accès à la demande d'origine dans $_GET['url'], afin qu'il puisse faire tout ce qu'il veut avec.

Vous mettez principalement ces règles de réécriture dans le fichier de configuration de votre serveur Web . Apache vous permet également * de les mettre dans un fichier appelé .htaccessdans la racine de votre document (c'est-à-dire à côté de vos fichiers .php).

* Si autorisé par le fichier de configuration Apache principal; c'est facultatif, mais souvent activé.

Qu'est - ce que mod_rewrite ne pas faire

mod_rewrite ne rend pas par magie toutes vos URL "jolies". C'est un malentendu courant. Si vous avez ce lien dans votre site Web:

<a href="https://stackoverflow.com/my/ugly/link.php?is=not&amp;very=pretty">

il n'y a rien que mod_rewrite puisse faire pour rendre ça joli. Pour en faire un joli lien, vous devez:

  1. Changez le lien en un joli lien:

    <a href="https://stackoverflow.com/my/pretty/link">
    
  2. Utilisez mod_rewrite sur le serveur pour gérer la requête à l'URL en /my/pretty/linkutilisant l'une des méthodes décrites ci-dessus.

(On pourrait l'utiliser mod_substituteconjointement pour transformer les pages HTML sortantes et leurs liens contenus. Bien que cela représente généralement plus d'efforts que la simple mise à jour de vos ressources HTML.)

Il y a beaucoup de choses que mod_rewrite peut faire et des règles de correspondance très complexes que vous pouvez créer, y compris le chaînage de plusieurs réécritures, le proxy de requêtes vers un service ou une machine complètement différent, le retour de codes d'état HTTP spécifiques en tant que réponses, la redirection de requêtes, etc. C'est très puissant et peut être utilisé pour très bien si vous comprenez le mécanisme de requête-réponse HTTP fondamental. Cela ne rend pas automatiquement vos liens jolis.

Voir la documentation officielle pour tous les drapeaux et options possibles.

déceler
la source
6
Peut-être mentionner la directive FallbackResource introduite dans la version 2.2.16 comme moyen préféré de réécrire dans un répartiteur.
Darsstar
78

Pour développer la réponse de deceze , je voulais fournir quelques exemples et explications de certaines autres fonctionnalités de mod_rewrite.

Tous les exemples ci-dessous supposent que vous avez déjà inclus RewriteEngine Ondans votre .htaccessfichier.

Exemple de réécriture

Prenons cet exemple:

RewriteRule ^blog/([0-9]+)/([A-Za-z0-9-\+]+)/?$ /blog/index.php?id=$1&title=$2 [NC,L,QSA]

La règle est divisée en 4 sections:

  1. RewriteRule - lance la règle de réécriture
  2. ^blog/([0-9]+)/([A-Za-z0-9-\+]+)/?$ - C'est ce qu'on appelle le modèle, mais je vais simplement le désigner comme le côté gauche de la règle - ce que vous voulez réécrire
  3. blog/index.php?id=$1&title=$2 - appelé la substitution, ou côté droit d'une règle de réécriture - ce que vous voulez réécrire
  4. [NC,L,QSA] sont des indicateurs pour la règle de réécriture, séparés par une virgule, que j'expliquerai plus tard

La réécriture ci-dessus vous permettrait de créer un lien vers quelque chose comme /blog/1/foo/et il se chargerait réellement /blog/index.php?id=1&title=foo.

Côté gauche de la règle

  • ^indique le début du nom de la page - il sera donc réécrit example.com/blog/...mais pasexample.com/foo/blog/...
  • Chaque ensemble de (…)parenthèses représente une expression régulière que nous pouvons capturer en tant que variable dans la partie droite de la règle. Dans cet exemple:
    • Le premier ensemble de crochets - ([0-9]+)- correspond à une chaîne avec au moins 1 caractère de longueur et avec uniquement des valeurs numériques (c'est-à-dire 0-9). Cela peut être référencé avec $1dans le côté droit de la règle
    • Le deuxième jeu de parenthèses correspond à une chaîne avec au moins 1 caractère de longueur, contenant uniquement des caractères alphanumériques (AZ, az ou 0-9) ou -ou +(la note +est échappée avec une barre oblique inverse car sans l'échapper, cela s'exécutera comme une expression régulière caractère de répétition ). Cela peut être référencé avec $2dans le côté droit de la règle
  • ?signifie que le caractère précédent est facultatif, donc dans ce cas les deux /blog/1/foo/et /blog/1/fooréécriraient au même endroit
  • $ indique que c'est la fin de la chaîne que nous voulons faire correspondre

Drapeaux

Ces options sont ajoutées entre crochets à la fin de votre règle de réécriture pour spécifier certaines conditions. Encore une fois, il existe de nombreux indicateurs différents que vous pouvez lire dans la documentation , mais je vais passer en revue certains des indicateurs les plus courants:

NC

L'indicateur aucune casse signifie que la règle de réécriture est insensible à la casse, donc pour l'exemple de règle ci-dessus, cela signifierait que les deux /blog/1/foo/et /BLOG/1/foo/(ou toute variation de ceci) seraient mis en correspondance.

L

Le dernier indicateur indique qu'il s'agit de la dernière règle à traiter. Cela signifie que si et seulement si cette règle correspond, aucune autre règle ne sera évaluée dans le traitement de réécriture en cours. Si la règle ne correspond pas, toutes les autres règles seront essayées dans l'ordre habituel. Si vous ne définissez pas l' Lindicateur, toutes les règles suivantes seront ensuite appliquées à l' URL réécrite .

END

Depuis Apache 2.4, vous pouvez également utiliser l' [END]indicateur. Une règle correspondante avec elle mettra fin complètement au traitement des alias / réécriture. (Alors que le [L]drapeau peut souvent déclencher un deuxième tour, par exemple lors de la réécriture dans ou hors de sous-répertoires.)

QSA

L'indicateur d'ajout de chaîne de requête nous permet de transmettre des variables supplémentaires à l'URL spécifiée qui seront ajoutées aux paramètres get d'origine. Pour notre exemple, cela signifie que quelque chose comme /blog/1/foo/?comments=15chargerait/blog/index.php?id=1&title=foo&comments=15

R

Ce drapeau n'est pas celui que j'ai utilisé dans l'exemple ci-dessus, mais je pense qu'il vaut la peine d'être mentionné. Cela vous permet de spécifier une redirection http, avec la possibilité d'inclure un code d'état (par exemple R=301). Par exemple, si vous vouliez faire une redirection 301 sur / myblog / vers / blog / vous écririez simplement une règle quelque chose comme ceci:

RewriteRule ^/myblog/(*.)$ /blog/$1 [R=301,QSA,L]

Réécrire les conditions

Les conditions de réécriture rendent les réécritures encore plus puissantes, vous permettant de spécifier des réécritures pour des situations plus spécifiques. Il y a beaucoup de conditions que vous pouvez lire dans la documentation , mais je vais aborder quelques exemples courants et les expliquer:

# if the host doesn't start with www. then add it and redirect
RewriteCond %{HTTP_HOST} !^www\.
RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

Il s'agit d'une pratique très courante, qui ajoutera au début de votre domaine www.(s'il n'y est pas déjà) et exécutera une redirection 301. Par exemple, le charger vous http://example.com/blog/redirigerait vershttp://www.example.com/blog/

# if it cant find the image, try find the image on another domain
RewriteCond %{REQUEST_URI} \.(jpg|jpeg|gif|png)$ [NC]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule (.*)$ http://www.example.com/$1 [L]

C'est un peu moins courant, mais c'est un bon exemple de règle qui ne s'exécute pas si le nom de fichier est un répertoire ou un fichier qui existe sur le serveur.

  • %{REQUEST_URI} \.(jpg|jpeg|gif|png)$ [NC] n'exécutera la réécriture que pour les fichiers avec une extension de fichier jpg, jpeg, gif ou png (insensible à la casse).
  • %{REQUEST_FILENAME} !-f vérifiera si le fichier existe sur le serveur actuel et n'exécutera la réécriture que si ce n'est pas le cas
  • %{REQUEST_FILENAME} !-d vérifiera si le fichier existe sur le serveur actuel et n'exécutera la réécriture que si ce n'est pas le cas
  • La réécriture tentera de charger le même fichier sur un autre domaine
pseudo
la source
39

Références

Stack Overflow dispose de nombreuses autres ressources intéressantes pour commencer:

Et des aperçus des expressions régulières adaptées aux nouveaux arrivants, même:

Espaces réservés souvent utilisés

  • .*correspond à tout, même une chaîne vide. Vous ne voulez pas utiliser ce modèle partout, mais souvent dans la dernière règle de secours.
  • [^/]+est plus souvent utilisé pour les segments de chemin. Cela correspond à tout sauf à la barre oblique.
  • \d+ ne correspond qu'aux chaînes numériques.
  • \w+correspond aux caractères alphanumériques. C'est essentiellement un raccourci pour [A-Za-z0-9_].
  • [\w\-]+pour les segments de chemin de type "slug", en utilisant des lettres, des chiffres, des tirets - et _
  • [\w\-.,]+ajoute des points et des virgules. Préférez un \-tiret échappé dans les […]classes de caractères.
  • \.désigne une période littérale. Sinon, en .dehors de […]est un espace réservé pour tout symbole.

Chacun de ces espaces réservés est généralement placé (…)entre parenthèses en tant que groupe de capture. Et le motif entier souvent dans les ^………$marqueurs de début + fin. La citation de "modèles" est facultative.

Réécrire les règles

Les exemples suivants sont centrés sur PHP et un peu plus incrémentiels, plus faciles à adapter pour des cas similaires. Ce ne sont que des résumés, souvent liés à plus de variantes ou à des questions et réponses détaillées.

  • Cartographie statique
    /contact,/about

    Raccourcir quelques noms de page en schémas de fichiers internes est le plus simple:

     RewriteRule ^contact$  templ/contact.html
     RewriteRule ^about$    about.php
    
  • Identifiants numériques
    /object/123

    L'introduction de raccourcis tels que http://example.com/article/531les scripts PHP existants est également facile. L'espace réservé numérique peut simplement être remappé à un $_GETparamètre:

     RewriteRule ^article/(\d+)$    article-show.php?id=$1
     #                      └───────────────────────────┘
    
  • Espaces réservés de style slug
    /article/with-some-title-slug

    Vous pouvez facilement étendre cette règle pour autoriser les /article/title-stringespaces réservés:

     RewriteRule ^article/([\w-]+)$    article-show.php?title=$1
     #                       └────────────────────────────────┘
    

    Notez que votre script doit être capable (ou être adapté) de mapper ces titres vers des identifiants de base de données. RewriteRules seul ne peut pas créer ou deviner des informations à partir de rien.

  • Slugs avec préfixes numériques
    /readable/123-plus-title

    Par conséquent, vous verrez souvent des /article/529-title-slugchemins mixtes utilisés dans la pratique:

     RewriteRule ^article/(\d+)-([\w-]+)$    article.php?id=$1&title=$2
     #                      └───────────────────────────────┘
    

    Maintenant, vous pouvez simplement ignorer de title=$2toute façon, car votre script dépendra de toute façon de l'identifiant de la base de données. La -title-slugdécoration d'URL est devenue arbitraire.

  • Uniformité avec les listes alternatives
    /foo/… /bar/… /baz/…

    Si vous avez des règles similaires pour plusieurs chemins de pages virtuelles, vous pouvez les faire correspondre et les compacter avec |des listes alternatives. Et encore une fois, réaffectez-les simplement aux paramètres GET internes:

     #                               ┌─────────────────────────┐
     RewriteRule ^(blog|post|user)/(\w+)$  disp.php?type=$1&id=$2
     #               └───────────────────────────────────┘
    

    Vous pouvez les diviser en plusieurs individus RewriteRulesi cela devient trop complexe.

  • Distribution des URL associées à différents backends
    /date/SWITCH/backend

    Une utilisation plus pratique des listes alternatives consiste à mapper les chemins de requête vers des scripts distincts. Par exemple, pour fournir des URL uniformes pour une ancienne et une nouvelle application Web en fonction des dates:

     #                   ┌─────────────────────────────┐
     #                   │                 ┌───────────┼───────────────┐
     RewriteRule ^blog/(2009|2010|2011)/([\d-]+)/?$ old/blog.php?date=$2
     RewriteRule ^blog/(\d+)/([\d-]+)/?$  modern/blog/index.php?start=$2
     #                          └──────────────────────────────────────┘
    

    Cela remappe simplement les publications 2009-2011 sur un script, et toutes les autres années implicitement sur un autre gestionnaire. Notez la règle la plus spécifique qui vient en premier . Chaque script peut utiliser différents paramètres GET.

  • Autres délimiteurs que de simples /barres obliques
    /user-123-name

    Vous voyez le plus souvent RewriteRules pour simuler une structure de répertoire virtuel. Mais vous n'êtes pas obligé de ne pas être créatif. Vous pouvez également utiliser des -traits d'union pour la segmentation ou la structure.

     RewriteRule ^user-(\d+)$    show.php?what=user&id=$1
     #                   └──────────────────────────────┘
     # This could use `(\w+)` alternatively for user names instead of ids.
    

    Pour le /wiki:section:Page_Nameschéma également commun :

     RewriteRule ^wiki:(\w+):(\w+)$  wiki.php?sect=$1&page=$2 
     #                   └─────┼────────────────────┘       │
     #                         └────────────────────────────┘
    

    Parfois, il convient d'alterner entre /-delimiters et :ou .dans la même règle même. Ou ayez à nouveau deux RewriteRules pour mapper des variantes sur différents scripts.

  • Barre /oblique de fin facultative
    /dir=/dir/

    Lorsque vous optez pour des chemins de style répertoire, vous pouvez le rendre accessible avec et sans /

     RewriteRule ^blog/([\w-]+)/?$  blog/show.php?id=$1
     #                         ┗┛
    

    Maintenant, cela gère à la fois http://example.com/blog/123et /blog/123/. Et l' /?$approche est facile à ajouter à toute autre RewriteRule.

  • Segments flexibles pour les chemins virtuels
    .*/.*/.*/.*

    La plupart des règles que vous rencontrerez mappent un ensemble contraint de /…/segments de chemin de ressources à des paramètres GET individuels. Certains scripts gèrent cependant un nombre variable d'options . Le moteur de regexp Apache ne permet pas d'en optionnel un nombre arbitraire. Mais vous pouvez facilement le développer vous-même en un bloc de règles:

     Rewriterule ^(\w+)/?$                in.php?a=$1
     Rewriterule ^(\w+)/(\w+)/?$          in.php?a=$1&b=$2
     Rewriterule ^(\w+)/(\w+)/(\w+)/?$    in.php?a=$1&b=$2&c=$3
     #              └─────┴─────┴───────────────────┴────┴────┘
    

    Si vous avez besoin de cinq segments de chemin, copiez ce schéma en cinq règles. Vous pouvez bien sûr utiliser un [^/]+espace réservé plus spécifique à chacun. Ici, l'ordre n'est pas aussi important, car aucun des deux ne se chevauche. Donc, avoir en premier les chemins les plus fréquemment utilisés est acceptable.

    Vous pouvez également utiliser les paramètres de tableau PHP via ?p[]=$1&p[]=$2&p[]=3une chaîne de requête ici - si votre script les préfère simplement avant la division. (Bien qu'il soit plus courant d'utiliser simplement une règle fourre-tout et de laisser le script lui-même étendre les segments hors de REQUEST_URI.)

    Voir aussi: Comment transformer mes segments de chemin d'URL en paires clé-valeur de chaîne de requête?

  • Segments facultatifs
    prefix/opt?/.*

    Une variante courante consiste à avoir des préfixes facultatifs dans une règle. Cela a généralement du sens si vous avez des chaînes statiques ou des espaces réservés plus contraints autour:

      RewriteRule ^(\w+)(?:/([^/]+))?/(\w+)$  ?main=$1&opt=$2&suffix=$3
    

    Désormais, le modèle le plus complexe englobe (?:/([^/])+)?simplement un groupe non capturant (?:…) et le rend facultatif )?. L'espace réservé contenu ([^/]+)serait un modèle de substitution $2, mais il serait vide s'il n'y a pas de /…/chemin intermédiaire .

  • Capturez le reste
    /prefix/123-capture/…/*/…whatever…

    Comme dit précédemment, vous ne voulez pas souvent de modèles de réécriture trop génériques. Il est cependant judicieux de combiner des comparaisons statiques et spécifiques avec un .*parfois.

     RewriteRule ^(specific)/prefix/(\d+)(/.*)?$  speci.php?id=$2&otherparams=$2
    

    Cela a optionnel tous /…/…/…les segments de chemin de fin. Ce qui nécessite bien sûr que le script de gestion les sépare, et les paramètres extraits de manière variable lui- même (ce que font les frameworks Web- "MVC" ).

  • "Extensions" du fichier de fin
    /old/path.HTML

    Les URL n'ont pas vraiment d'extensions de fichier. C'est le sujet de toute cette référence (= les URL sont des localisateurs virtuels, pas nécessairement une image directe du système de fichiers). Cependant, si vous aviez un mappage de fichiers 1: 1 auparavant, vous pouvez créer des règles plus simples:

     RewriteRule  ^styles/([\w\.\-]+)\.css$  sass-cache.php?old_fn_base=$1
     RewriteRule  ^images/([\w\.\-]+)\.gif$  png-converter.php?load_from=$2
    

    D'autres utilisations courantes sont le remappage de .htmlchemins obsolètes vers des .phpgestionnaires plus récents , ou simplement l'aliasing des noms de répertoire uniquement pour des fichiers individuels (réels / réels).

  • Ping-Pong (redirige et réécrit à l'unisson)
    /ugly.html← →/pretty

    Donc, à un moment donné, vous réécrivez vos pages HTML pour ne contenir que de jolis liens, comme indiqué par deceze . En attendant, vous recevrez toujours des demandes pour les anciens chemins, parfois même des signets. Pour contourner ce problème , vous pouvez faire un ping-pong sur les navigateurs pour afficher / établir les nouvelles URL.

    Cette astuce courante consiste à envoyer une redirection 30x / Location chaque fois qu'une URL entrante suit le schéma de dénomination obsolète / laid. Les navigateurs demanderont ensuite à nouveau la nouvelle / jolie URL, qui sera ensuite réécrite (uniquement en interne) à l'emplacement d'origine ou au nouvel emplacement.

     # redirect browser for old/ugly incoming paths
     RewriteRule ^old/teams\.html$ /teams [R=301,QSA,END]
    
     # internally remap already-pretty incoming request
     RewriteRule ^teams$ teams.php        [QSA,END]
    

    Notez comment cet exemple utilise simplement [END]au lieu de [L]pour alterner en toute sécurité. Pour les anciennes versions d'Apache 2.2, vous pouvez utiliser d'autres solutions de contournement, en plus de remapper les paramètres de chaîne de requête, par exemple: rediriger moche vers une jolie URL, remapper vers le chemin moche, sans boucles infinies

  • Espaces dans les motifs
    /this+that+

    Ce n'est pas si joli dans les barres d'adresse du navigateur, mais vous pouvez utiliser des espaces dans les URL. Pour les modèles de réécriture, utilisez des \␣espaces échappés par une barre oblique inverse . Sinon, "citez tout le motif ou la substitution:

     RewriteRule  "^this [\w ]+/(.*)$"  "index.php?id=$1"  [L]
    

    Les clients sérialisent les URL avec +ou %20pour des espaces. Pourtant, dans RewriteRules, ils sont interprétés avec des caractères littéraux pour tous les segments de chemin relatifs.

Doublons fréquents:

prévalents .htaccesspièges

Maintenant, prenez ceci avec un grain de sel. Tous les conseils ne peuvent pas être généralisés à tous les contextes. Ceci est juste un simple résumé de quelques écueils bien connus et non évidents:

  • Activer mod_rewriteet.htaccess

    Pour utiliser réellement RewriteRules dans les fichiers de configuration par répertoire, vous devez:

    • Vérifiez que votre serveur est AllowOverride Allactivé . Sinon, vos .htaccessdirectives par répertoire seront ignorées et RewriteRules ne fonctionnera pas.

    • Évidemment, ont mod_rewriteactivé dans votre httpd.confsection modules.

    • Ajoutez encore à chaque liste de règles RewriteEngine On. Alors que mod_rewrite est implicitement actif dans les sections <VirtualHost>et <Directory>, les .htaccessfichiers par répertoire ont besoin d'être invoqués individuellement.

  • La barre oblique principale ^/ne correspondra pas

    Vous ne devriez pas démarrer vos .htaccessmodèles RewriteRule avec ^/normalement:

     RewriteRule ^/article/\d+$  …
                  ↑
    

    Ceci est souvent vu dans les anciens tutoriels. Et c'était correct pour les anciennes versions d'Apache 1.x. De nos jours, les chemins de requête sont entièrement relatifs aux répertoires dans .htaccessRewriteRules. Laissez juste le début /.

    · Notez que la barre oblique est toujours correcte dans les <VirtualHost>sections. C'est pourquoi vous le voyez souvent ^/?optionnel pour la parité des règles.
    · Ou lorsque vous utilisez un, RewriteCond %{REQUEST_URI}vous correspondez toujours à un leader /.
    · Voir aussi Webmaster.SE: Quand la barre oblique principale (/) est-elle nécessaire dans les modèles mod_rewrite?

  • <IfModule *> les emballages sont partis!

    Vous avez probablement vu cela dans de nombreux exemples:

    <IfModule mod_rewrite.c>
       Rewrite… 
    </IfModule>
    
    • Il ne sens que dans les <VirtualHost>sections - si elle a été associée à une autre option de repli, comme ScriptAliasMatch. (Mais personne ne fait jamais ça).
    • Et il est couramment distribué pour les .htaccessensembles de règles par défaut avec de nombreux projets open source. Là, il est simplement conçu comme une solution de secours et maintient les URL "laides" par défaut.

    Cependant, vous ne le souhaitez généralement pas dans vos propres .htaccessfichiers.

    • Premièrement, mod_rewrite ne se désengage pas au hasard. (Si c'était le cas, vous auriez de plus gros problèmes).
    • S'il était vraiment désactivé, vos RewriteRules ne fonctionneraient toujours pas de toute façon.
    • Il est destiné à éviter les 500erreurs HTTP . Ce qu'il accomplit généralement, c'est honorer vos utilisateurs d' 404erreurs HTTP à la place. (Pas tellement plus convivial si vous y réfléchissez.)
    • Pratiquement, il supprime simplement les entrées de journal les plus utiles ou les e-mails de notification du serveur. Vous ne sauriez pas pourquoi vos RewriteRules ne fonctionnent jamais.

    Ce qui semble séduisant en tant que sauvegarde généralisée se révèle souvent être un obstacle dans la pratique.

  • Ne pas utiliser RewriteBasesauf si nécessaire

    De nombreux exemples de copier-coller contiennent une RewriteBase /directive. Ce qui se trouve être la valeur implicite par défaut de toute façon. Vous n'avez donc pas vraiment besoin de ça. C'est une solution de contournement pour les schémas de réécriture de VirtualHost sophistiqués et les chemins DOCUMENT_ROOT erronés pour certains hébergeurs partagés.

    Il est logique de l'utiliser avec des applications Web individuelles dans des sous-répertoires plus profonds. Il peut raccourcir les modèles RewriteRule dans de tels cas. En général, il est préférable de préférer les spécificateurs de chemin relatif dans les ensembles de règles par répertoire.

    Voir aussi Comment fonctionne RewriteBase dans .htaccess

  • Désactiver MultiViewslorsque les chemins virtuels se chevauchent

    La réécriture d'URL est principalement utilisée pour prendre en charge les chemins entrants virtuels . Généralement vous un seul script répartiteur ( index.php) ou quelques gestionnaires individuels ( articles.php, blog.php, wiki.php, ...). Ce dernier peut entrer en conflit avec des chemins RewriteRule virtuels similaires.

    Une requête par /article/123exemple pourrait mapper article.phpavec un /123PATH_INFO implicitement. Vous devrez soit garder vos règles avec le commonplace RewriteCond !-f+ !-d, et / ou désactiver le support PATH_INFO, ou peut-être simplement désactiver Options -MultiViews.

    Ce qui ne veut pas dire que vous devez toujours le faire . La négociation de contenu n'est qu'un automatisme des ressources virtuelles.

  • La commande est importante

    Voir Tout ce que vous avez toujours voulu savoir sur mod_rewrite si vous ne l'avez pas déjà fait. La combinaison de plusieurs RewriteRules conduit souvent à une interaction. Ce n'est pas quelque chose à empêcher habituellement par [L]drapeau, mais un schéma que vous adopterez une fois familiarisé. Vous pouvez re-re-re écriture des chemins virtuels à partir d' une règle à l' autre, jusqu'à ce qu'il atteigne un gestionnaire cible réelle.

    Pourtant, vous voudrez souvent avoir les règles les plus spécifiques ( /forum/…modèles de chaîne fixes ou espaces réservés plus restrictifs [^/.]+) dans les premières règles. Les règles génériques slurp-all ( .*) sont mieux laissées aux dernières . (Une exception est une RewriteCond -f/-dgarde en tant que bloc principal.)

  • Les feuilles de style et les images ne fonctionnent plus

    Lorsque vous introduisez des structures de répertoires virtuels, /blog/article/123cela a un impact sur les références de ressources relatives en HTML (comme <img src=mouse.png>). Ce qui peut être résolu par:

    • En utilisant uniquement des références absolues de serveur href="https://stackoverflow.com/old.html"ousrc="/logo.png"
    • Souvent simplement en ajoutant <base href="https://stackoverflow.com/index">dans votre <head>section HTML . Cela relie implicitement les références relatives à ce qu'elles étaient auparavant.

    Vous pouvez également créer d'autres règles de réécriture pour les relier .cssou les .pngchemins vers leurs emplacements d'origine. Mais c'est à la fois inutile ou entraîne des redirections supplémentaires et entrave la mise en cache.

    Voir aussi: CSS, JS et les images ne s'affichent pas avec une jolie URL

  • RewriteConds masque juste une RewriteRule

    Une mauvaise interprétation courante est qu'un RewriteCond bloque plusieurs RewriteRules (car elles sont visuellement disposées ensemble):

     RewriteCond %{SERVER_NAME} localhost
     RewriteRule ^secret  admin/tools.php
     RewriteRule ^hidden  sqladmin.cgi
    

    Ce qui n'est pas le cas par défaut. Vous pouvez les enchaîner à l' aide du [S=2]drapeau. Sinon, vous devrez les répéter. Alors que parfois vous pouvez créer une règle primaire "inversée" pour [FIN] le traitement de réécriture tôt.

  • QUERY_STRING exempté des règles de réécriture

    Vous ne pouvez pas faire la correspondance RewriteRule index.php\?x=y, car mod_rewrite se compare uniquement aux chemins relatifs par défaut. Vous pouvez cependant les faire correspondre séparément via:

     RewriteCond %{QUERY_STRING} \b(?:param)=([^&]+)(?:&|$)
     RewriteRule ^add/(.+)$  add/%1/$1  # ←──﹪₁──┘
    

    Voir aussi Comment puis-je faire correspondre les variables de chaîne de requête avec mod_rewrite?

  • .htaccess contre. <VirtualHost>

    Si vous utilisez RewriteRules dans un fichier de configuration par répertoire, il est inutile de s'inquiéter des performances des regex. Apache conserve les modèles PCRE compilés plus longtemps qu'un processus PHP avec un cadre de routage commun. Pour les sites à fort trafic, vous devriez cependant envisager de déplacer les ensembles de règles dans la configuration du serveur vhost, une fois qu'ils ont été testés au combat.

    Dans ce cas, préférez le ^/?préfixe de séparateur de répertoire optionnel . Cela permet de déplacer librement RewriteRules entre PerDir et les fichiers de configuration du serveur.

  • Chaque fois que quelque chose ne fonctionne pas

    Ne vous inquiétez pas.

    • Comparez access.logeterror.log

      Souvent, vous pouvez comprendre comment une RewriteRule se comporte mal simplement en regardant votre error.loget access.log. Corrélez les temps d'accès pour voir quel chemin de requête est arrivé à l'origine et quel chemin / fichier Apache n'a pas pu résoudre (erreur 404/500).

      Cela ne vous dit pas quelle RewriteRule est le coupable. Mais les chemins finaux inaccessibles comme /docroot/21-.itle?index.phppeuvent indiquer où inspecter plus loin. Sinon, désactivez les règles jusqu'à ce que vous obteniez des chemins prévisibles.

    • Activer le RewriteLog

      Consultez la documentation Apache RewriteLog . Pour le débogage, vous pouvez l'activer dans les sections vhost:

      # Apache 2.2
      RewriteLogLevel 5
      RewriteLog /tmp/rewrite.log
      
      # Apache 2.4
      LogLevel alert rewrite:trace5
      #ErrorLog /tmp/rewrite.log
      

      Cela donne un résumé détaillé de la façon dont les chemins de demande entrants sont modifiés par chaque règle:

      [..] applying pattern '^test_.*$' to uri 'index.php'
      [..] strip per-dir prefix: /srv/www/vhosts/hc-profi/index.php -> index.php
      [..] applying pattern '^index\.php$' to uri 'index.php'
      

      Ce qui aide à réduire les règles trop génériques et les incidents regex.

      Voir aussi:
      · .htaccess ne fonctionne pas (mod_rewrite)
      · Conseils pour le débogage des règles de réécriture .htaccess

    • Avant de poser votre propre question

      Comme vous le savez peut-être, Stack Overflow est très approprié pour poser des questions sur mod_rewrite. Faites-les sur le sujet en incluant des recherches et des tentatives antérieures (évitez les réponses redondantes), démontrez compréhension, et:

      • Incluez des exemples complets d'URL d'entrée, de chemins cibles faussement réécrits, votre véritable structure de répertoires.
      • L'ensemble complet de RewriteRule, mais également celui qui est présumé défectueux.
      • Versions Apache et PHP, type de système d'exploitation, système de fichiers, DOCUMENT_ROOT et $_SERVERenvironnement PHPs s'il s'agit d'une incompatibilité de paramètres.
      • Un extrait de votre access.loget error.logpour vérifier ce que les règles existantes ont résolu. Mieux encore, un rewrite.logrésumé.

      Cela permet d'obtenir des réponses plus rapides et plus exactes, et les rend plus utiles aux autres.

  • Commentez votre .htaccess

    Si vous copiez des exemples de quelque part, veillez à inclure un fichier # comment and origin link. Bien que ce ne soit que de mauvaises manières d'omettre l'attribution, cela nuit souvent à la maintenance plus tard. Documentez tout code ou source de didacticiel. En particulier, à l'inverse, vous devriez être d'autant plus intéressé à ne pas les traiter comme des boîtes noires magiques.

  • Ce ne sont pas des URL "SEO"

    Avertissement: Juste une bête noire. Vous entendez souvent de jolis schémas de réécriture d'URL appelés liens «SEO» ou autre. Bien que cela soit utile pour rechercher des exemples sur Google, c'est un abus de langage obsolète.

    Aucun des moteurs de recherche modernes n'est vraiment perturbé par .htmlet .phpdans les segments de chemin, ou ?id=123les chaînes de requête d'ailleurs. Les moteurs de recherche de vieux, comme AltaVista, ont éviter les sites Web ramper avec des chemins d'accès potentiellement mal assurés. Les robots d'exploration modernes ont souvent même envie de ressources Web profondes.

    Les «jolies» URLs devraient être utilisées de manière conceptuelle pour rendre les sites Web conviviaux .

    1. Avoir des schémas de ressources lisibles et évidents.
    2. S'assurer que les URL sont durables ( permaliens AKA ).
    3. Fournir la découvrabilité à travers /common/tree/nesting.

    Cependant, ne sacrifiez pas les exigences uniques du conformisme.

Outils

Il existe divers outils en ligne pour générer des règles de réécriture pour la plupart des URL à paramètres GET:

La plupart du temps, il suffit de générer [^/]+des espaces réservés génériques, mais cela suffit probablement pour les sites triviaux.

mario
la source
Il faut encore un peu de réécriture, plus de liens, et les nombreux sous-titres sont quelque peu désagréables. Il y a un certain chevauchement avec les autres réponses ici, alors peut-être peut-être être réduit. Cela concerne principalement les exemples visuels et cette liste de pièges courants.
mario
3
Je n'ai pas vu une telle beauté d'une réponse depuis longtemps! Mes yeux brillent pendant que je le lis. S'il vous plaît ne pas arrêter de publier de telles réponses :)
Rizier123
1
Excellent poste. M'a fait comprendre très rapidement les concepts de base de mod_rewrite!
brise du
6

Alternatives à mod_rewrite

De nombreux schémas d'URL virtuelles de base peuvent être réalisés sans utiliser RewriteRules. Apache permet aux scripts PHP d'être appelés sans .phpextension et avec un PATH_INFOargument virtuel .

  1. Utilisez le PATH_INFO , Luke

    De nos jours, il AcceptPathInfo Onest souvent activé par défaut. Ce qui permet essentiellement à d' .phpautres URL de ressources de porter un argument virtuel:

    http://example.com/script.php/virtual/path
    

    Maintenant, cela /virtual/pathapparaît dans PHP comme$_SERVER["PATH_INFO"] où vous pouvez gérer les arguments supplémentaires comme vous le souhaitez.

    Ce n'est pas aussi pratique que Apache ayant des segments de chemin d'entrée séparée en $1, $2, $3et de les transmettre comme distinctes les $_GETvariables à PHP. Il s'agit simplement d'émuler de "jolies URL" avec moins d'effort de configuration.

  2. Activez MultiViews pour masquer.php extension

    L'option la plus simple pour éviter également les .php"extensions de fichier" dans les URL est d'activer:

    Options +MultiViews
    

    Cela permet à Apache de sélectionner article.phppour les requêtes HTTP en /articleraison du nom de base correspondant. Et cela fonctionne bien avec la fonction PATH_INFO susmentionnée. Vous pouvez donc simplement utiliser des URL comme http://example.com/article/virtual/title. Ce qui est logique si vous avez une application Web traditionnelle avec plusieurs points / scripts d'appel PHP.

    Notez que MultiViews a un objectif différent / plus large. Cela entraîne une très légère baisse des performances, car Apache recherche toujours d'autres fichiers avec des noms de base correspondants. Il est en fait destiné à Content-négociation , si les navigateurs reçoivent la meilleure solution parmi les ressources disponibles ( par exemple article.en.php, article.fr.php, article.jp.mp4).

  3. SetType ou SetHandler pour les .phpscripts sans extension

    Une approche plus dirigée pour éviter de transporter des .phpsuffixes dans les URL consiste à configurer le gestionnaire PHP pour d'autres schémas de fichiers. L'option la plus simple remplace le type de gestionnaire / MIME par défaut via .htaccess:

    DefaultType application/x-httpd-php
    

    De cette façon, vous pouvez simplement renommer votre article.phpscript en juste article(sans extension), tout en le traitant en tant que script PHP.

    Maintenant, cela peut avoir des implications sur la sécurité et les performances, car tous les fichiers sans extension seraient désormais acheminés via PHP. Par conséquent, vous pouvez également définir ce comportement pour des fichiers individuels uniquement:

    <Files article>
      SetHandler application/x-httpd-php
      # or SetType 
    </Files>
    

    Cela dépend quelque peu de la configuration de votre serveur et du PHP SAPI utilisé. Les alternatives courantes incluent ForceType application/x-httpd-phpou AddHandler php5-script.

    Encore une fois, notez que ces paramètres se propagent de l'un .htaccessaux sous-dossiers. Vous devez toujours désactiver l'exécution de script ( SetHandler Noneet Options -Execou php_flag engine offetc.) pour les ressources statiques, et upload / répertoires etc.

  4. Autres schémas de réécriture Apache

    Parmi ses nombreuses options, Apache fournit des mod_aliasfonctionnalités - qui fonctionnent parfois aussi bien que mod_rewriteles RewriteRules. Notez que la plupart de ceux-ci doivent être configurés dans une <VirtualHost>section cependant, pas dans des .htaccessfichiers de configuration par répertoire.

    • ScriptAliasMatchest principalement pour les scripts CGI, mais devrait également fonctionner pour PHP. Il autorise les expressions régulières comme n'importe quel autre RewriteRule. En fait, c'est peut-être l'option la plus robuste pour configurer un contrôleur frontal fourre-tout.

    • Et une plaine Aliasaide également avec quelques schémas de réécriture simples.

    • Même une ErrorDocumentdirective simple pourrait être utilisée pour laisser un script PHP gérer les chemins virtuels. Notez qu'il s'agit d'une solution de contournement kludgy, qui interdit tout sauf les requêtes GET et inonde le error.log par définition.

    Voir http://httpd.apache.org/docs/2.2/urlmapping.html pour plus de conseils.

mario
la source