Características ocultas de mod_rewrite

119

Parece haber una cantidad decente de mod_rewritehilos flotando últimamente con un poco de confusión sobre cómo funcionan ciertos aspectos. Como resultado, he compilado algunas notas sobre la funcionalidad común y quizás algunos matices molestos.

¿Qué otras funciones / problemas comunes te has encontrado usando mod_rewrite?

Owen
fuente
5
Véase también serverfault.com/questions/214512/…
Michael Myers

Respuestas:

203

Dónde colocar las reglas mod_rewrite

mod_rewritelas reglas se pueden colocar dentro del httpd.confarchivo o dentro del .htaccessarchivo. si tiene acceso a httpd.conf, colocar reglas aquí ofrecerá un beneficio de rendimiento (ya que las reglas se procesan una vez, a diferencia de cada vez .htaccessque se llama al archivo).

Registro de solicitudes mod_rewrite

El registro se puede habilitar desde dentro del httpd.confarchivo (incluido <Virtual Host>):

# logs can't be enabled from .htaccess
# loglevel > 2 is really spammy!
RewriteLog /path/to/rewrite.log
RewriteLogLevel 2

Casos de uso común

  1. Para canalizar todas las solicitudes a un solo punto:

    RewriteEngine on
    # ignore existing files
    RewriteCond %{REQUEST_FILENAME} !-f   
    # ignore existing directories
    RewriteCond %{REQUEST_FILENAME} !-d   
    # map requests to index.php and append as a query string
    RewriteRule ^(.*)$ index.php?query=$1 
    

    Desde Apache 2.2.16 también puede usar FallbackResource.

  2. Manejo de redireccionamientos 301/302:

    RewriteEngine on
    # 302 Temporary Redirect (302 is the default, but can be specified for clarity)
    RewriteRule ^oldpage\.html$ /newpage.html [R=302]  
    # 301 Permanent Redirect
    RewriteRule ^oldpage2\.html$ /newpage.html [R=301] 
    

    Nota : las redirecciones externas son implícitamente redirecciones 302:

    # this rule:
    RewriteRule ^somepage\.html$ http://google.com
    # is equivalent to:
    RewriteRule ^somepage\.html$ http://google.com [R]
    # and:
    RewriteRule ^somepage\.html$ http://google.com [R=302]
    
  3. Forzar SSL

    RewriteEngine on
    RewriteCond %{HTTPS} off
    RewriteRule ^(.*)$ https://example.com/$1 [R,L]
    
  4. Banderas comunes:

    • [R]o [redirect]- forzar una redirección (por defecto es una redirección temporal 302)
    • [R=301]o [redirect=301]- forzar un redireccionamiento permanente 301
    • [L]o [last]- detener el proceso de reescritura (vea la nota a continuación en los errores comunes)
    • [NC]o [nocase]- especificar que la coincidencia no debe distinguir entre mayúsculas y minúsculas


    El uso de banderas de formato largo suele ser más legible y ayudará a otros que vengan a leer su código más tarde.

    Puede separar varios indicadores con una coma:

    RewriteRule ^olddir(.*)$ /newdir$1 [L,NC]
    

Errores comunes

  1. Mezcla de mod_aliasredireccionamientos de estilo conmod_rewrite

    # Bad
    Redirect 302 /somepage.html http://example.com/otherpage.html
    RewriteEngine on
    RewriteRule ^(.*)$ index.php?query=$1
    
    # Good (use mod_rewrite for both)
    RewriteEngine on
    # 302 redirect and stop processing
    RewriteRule ^somepage.html$ /otherpage.html [R=302,L] 
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    # handle other redirects
    RewriteRule ^(.*)$ index.php?query=$1                 
    

    Nota : se puede mezclar mod_aliascon mod_rewrite, pero implica más trabajo que simplemente tratando las redirecciones básicos que el anterior.

  2. El contexto afecta la sintaxis

    Dentro de .htaccessarchivos, una barra inicial no se utiliza en el patrón RewriteRule:

    # given: GET /directory/file.html
    
    # .htaccess
    # result: /newdirectory/file.html
    RewriteRule ^directory(.*)$ /newdirectory$1
    
    # .htaccess
    # result: no match!
    RewriteRule ^/directory(.*)$ /newdirectory$1
    
    # httpd.conf
    # result: /newdirectory/file.html
    RewriteRule ^/directory(.*)$ /newdirectory$1
    
    # Putting a "?" after the slash will allow it to work in both contexts:
    RewriteRule ^/?directory(.*)$ /newdirectory$1
    
  3. [L] no es el último! (algunas veces)

    La [L]bandera deja de procesar cualquier regla de reescritura adicional para que pase a través del conjunto de reglas . Sin embargo, si la URL se modificó en esa pasada y estás en el .htaccesscontexto o en la <Directory>sección, entonces tu solicitud modificada se pasará nuevamente a través del motor de análisis de URL. Y en la siguiente pasada, esta vez puede coincidir con una regla diferente. Si no entiende esto, a menudo parece que su [L]bandera no tuvo ningún efecto.

    # processing does not stop here
    RewriteRule ^dirA$ /dirB [L] 
    # /dirC will be the final result
    RewriteRule ^dirB$ /dirC     
    

    Nuestro registro de reescritura muestra que las reglas se ejecutan dos veces y la URL se actualiza dos veces:

    rewrite 'dirA' -> '/dirB'
    internal redirect with /dirB [INTERNAL REDIRECT]
    rewrite 'dirB' -> '/dirC'
    

    La mejor manera de evitar esto es usar la [END]bandera ( ver documentos de Apache ) en lugar de la [L]bandera, si realmente desea detener todo el procesamiento posterior de las reglas (y las pasadas posteriores). Sin embargo, la [END]bandera solo está disponible para Apache v2.3.9 + , por lo que si tiene v2.2 o una versión inferior, solo tiene la [L]bandera.

    Para versiones anteriores, debe confiar en RewriteConddeclaraciones para evitar la coincidencia de reglas en pases posteriores del motor de análisis de URL.

    # Only process the following RewriteRule if on the first pass
    RewriteCond %{ENV:REDIRECT_STATUS} ^$
    RewriteRule ...
    

    O debe asegurarse de que su RewriteRule esté en un contexto (es decir httpd.conf) que no hará que se vuelva a analizar su solicitud.

