web-dev-qa-db-fra.com

Rediriger les $ .html vers les conflits de règles $ / mod_rewrite

J'ai déjà remappé les URL de par exemple/sub/test/to test.html, mais je souhaite également rediriger /sub/test.html vers/sub/test /, mais il semble qu'il y ait un conflit entre les règles.

Les fichiers .htaccess se trouvent dans le sous-répertoire/et ne devraient s'appliquer qu'à/sous-répertoires et tous les sous-répertoires qu'il contient.

Voici le fichier .htaccess:

<IfModule mod_rewrite.c>
Options -MultiViews -Indexes
RewriteEngine On
RewriteBase /sub

# redirect urls without trailing slash
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !index.html
RewriteCond %{REQUEST_URI} !(.*)/$
RewriteRule (.*)$ $1/ [L,R=301]

# redirect .html to url
#RewriteCond %{REQUEST_FILENAME} !-d
#RewriteCond %{REQUEST_FILENAME} -f
#RewriteRule (.*)\.html$ $1/ [L,R=301]

# remap url to a .html file
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule (.*)/$ $1.html [L]

</IfModule>

La logique est que le chemin /sub/test.html est redirigé vers/sub/test/et qu'il est remappé en interne vers /sub/test.html.

Les fichiers .htaccess se trouvent dans le sous-répertoire/et ne devraient s'appliquer qu'à/sous-répertoires et tous les sous-répertoires qu'il contient.

Les lignes commentées provoquent une boucle de redirection vers/sub/test/qui correspond à l'URL souhaitée. S'il ne reste que les lignes commentées, il n'y a pas de boucle de redirection; il semble donc qu'il y ait un conflit entre les règles. Quelle est la cause de la boucle de redirection?

Boucle de redirection de fichier non existante:

Comment traiter les boucles de redirection de fichiers non existantes? Par exemple, /web.html redirige vers/web/alors que /web2.html non existant finit par ressembler à /web.html.html.html ....

3
DominicM

Quelle est la cause de la boucle de redirection?

Lorsque la "dernière directive" est exécutée, le processus de réécriture ne s’arrête pas subitement, seul le courant passe en cours s’arrête. L'ensemble du processus de réécriture recommence à partir du haut! Le processus ne s'arrête complètement que lorsque l'URL est inchangé (ou lorsqu'il atteint l'indicateur END dans Apache 2.4, comme le mentionne Ivo van der Veeken dans sa réponse).

La "dernière directive" est soit la dernière directive du fichier, soit une directive qui est traitée avec l'indicateur L (last).

RewriteRule (.*)/$ $1.html [L]

Ainsi, lorsque la directive ci-dessus est exécutée, le processus de réécriture reprend, mais cette fois-ci est intercepté par les directives (commentées) ci-dessus, qui suppriment le .html (nouvellement ajouté) et les redirigent. À la demande suivante, le .html est ajouté à nouveau (pour la réécriture interne), mais le processus de réécriture recommence, supprime le .html et redirige vers le bas, etc., etc.

