web-dev-qa-db-fra.com

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

"De jolis liens" est un sujet souvent demandé, mais il est rarement expliqué en détail. mod_rewrite est un moyen de créer de "jolis liens", mais il est complexe et sa syntaxe est très laconique, difficile à assimiler 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 conviviales pour le référencement, Slugging, URLs MVC (probablement impropres)

137
deceze

Pour comprendre ce dont mod_rewrite a besoin, vous devez d’abord comprendre le fonctionnement d’un serveur Web. Un serveur Web répond à requêtes HTTP . Une requête HTTP à son niveau le plus élémentaire ressemble à ceci:

GET /foo/bar.html HTTP/1.1

C’est la simple demande d’un navigateur adressée à un serveur Web et demandant l’url [~ # ~] [~ # ~] /foo/bar.html à partir de cela. Il est important de souligner qu'il ne demande pas de fichier , il demande simplement une URL arbitraire. La demande peut aussi ressembler à ceci:

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

C’est tout aussi valable une demande d’URL et cela n’a évidemment rien à voir avec des fichiers.

Le serveur Web est une application écoutant un port, acceptant les requêtes HTTP entrant 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 y répondre. Cette réponse n'est pas un fichier, il s'agit d'une réponse HTTP qui peut ou non avoir un rapport avec des fichiers physiques sur n'importe quel disque. Un serveur Web ne doit pas nécessairement être Apache, il existe de nombreux autres serveurs Web qui ne sont que des programmes qui s'exécutent de manière persistante et qui sont rattachés à un port qui répondent aux demandes HTTP. Vous pouvez en écrire un vous-même. Ce paragraphe visait à vous dissocier de toute notion voulant que les URL soient directement égales aux fichiers, ce qui est vraiment important à comprendre. :)

La configuration par défaut de la plupart des serveurs Web consiste à rechercher un fichier correspondant à l'URL du disque dur. Si la racine du document du serveur est définie sur, par exemple, /var/www, il peut sembler que le fichier /var/www/foo/bar.html existe et le sert si c'est le cas. Si le fichier se termine par ".php", il invoquera l'interprète PHP et ), puis renverra le résultat. Tout cela l'association est complètement configurable, un fichier ne doit pas nécessairement se terminer par ".php" pour que le serveur Web l'exécute via l'interprète PHP, et l'URL ne doit correspondre à aucun déposer sur le disque pour que quelque chose se passe.

mod_rewrite est un moyen de réécrire le traitement de la demande interne. Lorsque le serveur Web reçoit une demande pour l'URL /foo/bar, vous pouvez réécrire cette URL en quelque chose d'autre avant que le serveur Web ne recherche un fichier sur le disque qui lui correspond. Exemple simple:

RewriteEngine On
RewriteRule   /foo/bar /foo/baz

Cette règle indique chaque fois qu'une demande correspond à "/ foo/bar", réécrivez-la en "/foo/baz".. La demande sera alors traitée comme si /foo/baz avait été demandé à la place. Ceci peut être utilisé pour divers effets, par exemple:

RewriteRule (.*) $1.html

Cette règle ne correspond à rien (.*) et capture le ((..)), puis le réécrit pour ajouter ".html". En d'autres termes, si /foo/bar était l'URL demandée, elle sera traitée comme si /foo/bar.html avait été demandé. Voir http://regular-expressions.info pour plus d'informations sur la correspondance, la capture et le remplacement d'expressions régulières.

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

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

Ceci, encore une fois, fait correspondre n'importe quoi et le réécrit dans le fichier index.php avec l'URL demandée à l'origine ajoutée dans le paramètre de requête url. 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'], il peut donc faire tout ce qu'il veut.

En premier lieu, vous insérez 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 nommé .htaccess dans la racine de votre document (c’est-à-dire à côté de vos fichiers .php).

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

Ce que mod_rewrite ne fait pas

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

<a href="/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. Changer le lien en un joli lien:

    <a href="/my/pretty/link">
    
  2. Utilisez mod_rewrite sur le serveur pour gérer la demande à l'URL /my/pretty/link en utilisant l’une des méthodes décrites ci-dessus.

(On pourrait utiliser mod_substitute conjointement 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.)

Mod_rewrite peut faire beaucoup de choses et des règles de concordance très complexes que vous pouvez créer, telles que le chaînage de plusieurs réécritures, le transfert de requêtes à un service ou une machine complètement différent, le renvoi de codes de statut HTTP spécifiques sous forme de réponses, la redirection de demandes, etc. très bon si vous comprenez le mécanisme fondamental de requête-réponse HTTP. Cela ne pas rend automatiquement vos liens jolis.

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