Owen
fuente
10
Amigo, totalmente el mejor artículo en Internet ahora sobre reescritura de mods. Odio esa cosa. Soy un hereje lighttpd por lo mucho que odio mod_rewrite.
Kent Fredric
3
Esta ha sido la guía más útil que he encontrado sobre mod_rewrite hasta ahora. El solo hecho de conocer RewriteLog ayudó a solucionar tantos problemas que lo que me estaba tomando días para rastrear se convirtió en unos pocos minutos. (Me refiero a que las reglas fueron escritas pero no pude entender por qué no estaban funcionando)
Joe Chin
Publicación de hace 1 año, pero una de las cosas más útiles que he encontrado en SO - para mí.
Erik
3
La [L]bandera significa que una regla es la última en el procesamiento actual, esto no dejará de reescribirse, porque son redireccionamientos internos, por lo que se dirBaplicará dirCen el próximo procesamiento de htaccess. Solo RewriteRule ^(.*)$ index.php?query=$1habrá un ciclo infinito de redireccionamientos internos (en la práctica, se termina después de 10 iteraciones). -1 porque sugieres que [L] no es el último . No está terminando el proceso de reescritura, pero es el último .
kbec
3
Creo que RewriteCond %{HTTPS} offes la forma preferida de verificar una conexión HTTPS (en su ejemplo de forzar el tráfico no SSL a HTTPS)
Madbreaks
22

si necesita 'bloquear' redirecciones / reescrituras internas para que no ocurran en el .htaccess, eche un vistazo a la

RewriteCond %{ENV:REDIRECT_STATUS} ^$

condición, como se discute aquí .

mromaine
fuente
Gracias, ¡eso acaba de solucionar mi problema!
Mateo
¡Gracias por mí también, salvavidas!
Benjamin
¡Esto es realmente un salvavidas! La gente debería ser más consciente de eso. De hecho, voy a sugerir esto a todas las preguntas sobre .*la [L]bandera que leí antes de llegar aquí.
Qwerty
He visto varias modificaciones a este 200, !=200, ^., ^$. Aparentemente, la variable se establece en 200para una redirección, pero también otras páginas (error y demás) la establecen en algún valor. Ahora que los medios que o bien comprobar si is empty, is not empty, is 200o is not 200, dependiendo de lo que necesita.
Qwerty
18

El trato con RewriteBase:

Casi siempre es necesario configurar RewriteBase. Si no lo hace, apache adivina que su base es la ruta del disco físico a su directorio. Así que empieza con esto:

RewriteBase /
Sean McMillan
fuente
¡Ah! Eso solucionó totalmente el problema que estaba teniendo. ¡Gracias por eso!
Tom Savage
3
¿Alguna forma de decirlo RewriteBase ., o algo que indique que debe mantener la URL igual, simplemente cambiando lo que ha especificado?
Jay K
Gracias, esta es una información invaluable. :)
AturSams
2
Solo necesita establecer RewriteBasesi está utilizando la sustitución de ruta relativa en la RewriteRuledirectiva. Es mejor evitar el uso de rutas relativas.
MrWhite
2
No estoy de acuerdo con esta respuesta. En nuestro equipo de desarrollo lo evitamos por RewriteBasecompleto, ya que casi todos los desarrolladores malinterpretan lo que hace. Como dijo @ w3d, solo lo necesita si desea guardar caracteres y desea aplicar la misma base a todas sus RewriteRules en un solo archivo. Es probable que su código sea más claro para los demás si lo evita.
Simon East
13

