Sur le serveur, j'ai un fichier (sur le système de fichiers) appelé page.html
, auquel je souhaite accéder en tant que site.com/page
Ainsi, si quelqu'un accède à site.com/page.html
, il doit 301
redirection vers site.com/page
J'ai vu des règles de réécriture qui gèrent la réécriture /page
-> /page.html
en interne, mais le forcer à 301
rediriger /page.html
-> /page
provoque également un boucle de redirection pour moi.
Le drapeau END
semble pouvoir être utilisé pour faire ce que je veux, mais il n'est pas encore pris en charge.
J'ai aussi essayé d'utiliser ENV
comme suit:
RewriteRule ^page$ /page.html [L,E=END:1]
RewriteCond %{ENV:END} !1
RewriteRule ^page.html$ /page [R=301,L]
Mais cela entraîne également une boucle de redirection.
J'ai demandé ceci même question sur StackOverflow . Pour que cela fonctionne correctement, vous devez utiliser des variables d'environnement:
RewriteRule ^page$ /page.html [L,E=LOOP:1]
RewriteCond %{ENV:REDIRECT_LOOP} !1
RewriteRule ^page.html$ /page [R=301,L]
En effet, mod_rewrite effectue plusieurs passages dans vos règles. Lors du premier passage, il définit la variable d'environnement. Au cours de la deuxième passe, la variable est précédée du préfixe REDIRECT_
. Vous devez donc la lire sous la forme REDIRECT_LOOP
.
Le problème est que, lorsque vous utilisez mod_rewrite dans un fichier .htaccess ou une section <Directory>, chaque RewriteRule réussi - même interne - provoque le redémarrage de la demande en interne , et donc la totalité de la réécriture. jeu de règles à retraiter.
Ainsi, ce qui se passe, c'est que, lorsque l'utilisateur visite /page
, votre RewriteRule interne correspond et réécrit l'URL sous /page.html
. Mais cela fait que Apache redémarre le traitement de la requête et réexécute votre jeu de règles, ce qui provoque la correspondance de la règle external et réécrit et déclenche une redirection 301 vers /page
.
Une solution rapide et délicate (mais efficace!) Consiste à obliger votre règle de réécriture interne à ajouter un paramètre factice tel que redirect=no
à l'URL, puis à rechercher ce paramètre dans la règle de réécriture externe. Voici un exemple basé sur cette réponse que j'ai écrite pour une question similaire sur Stack Overflow :
RewriteEngine On
RewriteBase /
# Externally rewrite page.html -> page, unless query includes redirect=no:
RewriteCond %{QUERY_STRING} !(^|&)redirect=no(&|$)
RewriteRule ^(page)\.html$ /$1 [NS,R=301,L]
# Internally rewrite page -> page.html, add redirect=no to query:
RewriteRule ^(page)$ $1.html?redirect=no [NS,QSA]
(Bien sûr, n'hésitez pas à remplacer redirect=no
par quelque chose d'autre si cela entre en conflit avec un paramètre d'URL que vous pourriez utiliser.)
Un moyen courant d’empêcher la boucle de redirection consiste à vérifier la variable de serveur THE_REQUEST
, qui contient l’en-tête de la demande initiale, plutôt que le chemin d’URL de l’URL réécrite (dont le RewriteRule
modèle correspond à), qui est naturellement mis à jour lorsque l'URL est réécrite.
THE_REQUEST
la valeur ne change pas car l'URL est réécrite en interne, et contient une chaîne de la forme suivante:
GET /page.html HTTP/1.1
Donc, cela pourrait être réécrit comme:
# Redirect direct requests for /page.html to the canonical URL
RewriteCond %{THE_REQUEST} \.html
RewriteRule ^(page)\.html$ /$1 [R=301,L]
# Rewrite the "pretty" URL to the actual filesystem path
RewriteRule ^(page)$ $1.html [L]
Dans ce cas, la condition THE_REQUEST
s'assure simplement que .html
est présent dans l'URL initialement demandée. La vérification de page.html
est laissée à la RewriteRule
motif - qui est plus efficace (car elle est vérifiée en premier).
Il est toujours préférable d'avoir des redirections externes avant les réécritures internes.