2011-04-07 22 views
5

Hoy, estaba ayudando a alguien con un estuche de uso .htaccess, y came up with a solution que funciona pero no puedo resolverlo por mi cuenta.¿Por qué esto causa un ciclo de petición infinito?

Quería ser capaz de:

  • Examinar para index.php?id=3&cat=5
  • Ver la barra de direcciones leer index/3/5/
  • tenga el contenido sirve de index.php?id=3&cat=5

Los dos últimos pasos son bastante típico (generalmente del usuario que ingresa index/3/5 en primer lugar), pero el primer paso era obligatorio porque todavía tenía algunos enlaces de formato antiguo en su sitio y, por cualquier razón, no podrían cambiarlos. Por lo tanto, necesitaba admitir ambos formatos de URL y hacer que el usuario siempre terminara viendo el embellecido.

Después de muchas idas y venidas, se nos ocurrió la .htaccess siguiente archivo:

RewriteEngine on 

# Prevents browser looping, which does seem 
# to occur in some specific scenarios. Can't 
# explain the mechanics of this problem in 
# detail, but there we go. 
RewriteCond %{ENV:REDIRECT_STATUS} 200 
RewriteRule .* - [L] 

# Hard-rewrite ("[R]") to "friendly" URL. 
# Needs RewriteCond to match original querystring. 
# Uses "?" in target to remove original querystring, 
# and "%n" backrefs to move its components. 
# Target must be a full path as it's a hard-rewrite. 
RewriteCond %{QUERY_STRING} ^id=(\d+)&cat=(\d+)$ 
RewriteRule ^index\.php$ http://example.com/index/%1/%2/? [L,R] 

# Soft-rewrite from "friendly" URL to "real" URL. 
# Transparent to browser. 
RewriteRule ^index/(\d+)/(\d+)/$ /index.php?id=$1&cat=$2 

Si bien podría parecer que es un caso de uso un tanto extraña (" por qué no sólo tiene que utilizar el correcto enlaces en primer lugar? ", puede preguntar), simplemente vaya con eso. Independientemente del requisito original, este es el escenario y me está volviendo loco.

Sin la primera regla, el cliente ingresa en un bucle de solicitud, intentando GET /index/X/Y/ repetidamente y obteniendo 302 cada vez. La verificación en REDIRECT_STATUS hace que todo funcione sin problemas. Pero hubiera pensado que después de la regla final, no se cumplirían más reglas, el cliente no haría más solicitudes (nota, no [R]), y todo sería gravy.

Entonces ... ¿por qué esto daría como resultado un ciclo de solicitud cuando saco la primera regla?

+1

Para nada uso extraño en mi opinión – Cyclone

Respuesta

3

Sin ser capaz de jugar con su configuración, no puedo decir con certeza, pero creo que este problema se debe a la siguiente característica relativamente arcano de mod_rewrite:

Al manipular una URL/nombre de archivo en el contexto por directorio, mod_rewrite primero reescribe el nombre de archivo a su URL correspondiente (que generalmente es imposible, pero vea la directiva RewriteBase a continuación para obtener el truco) y luego inicia una nueva subpetición interna con la nueva URL. Esto reinicia el procesamiento de las fases de API.

(fuente: mod_rewrite technical documentation, me altamente recomendar la lectura de este)

En otras palabras, cuando se utiliza un RewriteRule en un fichero .htaccess, es posible que los nuevos mapas de URL reescrito para un tipo completamente diferente directorio en el sistema de archivos, en cuyo caso el archivo .htaccess en el directorio original ya no se aplicaría. Por lo tanto, siempre que un archivo RewriteRule en un archivo .htaccess coincida con la solicitud, Apache debe reiniciar el proceso desde cero con la URL modificada. Esto significa, entre otras cosas, que cada RewriteRule se vuelve a verificar.

En su caso, lo que sucede es que tiene acceso al /index/X/Y/ desde el navegador. Se desencadena la última regla en su archivo .htaccess, reescribiéndola en /index.php?id=X&cat=Y, por lo que Apache tiene que crear una nueva subrequest interna con la URL /index.php?id=X&cat=Y. Eso coincide con su regla de redirección externa anterior, por lo que Apache envía una respuesta 302 al navegador para redirigirlo al /index/X/Y/. Pero recuerde, el navegador nunca vio esa subrequest interna; por lo que sabe, ya estaba en /index/X/Y/. Así que le parece que está siendo redirigido desde /index/X/Y/ a esa misma URL, lo que activa un ciclo infinito.