Otras trampas:

1- A veces es una buena idea desactivar MultiViews

Options -MultiViews

No estoy bien versado en todas las capacidades de MultiViews, pero sé que arruina mis reglas mod_rewrite cuando está activo, porque una de sus propiedades es intentar 'adivinar' una extensión de un archivo que cree que estoy buscando .

Lo explicaré: suponga que tiene 2 archivos php en su directorio web, file1.php y file2.php y agrega estas condiciones y reglas a su .htaccess:

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ file1.php/$1 

Asume que todas las URL que no coinciden con un archivo o directorio serán capturadas por file1.php. ¡Sorpresa! Esta regla no se cumple para la URL http: // myhost / file2 / somepath . En su lugar, lo llevan dentro de file2.php.

Lo que está sucediendo es que MultiViews adivinó automáticamente que la URL que realmente deseaba era http: //myhost/file2.php/somepath y con mucho gusto lo llevó allí.

Ahora, no tienes ni idea de lo que acaba de pasar y estás en ese punto cuestionando todo lo que creías saber sobre mod_rewrite. Luego comienzas a jugar con las reglas para tratar de entender la lógica detrás de esta nueva situación, pero cuanto más pruebas, menos sentido tiene.

Bien, en resumen, si desea que mod_rewrite funcione de una manera que se aproxime a la lógica, apagar MultiViews es un paso en la dirección correcta.

2- habilitar FollowSymlinks

Options +FollowSymLinks 

Ese, realmente no conozco los detalles, pero lo he visto mencionado muchas veces, así que hazlo.

Michael Ekoka
fuente
Gracias :) Noté sorpresas inesperadas como / log / activity convirtiéndose en /log.txt/activity .. Gracias por el consejo :) .. lástima que las computadoras nunca se diviertan, suceden cosas inesperadas como seducir accidentalmente a todas tus compañeras de trabajo en Facebook :)
AturSams
1
+FollowSymLinksse menciona en la documentación como obligatorio para mod_rewriteque funcione, por vagas razones de seguridad.
Joey
Dos declaraciones aquí me preocupan inmensamente: 'No estoy bien versado en todas las capacidades de MultiViews, pero sé que arruina mis reglas mod_rewrite cuando está activo' y este 'Ese, realmente no sé los detalles de , pero lo he visto mencionado muchas veces, así que hazlo '. Desearía que personas como tú no escribieran respuestas en SO sobre cosas de las que no estás seguro.
TheCarver
1
@PaparazzoKid: Creo que estás confundiendo SO con una enciclopedia. Es una comunidad de personas que se unen para comprender mejor la tecnología con la que están trabajando. A diferencia de AW White y Joey antes que usted, su comentario es casi nulo. MV y FSL son dos de las muchas opciones de Apache. Mi respuesta es sobre las dificultades al trabajar específicamente con mod_rw, un módulo separado, que entra en conflicto con algunas opciones y funciona con otras. Expliqué cómo MV afecta mod_rw y mencioné que + FSL es una recomendación popular. Joey confirmó que de hecho es obligatorio. ¿Qué traes a la mesa?
Michael Ekoka
Gracias. Acabo de pasar la mayor parte de una hora haciendo funcionar un sitio heredado y tratando de depurar las reglas de reescritura, solo para descubrir que MultiViews lo estaba anulando todo.
Andrew McCombe
5

La ecuación se puede hacer con el siguiente ejemplo:

RewriteCond %{REQUEST_URI} ^/(server0|server1).*$ [NC]
# %1 is the string that was found above
# %1<>%{HTTP_COOKIE} concatenates first macht with mod_rewrite variable -> "test0<>foo=bar;"
#RewriteCond search for a (.*) in the second part -> \1 is a reference to (.*)
# <> is used as an string separator/indicator, can be replaced by any other character
RewriteCond %1<>%{HTTP_COOKIE} !^(.*)<>.*stickysession=\1.*$ [NC]
RewriteRule ^(.*)$ https://notmatch.domain.com/ [R=301,L]

Equilibrio de carga dinámico:

Si usa mod_proxy para equilibrar su sistema, es posible agregar un rango dinámico de servidor trabajador.

RewriteCond %{HTTP_COOKIE} ^.*stickysession=route\.server([0-9]{1,2}).*$ [NC]
RewriteRule (.*) https://worker%1.internal.com/$1 [P,L]
DrDol
fuente
4