103
deceze

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

Tous les exemples ci-dessous supposent que vous avez déjà inclus RewriteEngine On Dans votre fichier .htaccess.

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 motif, mais je le désignerai simplement par le côté gauche de la règle - ce que vous voulez réécrire
  3. blog/index.php?id=$1&title=$2 - appelée la substitution, ou le côté droit d'une règle de réécriture - ce que vous voulez réécrire
  4. [NC,L,QSA] Sont des drapeaux 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 de charger /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 pas example.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 de caractères d'au moins 1 caractère et de valeurs numériques uniquement (c'est-à-dire 0 à 9). Ceci peut être référencé avec $1 Dans le côté droit de la règle
    • Le second ensemble de parenthèses correspond à une chaîne de 1 caractère minimum contenant uniquement des caractères alphanumériques (AZ, az ou 0-9) ou - Ou + (Note + Est échappé avec une barre oblique inversée, sans quoi, elle sera exécutée comme un caractère de répétition de l'expression rationnelle ). Ceci peut être référencé avec $2 Dans le côté droit de la règle
  • ? Signifie que le caractère précédent est facultatif. Ainsi, dans ce cas, /blog/1/foo/ Et /blog/1/foo Seront réécrits au même endroit.
  • $ Indique qu'il s'agit de la fin de la chaîne à laquelle 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 y a beaucoup de drapeaux différents que vous pouvez lire dans la documentation , mais je vais passer en revue certains des drapeaux les plus courants:

NC

L'indicateur no cas signifie que la règle de réécriture est insensible à la casse. Par conséquent, pour l'exemple de règle ci-dessus, cela signifierait que /blog/1/foo/ Et /BLOG/1/foo/ (Ou toute variante de celle-ci) seraient appariés.

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, comme d'habitude. Si vous ne définissez pas l'indicateur L, toutes les règles suivantes seront ensuite appliquées à l'URL réécrite .

END

Depuis Apache 2.4, vous pouvez également utiliser l'indicateur [END]. Une règle de correspondance avec elle arrêtera complètement le traitement ultérieur du pseudonyme/réécriture. (Alors que le drapeau [L] Peut souvent déclencher un deuxième tour, par exemple lors de la réécriture dans ou hors des sous-répertoires.)

QSA

L'indicateur de chaîne de requête append nous permet de transmettre des variables supplémentaires à l'URL spécifiée, qui seront ajoutées aux paramètres de récupération d'origine. Pour notre exemple, cela signifie que quelque chose comme /blog/1/foo/?comments=15 Chargerait /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 c’est un drapeau qui mérite 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 voulez faire une redirection 301 sur/myblog/to/blog /, vous écririez simplement une règle du genre:

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

Conditions de réécriture

Conditions de réécriture rendre les réécritures encore plus puissantes, vous permettant de spécifier des réécritures pour des situations plus spécifiques. Vous pouvez lire beaucoup de conditions 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]

C'est une pratique très courante, qui précédera votre domaine avec www. (S'il n'y est pas déjà) et exécutera une redirection 301. Par exemple, en chargeant http://example.com/blog/, Cela vous redirigerait vers http://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 du fichier est un répertoire ou un fichier existant sur le serveur.

  • %{REQUEST_URI} \.(jpg|jpeg|gif|png)$ [NC] n'exécutera la réécriture que pour les fichiers dont l'extension est jpg, jpeg, gif ou png (insensibles à la casse).
  • %{REQUEST_FILENAME} !-f Vérifiera si le fichier existe sur le serveur actuel et n'exécutera la réécriture que s'il n'en a pas
  • %{REQUEST_FILENAME} !-d Vérifiera si le fichier existe sur le serveur actuel et n'exécutera la réécriture que s'il n'en a pas
  • La réécriture tentera de charger le même fichier sur un autre domaine.
76
Nick

Les références

Stack Overflow a many autres ressources utiles pour commencer:

Et des aperçus regex conviviaux pour les 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 repli.
  • [^/]+ 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 en gros un raccourci pour [A-Za-z0-9_].
  • [\w\-]+ Pour les segments de chemin de style "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 […].
  • \. Indique 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 entouré de parenthèses (…) En tant que groupe de capture. Et le motif entier souvent dans ^………$ Marqueurs de début + fin. La mention de "modèles" est facultative.