Pour rompre la boucle, vous pouvez utiliser le drapeau END (Apache 2.4+) comme indiqué ci-dessus, au lieu de L. Vous pouvez également utiliser une condition supplémentaire (directive RewriteCond) sur votre redirection .html qui vérifie THE_REQUEST. Cette variable de serveur contient la valeur de la requête initiale (l'en-tête de requête envoyé par le client), mais pas la requête réécrite . lorsque la demande est réécrite, cassant ainsi la "boucle" Cela fonctionne sur toutes les versions d'Apache. Alors, essayez quelque chose comme:

# redirect .html to url
RewriteCond %{THE_REQUEST} ^[A-Z]+\ /.+\.html\ HTTP/
#RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule (.+)\.html$ $1/ [L,R=301]

La condition ci-dessus s'assure que le .html n'existe que sur la demande initiale, pas la demande réécrite. Je ne pense pas que vous ayez vraiment besoin de vérifier que ce n'est pas un répertoire, à moins que vous n'ayez des répertoires nommés <something>.html ?!

THE_REQUEST ressemble à quelque chose comme:

GET /sub/test.html HTTP/1.1

Boucle de redirection de fichier inexistante:

# remap url to a .html file
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule (.*)/$ $1.html [L]

Vos directives de réécriture ajoutent .html à la demande chaque fois que le fichier demandé n’existe pas. Ainsi, does-not-exist/ est réécrit en does-not-exist.html qui est redirigé en externe (par votre première règle) vers does-not-exist.html/ qui est réécrit en does-not-exist.html.html, etc.

Vous pouvez inclure une vérification supplémentaire pour vous assurer que le fichier réécrit existerait avant de le réécrire. par exemple. RewriteCond %{DOCUMENT_ROOT}/sub/$1.html -f - la complexité supplémentaire provient du fait que vos URL se terminent par une barre oblique. Dans le contexte:

# remap url to a .html file
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{DOCUMENT_ROOT}/sub/$1.html -f
RewriteRule (.*)/$ $1.html [L]

EDIT: Pour que cela soit plus générique (pour éviter de devoir inclure le sous-répertoire /sub), vous pouvez essayer changer la 3ème condition en:

RewriteCond %{REQUEST_FILENAME}.html -f

Cela repose sur le fait qu'Apache supprime en interne les barres obliques de fin de la requête.

rendez-le plus portable en rendant le RewriteBase dynamique

Si ce fichier .htaccess se trouve dans le même répertoire que vous spécifiez pour la directive RewriteBase (qu’il s’agit de cette instance), vous pourriez dynamiquement assigne le sous-répertoire à une variable d'environnement et l'utilise dans vos substitutions RewriteRule.

# Removed...
#RewriteBase /sub

# Assign the accessed subdirectory to an environment variable
RewriteCond %{REQUEST_URI} ^(/[^/]+)/
RewriteRule ^ - [E=SUBDIR:%1]

# Use this environment variable as a prefix to your substitutions
:
RewriteRule (.*)$ %{ENV:SUBDIR}/$1/ [L,R=301]

Incidemment, cela n’est requis que pour vos redirections externes . Vous n'avez pas besoin de cela pour des écritures internes dans des fichiers .htaccess par répertoire, car le préfixe de répertoire est automatiquement ajouté aux substitutions relatives.

Cela voudrait dire que/sous-répertoire pourrait être un répertoire avec n'importe quel nom ou même plusieurs sous-répertoires.

Pour plusieurs sous-répertoires, vous pouvez changer le CondPattern avec quelque chose comme:

RewriteCond %{REQUEST_URI} ^(/.+)/

Par défaut, l'expression régulière est gourmande , ainsi tout ce qui se trouve entre la première et la dernière barre oblique est capturé. Cependant, cela risque de casser certaines adresses URL/serveurs et de vous ouvrir la voie à une attaque XSS. Je coderais donc pour vos besoins spécifiques, au lieu d’être trop générique.

1
MrWhite

Tout d’abord, RewriteCond %{REQUEST_FILENAME} !index.html ne fonctionnera pas, vous aurez besoin de !"index\.html", avec les guillemets. Sinon, ce n'est pas une expression rationnelle valide.

Selon docs , vous devez utiliser le drapeau [END] au lieu de [L] pour empêcher tout traitement ultérieur de la demande (en interne, votre réécriture est réévaluée par les règles de redirection Apache). Apparemment, il y a n bogue avec [END] qui n'a été corrigé que dans la version 2.4.9, vous devrez donc peut-être trouver un autre moyen.

Je changerais également l'ordre des déclarations. La plupart de vos liens doivent pointer vers l'URL propre, ce qui devrait être un peu plus rapide.

# remap url to a .html file
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule (.*)/$ $1.html [END]

# redirect .html to url
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule (.*)\.html$ $1/ [L,R=301]
2
Ivo van der Veeken

Voici le .htaccess final qui fonctionne presque parfaitement.

<IfModule mod_rewrite.c>
Options -MultiViews -Indexes
RewriteEngine On
#RewriteBase /sub

# Assign the accessed subdirectory to an environment variable
RewriteCond %{REQUEST_URI} ^(/[^/]+)/
RewriteRule ^ - [E=SUBDIR:%1]

ErrorDocument 404 /%{ENV:SUBDIR}/404.html

# redirect urls without trailing slash
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !index\.html
RewriteCond %{REQUEST_URI} !(.*)/$
RewriteRule (.*)$ %{ENV:SUBDIR}/$1/ [L,R=301]

# redirect index.html to sub root
RewriteCond %{THE_REQUEST} index\.html
RewriteRule ^index.html$ /%{ENV:SUBDIR}/ [L,R=301]

# redirect .html to url
RewriteCond %{THE_REQUEST} (.*)\ /%{ENV:SUBDIR}/.+\.html
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule (.*)\.html$ $1/ [L,R=301]

# remap url to a .html file
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{DOCUMENT_ROOT}/%{ENV:SUBDIR}/$1.html -f
RewriteRule (.*)/$ $1.html [L]

</IfModule>

Le seul changement qui serait Nice serait de le rendre plus portable en rendant dynamique le RewriteBase. Je suis sûr qu'il existe un moyen d'obtenir dynamiquement le répertoire/sub et de l'utiliser au lieu de définir le RewriteBase. Cela voudrait dire que/sous-répertoire pourrait être un répertoire avec n'importe quel nom, voire plusieurs sous-répertoires.

0
DominicM