Es necesario comprender mejor la bandera [L]. El indicador [L] es el último, solo tiene que entender qué hará que su solicitud sea enrutada nuevamente a través del motor de análisis de URL. De los documentos ( http://httpd.apache.org/docs/2.2/rewrite/flags.html#flag_l ) (el énfasis es mío):

El indicador [L] hace que mod_rewrite deje de procesar el conjunto de reglas. En la mayoría de los contextos, esto significa que si la regla coincide, no se procesarán más reglas. Esto corresponde al último comando en Perl, o al comando break en C. Use este indicador para indicar que la regla actual debe aplicarse inmediatamente sin considerar más reglas.

Si está utilizando RewriteRule en archivos .htaccess o en <Directory>secciones , es importante comprender cómo se procesan las reglas. La forma simplificada de esto es que una vez que se han procesado las reglas, la solicitud reescrita se devuelve al motor de análisis de URL para hacer lo que pueda con ella. Es posible que a medida que se maneja la solicitud reescrita, el archivo .htaccess o la<Directory> sección se puedan encontrar nuevamente y, por lo tanto, el conjunto de reglas se pueda ejecutar nuevamente desde el principio. Por lo general, esto sucederá si una de las reglas provoca una redirección, ya sea interna o externa, lo que hace que el proceso de solicitud comience de nuevo.

Por lo que el indicador [L] hace parada el tratamiento de sus reglas de reescritura más para que pase a través del conjunto de reglas. Sin embargo, si su regla marcada con [L] modificó la solicitud y está en el contexto .htaccess o<Directory> sección, entonces su solicitud modificada se pasará nuevamente a través del motor de análisis de URL. Y en la siguiente pasada, esta vez puede coincidir con una regla diferente. Si no comprende lo que sucedió, parece que su primera regla de reescritura con la bandera [L] no tuvo ningún efecto.

La mejor manera de evitar esto es usar la bandera [END] ( http://httpd.apache.org/docs/current/rewrite/flags.html#flag_end ) en lugar de la bandera [L], si realmente quieres detener todo el procesamiento posterior de las reglas (y el análisis posterior). Sin embargo, el indicador [FIN] solo está disponible para Apache v2.3.9 +, por lo que si tiene v2.2 o inferior, se quedará con el indicador [L]. En este caso, debe confiar en las declaraciones de RewriteCond para evitar que las reglas coincidan en las pasadas posteriores del motor de análisis de URL. O debe asegurarse de que sus RewriteRule estén en un contexto (es decir, httpd.conf) que no provocará que se vuelva a analizar su solicitud.

JaredC
fuente
3

Otra gran característica son las expansiones de reescritura de mapas. Son especialmente útiles si tiene una gran cantidad de hosts / reescrituras para manejar:

Son como un reemplazo de valor clave:

RewriteMap examplemap txt:/path/to/file/map.txt

Entonces puedes usar un mapeo en tus reglas como:

RewriteRule ^/ex/(.*) ${examplemap:$1}

Puede encontrar más información sobre este tema aquí:

http://httpd.apache.org/docs/2.0/mod/mod_rewrite.html#mapfunc

SER
fuente
Ignore esta función si está utilizando .htaccessreescrituras basadas en. No funciona en este contexto.
TerryE
2
La directiva RewriteMap debe usarse en el contexto del servidor (httpd.conf), pero una vez definida allí, puede usar el mapa a través de RewriteRule en un archivo .htaccess.
JaredC
2

mod_rewrite puede modificar aspectos del manejo de solicitudes sin alterar la URL, por ejemplo, configurar variables de entorno, configurar cookies, etc. Esto es increíblemente útil.

Establezca condicionalmente una variable de entorno:

RewriteCond %{HTTP_COOKIE} myCookie=(a|b) [NC]
RewriteRule .* - [E=MY_ENV_VAR:%b]

Devolver una respuesta 503: RewriteRuleel [R]indicador 'puede tomar un valor que no sea 3xx y devolver una respuesta que no redireccione, por ejemplo, para el tiempo de inactividad administrado / mantenimiento:

RewriteRule .* - [R=503,L]

devolverá una respuesta 503 (no una redirección per se).

Además, mod_rewrite puede actuar como una interfaz superpoderosa para mod_proxy, por lo que puede hacer esto en lugar de escribir ProxyPassdirectivas:

RewriteRule ^/(.*)$ balancer://cluster%{REQUEST_URI} [P,QSA,L]

Opinión: Usando RewriteRulesyRewriteCond para enrutar solicitudes a diferentes aplicaciones o balanceadores de carga en función de prácticamente cualquier aspecto concebible de la solicitud es inmensamente poderoso. Controlar las solicitudes en su camino hacia el backend y poder modificar las respuestas en su camino de regreso hace que mod_rewrite sea el lugar ideal para centralizar todas las configuraciones relacionadas con el enrutamiento.

Tómate el tiempo para aprenderlo, ¡vale la pena! :)

semanal
fuente