Que es mod_rewrite?

mod_rewrite es un modulo para apache ampliamente extendido. Que permite al servidor manipular las URL solicitadas.. Las URL entrantes son revisadas siguiendo una serie de reglas. Las reglas contienen expresiones regulares que detectan patrones particulares. Si el patrón es encontrado en la URL entrante, y las condiciones se cumplen, el patrón es remplazado por un nueva cadena de texto o una acción determinada. El proceso continua hasta que no halla más reglas o el sistema explícitamente se detenga.

En conclusión:

  • Hay una lista de reglas que son procesadas en orden.
  • Si las reglas coinciden, se procesa la condición para esa regla.
  • En un procesamiento se produce una substitución o una acción.

Ventajas de mod_rewrite

Hay muchas ventajas obvias al usar mod_rewrite, pero hay algunas que no son tan obvias:

mod_rewrite es la herramienta más usada para transformar URLs feas y esotéricas en lindas y limpias URLs amistosas.

Estas URL son mejores para posicionar nuestra pagina web en los motores de búsqueda debido a que son mas sencillas de interpretar y ofrecen más información contextual y semántica para sus robots.

URL ESOTERICA:          http://example.com/user.php?id=555
URL BONITA:                 http://example.com/user/juantopo

En el primer ejemplo el script PHP es invocado explícitamente y tiene que manejar un numero de id como parámetro. Un script pobremente programado probablemente fallara, y, en un script aún más pobremente programado un mal ingreso podría causar corrupción de datos, sin hablar del agujero de seguridad que mostrar el script php. Sin embargo si al usuario solamente le presentamos una URL amigable, el nunca sabrá que el script user.php existe.

Usando mod_rewrite podemos restringir muchísimos ataques e inyecciones para nuestra web, añadiendo una capa extra de seguridad, mejorando en general toda la seguridad del sitio.

Activando mod_rewrite en el servidor

Para activar mod_rewrite o cualquier otro modulo apache debemos activarlo mediante el archivo de configuración global (httpd.conf). Esto quiere decir que si contratamos un servicio de hosting, debemos verificar si esta o no activado, sin embargo como su uso esta muy extendido, la mayoría de las empresas de hosting hacen uso de el por defecto.

Si eres dueño del servidor que utilizas, mod_rewrite debe estar incluido en el momento de compilar apache, si es que ya no viene por defecto. La mayoría de los servidores apaches de las distribuciones Linux ya lo traen por defecto en sus paquetes apache, es solo cuestión de activarlo.

a2enmod rewrite

Para Windows debemos ir al archivo httpd.conf y descomentar la siguiente linea.

#LoadModule rewrite_module modules/mod_rewrite.so

La idea es que quede así:

LoadModule rewrite_module modules/mod_rewrite.so

Por supuesto después debemos reiniciar nuestro apache, en Linux Ubuntu/Debian podemos hacerlo mediante el siguiente comando (si no tienen permiso de administrador pueden usar sudo en Ubuntu):

service apache2 restart

Para verificar si efectivamente tienen activado mod_rewrite hay varias formas, usando el siguiente comando:

apachectl -t -D DUMP_MODULES

mod_rewrite correctamente instalado

Si no pueden usar en algún script php el siguiente comando

<?php phpinfo(); ?>

PHP INFO mostrando a Mod_rewrite instalado

Ahora debemos cambiar la configuración del sitio para que mod_rewrite pueda ser utilizado por el sitio. Para ello debemos editar la configuración del sitio que esta ubicado en /etc/apache2/sites-enabled/ el sitio por defecto (ubicado en /var/www/html/) se puede configurar de la siguiente forma:

# Puedes reemplazar nano por otro editor como vi o emacs y obviamente necesitas acceso root
nano /etc/apache2/sites-enabled/000-default

En donde dice AllowOverride None debemos cambiar por AllowOverride All, reiniciamos el servidor y ya tenemos disponible en nuestro sitio mod_rewrite activado.

Configurando .htaccess

En el archivo .htaccess es donde configuraremos las distintas reglas y sus procesamientos, lo normal es que por cada directorio que queramos hacer uso de mod_rewrite tengamos un .htaccess. Si ponemos reglas en el .htaccess de la raíz del sitio el prefijo (/) es removido de la variable REQUEST_URI y todas las solicitudes automáticamente asumen que son relativas al directorio actual. Tenga siempre en cuenta la barra principal (/).

Expresiones regulares

Este tutorial no intenta enseñar expresiones regulares, asumimos que usted esta familiarizado con ellas, si no es así visite el siguiente tutorial. Es importante conocer las expresiones regulares, pues estas son maravillosas herramientas y sugiero aprenderlas. Las expresiones regulares son un conocimiento necesario si tu quieres usar efectivamente mod_rewrite.