Además del golpe de rendimiento, esta es probablemente una de las mejores razones por las que debe evitar poner reglas de reescritura en los archivos .htaccess cuando sea posible. Si mueve estas reglas a la configuración del servidor principal, no tendrá este problema porque las coincidencias en las reglas no desencadenarán subpeticiones internas. Si no tiene acceso a los archivos de configuración del servidor principal, una manera de evitarlo (EDIT: o eso pensé, aunque no parece funcionar - ver comentarios) es agregando el [NS] (no subpetición) bandera para su regla de redirección externa,

RewriteRule ^index\.php$ http://example.com/index/%1/%2/? [L,R,NS] 

Una vez hecho esto, ya no debería necesitar la primera regla que comprueba el REDIRECT_STATUS.

+0

Eres, por solo un día, mi héroe y salvador. –

+0

(Aunque, en la primera comprobación, 'NS' en cualquiera de las reglas no parece marcar la diferencia.) –

+0

Meh, lo aceptaré de todos modos ... si nada más, proporcionó una gran explicación de la causa más probable , que es lo que estaba buscando. Gracias –

0

La solución a continuación funcionó para mí.

RewriteEngine on 
RewriteBase/

#rule1 
#Guard condition: only if the original client request was for index.php 
RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /index\.php [NC] 
RewriteCond %{QUERY_STRING} ^id=(\d+)&cat=(\d+)$ [NC] 
RewriteRule . /index/%1/%2/? [L,R] 

#rule 2 
RewriteRule ^index/(\d+)/(\d+)/$ /index.php?id=$1&cat=$2 [L,NC] 

Aquí es lo que creo que está pasando

De los pasos que cotiza por encima

  1. Navegar a index.php? Id = 3 & cat = 5
  2. Ver el barra de ubicación leer índice/3/5/
  3. Tener el contenido servido desde index.php? id = 3 & cat = 5

En el Paso 1, Regla 1 partidos y redirige a la barra de ubicación y cumple Paso 2.

En el Paso 3, la Regla 2 ahora partidos y reescribe a index.php.

Las reglas se vuelven a ejecutar, por las razones expuestas por David, pero dado que THE_REQUEST es inmutable una vez configurado en la solicitud original, todavía contiene /index/3/5 por lo que la regla 1 no coincide.

La regla 2 tampoco coincide y se proporciona el resultado de index.php.

La mayoría de las otras variables son mutables, p. Ej. REQUEST_URI. Su modificación durante el procesamiento de la regla y la expectativa incorrecta de que el patrón coincide con la solicitud original es una razón común para los bucles infinitos.

Su siente bastante esotérica veces, pero estoy seguro de que hay una razón lógica para su complejidad :-)

EDITAR

Seguramente hay dos peticiones distintas

Hay 2 solicitudes de clientes, la original de Step1 y la de la redirección externa en el paso 2.

Lo que pasé por alto anteriormente es que cuando la Regla 2 coincide con la segunda solicitud, se reescribe en /index.php y causa un redireccionamiento interno. Esto fuerza que el archivo .htaccess para el directorio / se cargue de nuevo (podría haber sido fácilmente otro directorio con diferentes reglas .htaccess) y vuelve a ejecutar todas las reglas.

Entonces ... ¿por qué esto daría como resultado un ciclo de solicitud cuando saco la primera regla?

Cuando las reglas se vuelva a ejecutar, la primera regla coincide ahora de forma inesperada, como resultado de la reescritura de Regla2, y hace una redirección, provocando un bucle infinito.

La respuesta de David contiene la mayor parte de esta información y es lo que quise decir "por las razones que David declaró".

Sin embargo, el punto principal aquí es que necesita la condición adicional, ya sea su condición, que detiene el procesamiento de reglas adicionales en redirecciones internas, o la mía, que impide que coincida la regla 1, es necesaria para evitar el bucle infinito.

+0

Seguramente hay dos solicitudes distintas –

+0

@LightnessRacesinOrbit ver edit –

Cuestiones relacionadas