RewriteRules

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

  • Cartographie statique
    /contact/about

    Raccourcir quelques noms de page aux modèles de fichiers internes est très simple:

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

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

     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 espaces réservés /article/title-string:

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

    Notez que votre script doit pouvoir (ou être adapté) mapper ces titres sur des identifiants de base de données. RewriteRules ne peut à lui seul 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 chemins mixtes /article/529-title-slug Utilisés dans la pratique:

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

    Maintenant, vous pouvez simplement ignorer le passage de title=$2, Car votre script dépendra de toute façon de la base de données. Le -title-slug Est devenu une décoration d'URL arbitraire.

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

    Si vous avez des règles similaires pour plusieurs chemins de page virtuels, vous pouvez les faire correspondre et les compacter avec des listes alternatives |. Et encore une fois, il suffit de les réaffecter aux paramètres internes de GET:

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

    Vous pouvez les diviser en RewriteRules individuels si cela devenait trop complexe.

  • Envoi d'URL associées à différents serveurs
    /date/SWITCH/backend

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

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

    Il s'agit simplement de remapper les publications de 2009-2011 sur un script et implicitement toutes les autres années vers un autre gestionnaire. Notez la règle plus spécifique à venir en premier. Chaque script peut utiliser différents paramètres GET.

  • Autres délimiteurs que les barres de chemin /
    /user-123-name

    RewriteRules est généralement utilisé pour simuler une structure de répertoires virtuels. Mais vous n'êtes pas obligé de ne pas être créatif. Vous pouvez également utiliser - Des tirets 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 commun aussi /wiki:section:Page_Name schème:

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

    De temps en temps, il convient d'alterner entre les délimiteurs / - et : Ou . Dans la même règle. Ou encore avoir deux RewriteRules pour mapper des variantes sur différents scripts.

  • Barre de fin / Optionnelle
    /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/123 Et /blog/123/. Et le /?$ Il est facile d’appliquer cette approche sur n’importe quelle autre RewriteRule.

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

    La plupart des règles que vous rencontrerez mapperont un ensemble contraint de segments de chemin de ressource /…/ À des paramètres GET individuels. Certains scripts gérer un nombre variable d'options cependant. Le moteur Apache regexp ne permet pas d’optimiser un nombre arbitraire d’entre eux. Mais vous pouvez facilement l'étendre 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 d’un maximum de cinq segments de chemin, copiez ce schéma dans 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 ne se chevauche. Donc, avoir les chemins les plus fréquemment utilisés en premier va bien.

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

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

  • Segments optionnels
    prefix/opt?/.*

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

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

    Maintenant, le modè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 vide si il n'y a pas de chemin du milieu /…/.

  • Capturer 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. Cependant, il est parfois logique de combiner des comparaisons statiques et spécifiques avec un .*.

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

    Ceci a opté pour n'importe quel segment de chemin de fin /…/…/…. Ce qui oblige bien sûr le script de manipulation à les scinder, et les variabl-ify extraits eux-mêmes (ce que Web- "MVC" les frameworks font) .

  • Fichier de fin "extensions"
    /old/path.HTML

    Les URL n'ont pas vraiment d'extensions de fichier. C’est le but de toute cette référence (= Les URL sont des localisateurs virtuels, pas nécessairement une image de système de fichiers directe). Cependant, si vous aviez un mappage de fichier 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 des chemins .html Obsolètes en nouveaux gestionnaires .php, Ou simplement l'alias 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 porter que de jolis liens, comme décrit par deceze . En attendant, vous recevrez toujours des demandes pour les anciens chemins, parfois même à partir de signets. En tant que solution de contournement, vous pouvez ping-pong sur les navigateurs pour afficher/établir les nouvelles URL.

    Cette astuce courante consiste à envoyer un 30x/Location redirect à chaque fois qu'une URL entrante suit le schéma de dénomination obsolète/moche. Les navigateurs vont alors rerequest la nouvelle/jolie URL, qui est ensuite réécrite (juste 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 que 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, en plus de remapper les paramètres de chaîne de requête, par exemple: redirection de moche vers une jolie URL, reconfiguration du chemin moche, sans boucle infinie

  • Les espaces  en motifs
    /this+that+

    Ce n'est pas c'est 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 \␣ Avec une barre oblique inversée. Sinon juste " - citez le motif complet ou la substitution:

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

    Les clients sérialisent les URL avec + Ou %20 Pour les 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évalent .htaccess Piè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 résumé simple de quelques points d'achoppement connus et peu évidents:

  • Activer mod_rewrite Et .htaccess

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

    • Vérifiez que votre serveur a AllowOverride All Activé . Sinon, vos directives .htaccess Par répertoire seront ignorées et RewriteRules ne fonctionnera pas.

    • Evidemment activez mod_rewrite dans votre section de modules httpd.conf.

    • Ajoutez chaque liste de règles avec toujours RewriteEngine On. Bien que mod_rewrite soit implicitement actif dans les sections <VirtualHost> Et <Directory>, Les fichiers par répertoire .htaccess En ont besoin individuellement.

  • La barre oblique ^/ Ne correspond pas

    Vous ne devriez pas commencer vos motifs .htaccess RewriteRule avec ^/ Normalement:

     RewriteRule ^/article/\d+$  …
                  ↑
    

    Cela se voit souvent dans les vieux tutoriels. Et cela était correct pour les anciennes versions Apache 1.x. De nos jours, les chemins des requêtes sont commodes entièrement relatifs au répertoire dans .htaccess RewriteRules. Laissez simplement le premier / Dehors.

    · Notez que la barre oblique est toujours correcte dans les sections <VirtualHost>. C'est pourquoi vous le voyez souvent ^/? Optionnel pour la parité des règles.
    · Ou si vous utilisez un RewriteCond %{REQUEST_URI}, Vous feriez tout de même pour un / Initial.
    · Voir aussi Webmaster.SE: Quand la barre oblique (/) est-elle nécessaire dans les modèles mod_rewrite?

  • <IfModule *> Les wrappers sont prêts!

    Vous avez probablement déjà vu cela dans de nombreux exemples:

    <IfModule mod_rewrite.c>
       Rewrite… 
    </IfModule>
    
    • Cela fait est logique dans les sections <VirtualHost> - si elle était combinée avec une autre option de secours, telle que ScriptAliasMatch. (Mais personne ne fait jamais ça).
    • Et il est généralement distribué pour les ensembles de règles .htaccess Par défaut avec de nombreux projets open source. Là, il s’agit simplement d’une solution de secours et conserve les URL "laides" par défaut.

    Cependant, vous ne voulez pas cela dans vos propres fichiers .htaccess.

    • Tout d'abord, mod_rewrite ne se désengage pas de manière aléatoire. (Si c'était le cas, vous auriez de plus gros problèmes).
    • S'il était vraiment désactivé, vos règles Rewrite ne fonctionneraient toujours pas de toute façon.
    • Cela vise à empêcher les erreurs HTTP 500. Ce qu’il accomplit habituellement, c'est de donner à vos utilisateurs des erreurs HTTP 404 À la place. (Pas tellement plus convivial si vous y réfléchissez.)
    • En pratique, cela supprime simplement les entrées de journal les plus utiles, ou les courriers de notification du serveur. Vous ne seriez pas le plus sage de savoir pourquoi vos règles Rewrite ne fonctionnent jamais.

    Ce qui semble attrayant comme sauvegarde généralisée s'avère souvent être un obstacle dans la pratique.

  • N'utilisez pas RewriteBase sauf si nécessaire

    De nombreux exemples copier-coller contiennent une directive RewriteBase /. Ce qui se trouve être le défaut implicite de toute façon. Donc, vous n'avez pas réellement besoin de cela. C'est une solution de contournement pour les schémas de réécriture fantastiques de VirtualHost et les chemins DOCUMENT_ROOT erronés pour certains hébergeurs partagés.

    Il est logique d’utiliser des applications Web individuelles dans des sous-répertoires plus profonds. Dans de tels cas, cela peut raccourcir les modèles RewriteRule. En règle générale, 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 MultiViews lorsque des chemins virtuels se chevauchent

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

    Une demande de /article/123, Par exemple, pourrait correspondre à article.php Avec un /123 PATH_INFO implicitement. Vous devrez alors garder vos règles avec le banal 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 avez toujours devoir. La négociation de contenu n'est qu'un automatisme pour les ressources virtuelles.

  • La commande est importante

    Voir Tout ce que vous avez toujours voulu savoir sur mod_rewrite si ce n'est déjà fait. La combinaison de plusieurs règles Rewrite entraîne souvent une interaction. Ce n’est pas quelque chose à éviter habituellement par drapeau [L], Mais un stratagème que vous adopterez une fois versé. Vous pouvez re-re-re-re-re écrire des chemins virtuels d'une règle à une autre, jusqu'à ce qu'elle atteigne un véritable gestionnaire cible .

    Vous voudriez quand même souvent vouloir définir les règles les plus spécifiques (modèles à chaîne fixe /forum/…, Ou des espaces réservés plus restrictifs [^/.]+) Dans le début règles. Les règles génériques de Slurp-all (.*) Sont préférables à celles de plus tard. (Une exception est un garde RewriteCond -f/-d En tant que bloc primaire.)

  • Les feuilles de style et les images cessent de fonctionner

    Lorsque vous introduisez des structures de répertoires virtuels /blog/article/123, Cela affecte les références de ressources relatives en HTML (telles que <img src=mouse.png>). Qui peut être résolu par:

    • En utilisant uniquement les références absolues sur le serveur href="/old.html" Ou src="/logo.png"
    • Souvent, simplement en ajoutant <base href="/index"> Dans votre section HTML <head>. Cela renoue implicitement les références relatives à ce qu’ils étaient auparavant.

    Vous pouvez également créer d'autres règles RewriteRules pour relier les chemins .css Ou .png À leurs emplacements d'origine. Mais cela n’est pas nécessaire 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 un RewriteRule

    Une erreur d'interprétation courante est qu'un RewriteCond bloque plusieurs RewriteRules (parce qu'elles sont visuellement organisées ensemble):

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

    Ce qu'il ne fait pas par défaut. Vous pouvez les chaîner en utilisant le drapeau [S=2]. Sinon, vous devrez les répéter. Bien que vous puissiez parfois créer une règle primaire "inversée" pour [END] le traitement de réécriture à un stade précoce.

  • QUERY_STRING exempt de RewriteRules

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

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

    Voir aussi Comment puis-je associer des variables de chaîne de requête à mod_rewrite?

  • .htaccess Vs. <VirtualHost>

    Si vous utilisez RewriteRules dans un fichier de configuration par répertoire, il est inutile de s’inquiéter des performances des expressions rationnelles. Apache conserve les modèles PCRE compilés plus longs qu'un processus PHP avec un cadre de routage commun. Pour les sites à fort trafic, vous devez toutefois déplacer les ensembles de règles dans la configuration du serveur vhost, une fois qu'ils ont été mis à l'essai. .

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

  • Chaque fois que quelque chose ne fonctionne pas

    Ne vous inquiétez pas.

    • Comparez access.log Et error.log

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

      Cela ne vous dit pas quel RewriteRule est le coupable. Mais des chemins d'accès inaccessibles tels que /docroot/21-.itle?index.php Peuvent indiquer où inspecter davantage. Sinon, désactivez les règles jusqu'à ce que vous obteniez des chemins prévisibles.

    • Activer le RewriteLog

      Voir Apache RewriteLog docs. 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 requête 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 également:
      · . 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 les recherches et tentatives précédentes (évitez les réponses redondantes), démontrez la compréhension de base regex , et:

      • Inclure complet exemples d’URL d’entrée, de chemins cibles faussement réécrits, de votre structure de répertoire réelle.
      • L'ensemble complet de RewriteRule, mais aussi distingue celui présumé défectueux.
      • Apache et les versions PHP, type de système d'exploitation, système de fichiers, DOCUMENT_ROOT et PHP $_SERVER S'il s'agit d'une incompatibilité de paramètres.
      • Un extrait de vos access.log Et error.log Pour vérifier la résolution des problèmes existants. Mieux encore, un résumé rewrite.log.

      Cela permet d’obtenir des réponses plus rapides et plus précises et de les rendre plus utiles aux autres.

  • Commentez votre .htaccess

    Si vous copiez des exemples quelque part, veillez à inclure un # comment and Origin link. Si omettre l'attribution est simplement une mauvaise façon de faire, cela fait souvent très mal à la maintenance par la suite. Documentez n'importe quel code ou source de tutoriel. En particulier lorsque vous n'êtes pas à l'aise, vous devriez être d'autant plus intéressé à ne pas les traiter comme des boîtes noires magiques.

  • Ce n'est pas "URL" -URLs

    Disclaimer: Juste une bête noire. Vous entendez souvent de jolis schémas de réécriture d'URL appelés liens "SEO" ou quelque chose du genre. Bien que cela soit utile pour googler des exemples, c'est un terme impropre.

    Aucun des moteurs de recherche modernes n'est vraiment perturbé par .html Et .php Dans les segments de chemin, ou par les chaînes de requête ?id=123. Les anciens moteurs de recherche, tels que AltaVista, did évitent d'explorer les sites Web dotés de chemins d'accès potentiellement ambigus. Les robots modernes ont souvent même besoin de ressources Web profondes.

    Les "jolies" URL doivent normalement être utilisées pour créer des sites Web convivial.

    1. Avoir des schémas de ressources lisibles et évidents.
    2. Garantir la longévité des URL (AKA permaliens ).
    3. Fournir la possibilité de découverte via /common/tree/nesting.

    Cependant, ne sacrifiez pas les exigences uniques en matière de conformisme.

Outils

Il existe différents outils en ligne pour générer RewriteRules pour la plupart des URL de paramètres GET:

Généralement, sortez simplement des espaces réservés génériques [^/]+, Mais cela suffit probablement pour les sites triviaux.

36
mario

Alternatives à mod_rewrite

De nombreux modèles d'URL virtuels de base peuvent être réalisés sans utiliser RewriteRules. Apache permet à PHP) d'appeler des scripts sans l'extension .php et avec un argument virtuel PATH_INFO.

  1. Utilisez le PATH_INFO , Luke

    De nos jours AcceptPathInfo On est souvent activé par défaut. Ce qui permet fondamentalement à .php et à d'autres URL de ressource de porter un argument virtuel:

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

    Maintenant, ce /virtual/path apparaît dans PHP en tant que $_SERVER["PATH_INFO"]) _ où vous pouvez gérer tous les arguments supplémentaires comme vous le souhaitez.

    Cela n’est pas aussi pratique que de séparer les segments de chemin d’entrée Apache en $1, $2, $3 et de les transmettre sous forme de variables $_GET distinctes en PHP. Il s'agit simplement d'émuler de "jolies URL" avec moins d'effort de configuration.

  2. Activer MultiViews pour masquer l'extension .php

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

    Options +MultiViews
    

    Apache doit donc sélectionner article.php pour les demandes HTTP sur /article en raison du nom de base correspondant. Et cela fonctionne bien avec la fonctionnalité PATH_INFO susmentionnée. Donc, vous pouvez simplement utiliser des URL comme http://example.com/article/virtual/title. Ce qui est logique si vous avez une application Web traditionnelle avec plusieurs PHP points d’invocation/scripts.

    Notez que MultiViews a un objectif différent/plus large. Cela entraîne une pénalité de performances très mineure , car Apache recherche toujours les autres fichiers avec les noms de base correspondants. Il s’agit en réalité de Content-Negotiation , afin que les navigateurs reçoivent la meilleure alternative parmi les ressources disponibles (telles que article.en.php, article.fr.php, article.jp.mp4 ).

  3. SetType ou SetHandler pour les scripts sans extension .php

    Une approche plus ciblée pour éviter de contourner .php suffixes dans les URL est configuration du gestionnaire PHP) pour les autres modèles de fichiers. L'option la plus simple est de remplacer le type MIME/gestionnaire par défaut via .htaccess:

    DefaultType application/x-httpd-php
    

    De cette façon, vous pouvez simplement renommer votre script article.php en article (sans extension), tout en le faisant traiter comme PHP).

    Cela peut maintenant avoir des conséquences sur la sécurité et les performances, car tous les fichiers sans extension seraient acheminés via PHP maintenant. 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 un peu de la configuration de votre serveur et de la PHP SAPI utilisée. Les alternatives courantes incluent ForceType application/x-httpd-php ou AddHandler php5-script.

    Notez à nouveau que ces paramètres se propagent d’un .htaccess à des sous-dossiers. Vous devez toujours désactiver l'exécution du script (SetHandler None et Options -Exec ou php_flag engine off, etc.) pour les ressources statiques, et télécharger/répertoires, etc.

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

    Parmi ses nombreuses options, Apache fournit mod_alias fonctionnalités - qui fonctionnent parfois aussi bien que mod_rewrite s RewriteRules. Notez que la plupart d'entre eux doivent être configurés dans une section <VirtualHost>, mais pas dans des fichiers de configuration par répertoire .htaccess.

    • ScriptAliasMatch est principalement destiné aux scripts CGI, mais devrait également fonctionner pour PHP. Il permet les expressions rationnelles comme n'importe quel RewriteRule. En fait, c'est peut-être l'option la plus robuste pour configurer un contrôleur frontal fourre-tout.

    • Et un simple Alias aide également à quelques schémas simples de réécriture.

    • Même une directive simple ErrorDocument peut être utilisée pour permettre à un script PHP de gérer des chemins virtuels. Notez qu'il s'agit d'une solution de contournement kludgy, cependant, elle interdit tout GET demande et inonde le error.log par définition.

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

5
mario