Trabajando con mod_rewrite

Ya as sido demasiado paciente, veamos un ejemplo rápido. Aquí esta el codigo desde el .htaccess

# Activando la re-escritura
RewriteEngine on
# Re-escribir url de usuarios
#   Input:  user/NAME/
#   Output: user.php?id=NAME
RewriteRule ^user/(w+)/&#36;$ user.php?id=&#36;1

Antes de explicar algo sobre el código anterior vamos a hacer una revisión de los archivos en el directorio.

El directorio contiene dos archivos index.php y user.php . El index solo tiene algunos links, de varios formatos, dirigidos hacia la pagina user. El código PHP esta usado solamente para propósitos de depuración, para confirmar que la pagina ha sido accedida usando el parámetro id. Aquí esta el contenido de user.php:

<?php
// Obtener el nombre de usuario desde la URL
$id = $_GET['id'];

?>
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Problema</title>
</head>
<body>
    <h1>Ahora esta usando user.php!</h1>
    <p>Bienvenido: <?php echo $id; ?></p>
</body>
</html>

Este ejemplo tiene unos cuantos ejemplos, primero observe que activamos la re-escritura de URL usando la directiva RewriteEngine. Si tu archivo .htaccess va a utilizar reglas de re-escritura, debe incluir siempre esta linea. De otra manera, no puedes estar seguro si esta activado o no.

La primer regla de re-escritura RewriteRule es para manejar el script user.php. Como indican los comentarios, estamos reescribiendo la URL "amistosa" a una URL PHP normal.

La regla:
RewriteRule ^user/(w+)/?$ user.php?id=$1

Patron de busqueda:
^              Situamos la búsqueda al principio del string
user/          Valor literal del patrón de busqueda
(w+)          Captura cualquier palabra y la coloca en $1
/?             Valor opcional por si utilizamos una barra invertida
$              Final del input

Sustitución
user.php?id=   Cadena literal a usar
$1             ?a primer palabra capturada

La siguiente tabla explica la posibilidades

ingreso coincide Captura Salida Resultado
user.php?id=joe no user.php?id=joe Normal
user/joe Si joe user.php?id=joe Optimo
user/joe/ Si joe user.php?id=joe Optimo
user/joe/x no user/joe/x Fallo

En el primer y ultimo ejemplo tenemos una string objetivo que no son afectadas por la RewriteRule. Sin embargo funciona adecuadamente por invoca adecuadamente el script PHP mientras que la ultima no, simplemente nos devuelve un error 404.

Este ejemplo es sencillo de entender. Sin embargo, hay un montón de detalles minuciosos que pase por alto. Para ejecutar script más complejos, debo clarificar minuciosamente lo que sucedió con anterioridad. En la siguiente sección, voy a hablar sobre cada paso que sucede en el ciclo.

El flujo de ejecución en detalle

El flujo de ejecución alrededor de las reglas de re-escrituras son simples, aunque no visto desde una perspectiva inversa. Así que voy a dividirlos en pequeños y útiles detalles.

Todo comienza cuando el usuario realiza una solicitud al servidor. El escribe o pulsa sobre una URL en el navegador, luego su navegador convierte esto en una solicitud HTTP para enviar al servidor. Apache recibe esta solicitud y parsea esta en piezas. La siguiente imagen es un ejemplo de esto:

Partes de una petición HTTP Tenga en cuanta que cada vez que menciono una variable Apache, uso esta extraña sintaxis: %{APACHE_VAR}.

¿Entonces que parte de esto tiene que ver con mod_rewrite? Si tu estas trabajando en un archivo .htaccess, entonces tu estas trabajando con la porción REMOTE_URI sin la barra inclinada principal.

Para eliminar cualquier tipo de ambigüedad en la siguiente imagen, resaltado en dorado podemos observar cual es la porción de la URL Part sobre la que mod_rewrite actúa adentro del archivo .htaccess:

Partes de una petición HTTP

Para el resto de esta sección voy usar estas dos URLs para describir el flujo de la ejecución. Voy a referirme a la primera como la URL Verde y la segunda como la URL azul. Y para referirme a ellas lo hare como "URL Parcial" a lo largo de este análisis, que la URL sin la barra inclinada principal.

Diagrama de flujo

He aquí un diagrama de flujo que intenta proveer una visualización de el flujo genérico de ejecución a lo través de las múltiples reglas de un archivo .htaccess:

Flujo de ejecución de mod_rewrite

