Funciones ocultas de mod_rewrite

Parece haber un número decente de hilos mod_rewrite flotando últimamente con un poco de confusión sobre cómo funcionan ciertos aspectos de él. Como resultado, he recostackdo algunas notas sobre la funcionalidad común, y tal vez algunos matices molestos.

¿Qué otras características / problemas comunes has encontrado usando mod_rewrite ?

Dónde colocar las reglas de mod_rewrite

mod_rewrite reglas mod_rewrite pueden colocarse dentro del archivo httpd.conf , o dentro del archivo .htaccess . si tiene acceso a httpd.conf , colocar reglas aquí ofrecerá un beneficio de rendimiento (ya que las reglas se procesan una vez, en lugar de cada vez que se llama al archivo .htaccess ).

Registro de solicitudes mod_rewrite

El registro puede habilitarse desde el archivo httpd.conf (incluido ):

 # 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 puedes 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 : los redireccionamientos externos son implícitamente 302 redirecciones:

     # 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. Forzando 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 de 301
    • [L] o [last] – detiene el proceso de reescritura (mira la nota más abajo en las trampas comunes)
    • [NC] o [nocase] : especifique que la coincidencia debe ser insensible a las mayúsculas y minúsculas.

    Usar la forma larga de banderas es a menudo 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_alias estilo mod_alias con mod_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 : puede mezclar mod_alias con mod_rewrite , pero implica más trabajo que simplemente manejar redirecciones básicas como se mod_alias anteriormente.

  2. El contexto afecta la syntax

    Dentro de los archivos .htaccess , una barra diagonal no se usa 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! (a veces)

    El indicador [L] detiene el procesamiento de otras reglas de reescritura para ese pase a través del conjunto de reglas . Sin embargo, si la URL se modificó en ese pase y está en el contexto .htaccess o en la sección , su solicitud modificada volverá a pasar a través del motor de análisis de URL. Y en el siguiente pase, puede coincidir con una regla diferente esta vez. 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 el indicador [END] ( ver documentos de Apache ) en lugar del indicador [L] , si realmente desea detener todo el procesamiento posterior de las reglas (y pases posteriores). Sin embargo, el indicador [END] solo está disponible para Apache v2.3.9 + , por lo que si tiene v2.2 o menos, se quedará bloqueado solo con el indicador [L] .

    Para las versiones anteriores, debe confiar en RewriteCond declaraciones RewriteCond para evitar la coincidencia de las 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 bien, debe asegurarse de que sus RewriteRule estén en un contexto (es decir, httpd.conf ) que no hará que su solicitud sea re-analizada.

si necesita ‘bloquear’ las redirecciones / reescrituras internas en el .htaccess, eche un vistazo al

 RewriteCond %{ENV:REDIRECT_STATUS} ^$ 

condición, como se discutió aquí .

El trato con RewriteBase:

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

 RewriteBase / 

Otras trampas:

1- A veces es una buena idea desactivar MultiViews

 Options -MultiViews 

No estoy del todo bien con todas las capacidades de MultiViews, pero sé que estropea mis reglas de mod_rewrite cuando está activo, porque una de sus propiedades es tratar de ‘adivinar’ una extensión a un archivo que cree que estoy buscando. .

Voy a explicar: supongamos que tienes 2 archivos php en tu directorio web, archivo1.php y archivo2.php y agregas estas condiciones y reglas a tu .htaccess:

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

Usted asume que todas las direcciones URL que no coinciden con un archivo o un directorio serán tomadas por file1.php. ¡Sorpresa! Esta regla no se cumple para la URL http: // myhost / file2 / somepath . En cambio, te llevan dentro de file2.php.

Lo que sucede es que MultiViews adivinó automágicamente que la url que realmente quería 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 en ese momento estás cuestionando todo lo que pensabas que sabías sobre mod_rewrite. Luego comienzas a jugar con las reglas para intentar darle sentido a la lógica detrás de esta nueva situación, pero cuanto más pruebas, menos sentido tiene.

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

2- habilitar FollowSymlinks

 Options +FollowSymLinks 

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

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 dynamic de carga:

Si usa mod_proxy para equilibrar su sistema, es posible agregar un rango dynamic de servidor de trabajo.

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

Una mejor comprensión de la bandera [L] está en orden. La bandera [L] es la última, 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 ) (énfasis 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 otras reglas.

Si está utilizando RewriteRule en los archivos .htaccess o en las secciones , es importante conocer cómo se procesan las reglas. La forma simplificada de esto es que una vez que las reglas se han procesado, la solicitud reescrita se devuelve al motor de análisis de URL para hacer lo que se le ocurra. Es posible que a medida que se maneje la solicitud reescrita, el archivo .htaccess o la sección puedan encontrarse nuevamente, y por lo tanto, el conjunto de reglas se puede ejecutar nuevamente desde el inicio. Lo más común es que esto suceda si una de las reglas provoca una redirección, ya sea interna o externa, lo que provoca que el proceso de solicitud comience nuevamente.

Entonces, la bandera [L] deja de procesar cualquier otra regla de reescritura para esa pasada a través del conjunto de reglas. Sin embargo, si su regla marcada con [L] modificó la solicitud y se encuentra en el contexto .htaccess o en la sección , su solicitud modificada volverá a pasar a través del motor de análisis de URL. Y en el siguiente pase, puede coincidir con una regla diferente esta vez. Si no comprende lo que sucedió, parece que su primera regla de reescritura con la bandera [L] no tuvo ningún efecto.

La mejor forma de evitar esto es utilizar el indicador [END] ( http://httpd.apache.org/docs/current/rewrite/flags.html#flag_end ) en lugar del indicador [L], si realmente desea detenerse. todo el procesamiento posterior de las reglas (y posterior repaso). Sin embargo, el indicador [END] solo está disponible para Apache v2.3.9 +, por lo que si tiene v2.2 o menos, se quedará bloqueado solo con el indicador [L]. En este caso, debe confiar en las declaraciones RewriteCond para evitar la coincidencia de las reglas en pases posteriores del motor de análisis de URL. O bien, debe asegurarse de que sus RewriteRule estén en un contexto (es decir, httpd.conf) que no hará que su solicitud sea re-analizada.

Otra gran característica son rewrite-map-expansions. Son especialmente útiles si tiene una gran cantidad de hosts / reescrituras para manejar:

Son como un reemplazo de clave-valor:

 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

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

Establecer condicionalmente una variable de entorno:

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

Devuelve una respuesta 503: la RewriteRule [R] RewriteRule puede tomar un valor que no sea 3xx y devolver una respuesta que no redirecciona, por ejemplo, para el tiempo de inactividad / mantenimiento administrado:

 RewriteRule .* - [R=503,L] 

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

Además, mod_rewrite puede actuar como una interfaz de gran potencia para mod_proxy, por lo que puede hacer esto en lugar de escribir directivas ProxyPass :

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

Opinión: el uso de RewriteRule y RewriteCond 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. El control de las solicitudes en el camino hacia el back-end y la posibilidad de modificar las respuestas en el camino de regreso hacen de mod_rewrite el lugar ideal para centralizar todas las configuraciones relacionadas con el enrutamiento.

Tómese el tiempo para aprender, vale la pena! 🙂