No que en la parte superior de el flujo del diagrama, la URL ingresa en el RewriteRule el valor modificado ingresa dentro de la SiguienteRegla. Sin entrar en detalles, cada RewriteCond esta asociado con una sola RewriteRule. Las condiciones aparecen antes de las reglas, pero solo se evalúa si las reglas coinciden. Como el diagrama de flujo muestra, si una regla de re-escritura coincide, entonces Apache chequea si hay alguna condición para esa regla. Si no hay más condiciones entonces Apache realiza la substitución, pero ello solo sucede si todas las condiciones son verdaderas. Vamos a visualizar esto en un ejemplo concreto:

Nuestras reglas mod_rewrite

Aquí podemos ver dos reglas, la regla #1 es igual a la vimos en el ejemplo anterior. La regla 2 es nueva, note que usa una condición. La "URL parcial" de la que hemos estado hablando es procesada por las reglas siguiendo un orden de arriba hacia abajo.

La clave para entender este ejemplo es entender el objetivo. En el he permitido el acceso a URL amigables, pero denegando el acceso directo al script PHP. Algunas personas dirán que este es una mala idea. Podrían decir que, como desarrollador esto hace las cosas más difíciles a la hora de depurar. Esto es cierto, yo no recomiendo hacer esto, es simplemente un ejemplo...

URL VERDE

Flujo de ejecución de ejemplo verde

Arriba de todo podemos ver la variable Apache THE_REQUEST. He puesto esto arriba porque, a diferencia de muchas de las variables de Apache nosotros vamos a trabajar con ella durante toda la duración de la solicitud, esta variable NUNCA CAMBIA, esta es una de las razones por la cual la regla #2 usa %{THE_REQUEST} en vez de THE_REQUEST.

Regla #1

  • La URL coincide con el patrón
  • No hay condiciones, entonces continua
  • La substitución se realiza
  • Como no hay ninguna flags entonces continua la ejecución

Despues de pasar por la primera regla la URL a cambiado. Ahora la URL a sido reescrita a perfil.php?id=juan, por lo que Apache a actualizado muchas de sus variables. La nueva "URL parcial", continua hacia la segunda regla.

Regla 2

  • La URL coincide con el patrón
  • Hay condiciones, entonces se comprueban.
  • La variable THE_REQUEST no contiene perfil.php, entonces las condiciones falla.
  • Como las condiciones fallan, ignoramos la substitución y la flags
  • La URL sale sin cambiar de esta regla.

URL AZUL

flujo de URL AZUL

Regla #1

  • La URL no coincide con el patrón
  • Todo lo demás es ignorado.

Regla #2

  • La URL coincide con el patrón.
  • Como hay condiciones, se comprueban.
  • THE_REQUEST contiene perfil.php, entonces la condición se cumple.
  • Se realiza la substitución
  • Como hay una flags F que significa prohibido (de Forbidden) se despliega una repuesta 403 al cliente.

La siguiente es una tabla de respuestas para el ejemplo actual:

ingreso coincide Captura Salida Resultado
perfil.php?id=juan Si (#2) perfil.php?id=juan Prohibido 403
perfil/juan Si (#1) juan perfil.php?id=juan correcto
perfil/juan/ Si (#1) juan perfil.php?id=juan correcto
perfil/juan/x No perfil/juan/x No encontrado 404

Sintaxis

Puedes visitar la pagina oficial de Apache para aún más información .

Sintaxis de rewriteRule Sintaxis de rewriteRule

Ejemplos

Removiendo el www

Esta es una de las más clásicas reglas de re-escritura:

RewriteEngine on
RewriteCon %{HTTP_HOST} ^www.ejemplo.com$ [NC]
RewriteRule ^(.*)$ http://example.com/$1 [R=301,L]

La RewriteRule anterior coincide con cualquier solicitud y la guarda como $1 , lo importante sucede en la RewriteCond. Esta condición chequea la variable HTTP_HOST para determinar si empieza con "www." :

  • La substitución es para toda la URL (esta empieza con http://)
  • La substitución contiene $1, que esta es capturada luego.
  • La flag [R=301] redirecciona el navegador a la nueva URL.
  • La flag [L] indica que esta es la ultima regla a ejecutar. Después de esta linea si la condición se a ejecutado no se seguirá procesando las demás reglas.

Prohibiendo el Hotlinking

¿Que es el HotLinking? Aunque no sea 100% confiable, en general esto suele ser suficiente.

# combatiendo el Hotlinking
RewriteEngine On
RewriteCond %{HTTP_REFERER} !^http://example.net/?.*$ [NC]
RewriteCond %{HTTP_REFERER} !^http://example.net/?.*$ [NC]
RewriteRule .(gif|jpe?g|png|bmp)$ - [F,NC]

Conclusión

Espero que encuentres útil este tutorial y que hallas podido perderle en miedo a mod_rewrite. Te invito a compartir este y otros tutoriales. Y si sabes como mejorar este tutorial espero tu respuesta.