Reescribir direcciones con mod_rewrite de Apache

Publicado el 29 de junio de 2005 - Han habido 32311 lecturas de este escrito

Muchas veces, cuando estamos haciendo páginas web, nos interesa tener URIs lo más limpias que sea posible; no ya porque te indexen mejor los buscadores o porque queden más bonitas, sino porque un usuario normal, por lo general, va a recordar más fácilmente una dirección de este tipo, con lo que le será más fácil volver a nuestra página.

Para esta tarea se suelen usar archivos .htaccess del servidor HTTP Apache junto con el módulo de reescritura del mismo (el mod_rewrite). El mod_rewrite es una pequeña extensión de Apache que nos permite que, al visitar una dirección de una página, realmente se está llamando a otra diferente.

Funcionamiento

Apache usa archivos con el nombre de .htaccess (que no es que tengas que poner nombre.htaccess, sino simplemente eso, .htaccess) para que el administrador de las páginas web (que no del servidor) pueda definir una serie de parámetros de configuración para su espacio. Deben estar ubicados en el directorio sobre el que quieras aplicar esa configuración.

Crear archivos de este tipo en plataformas Microsoft Windows puede ser un problema, ya que el famoso "sistema operativo" no te deja poner archivos sin nombre (solo tiene extensión) pero ello no significa que no se pueda hacer.

El archivo se puede crear de varias maneras, yo recomiendo PHP ya que es muy fácil de hacer. Simplemente con poner algo como lo siguiente nos debería crear el archivo:

<?php
touch
('.htaccess');
?>

Antes de seguir adelante debemos comprobar que podemos usar los archivos .htaccess y que tenemos el mod_rewrite activado.

Comprobar que podemos usar .htaccess es bastante sencillo, basta con crear un archivo con ese nombre y poner en el una cadena aleatoria (tipo "asdasd123") y guardarlo en el directorio sobre el que queremos aplicar la reescritura. Si el servidor nos devuelve un Internal Server Error es que todo ha ido bien, podemos usarlos.

Para comprobar que tenemos el mod_rewrite podemos hacer un phpinfo() (si disponemos de PHP en el servidor). Si Apache está como módulo de PHP entonces podemos mirar los módulos que tiene cargados; muy posiblemente el mod_rewrite está entre ellos.

Para empezar deberemos poner al principio del documento (me refiero al .htaccess) la cadena RewriteEngine On, que se encarga de activar el módulo de reescritura, tras poner esto ya debemos poder empezar a escribir nuestras reglas para formatear las direcciones como nos plazca. A veces puede ser necesario poner un Options FollowSymLinks antes para que funcione, de modo que quedaría así:

Options FollowSymLinks
RewriteEngine On

Si te ha fallado alguno de los pasos o no estás seguro de si tienes activado el mod_rewrite siempre puedes consultarle al administrador de tu sistema.

Funciones o comandos del mod_rewrite

Hay varias funciones (o comandos) que usaremos a menudo y debemos conocer para escribirlos sin tener que recurrir al manual cada vez. La sintaxis de los comandos del mod_rewrite es bastante sencilla y rápida de entender. Los comandos empiezan con Rewrite. Normalmente el patrón es el siguiente:

Comando Parámetro1 Parámetro2

Aunque en algunos casos varía ligeramente, como se verá a continuación:

RewriteEngine

Solo acepta dos valores para su único parámetro: On y Off. Le dice a Apache una vez puesto en el .htaccess si debe o no iniciar el motor de reescritura. Está desactivado por defecto.

#Para activar la reescritura
RewriteEngine On

RewriteBase

Nos permite ajustar una "base" para las rutas que escribamos a partir de que se usa. Lo que hace es añadir a las rutas siguientes el prefijo que indiquemos, esto puede ahorrar mucho espacio y es necesario muchas veces al no encontrarse el archivo .htaccess en el mismo directorio sobre el que se quiere aplicar (por eso es recomendable ponerlo ahí).

RewriteRule ^/pagina/index.xml$ /pagina/xml.php?generar=xml
#Con RewriteBase nos podemos ahorrar el /pagina/ de la siguiente forma:
RewriteBase /pagina/
RewriteRule ^index.xml$ xml.php?generar=xml

RewriteCond

Con esto se pueden definir sentencias condicionales, la sintaxis es muy simple y fácil de entender. Se pueden unir varias condiciones con el modificador OR.

A continuación de este comando se debe poner o bien otro RewriteCond (usando el modificador para unirlos) o bien un RewriteRule, que es el que se debe ejecutar en última instancia en caso de cumplirse la(s) condicion(es). En caso de no cumplirse la condición el RewriteRule sería ignorado.

#RewriteCond Cadena Patrón
#Si la dirección contiene "pepe" se ejecuta el RewriteRule que habría debajo
RewriteCond %{REQUEST_URI} pepe

RewriteRule

Posiblemente este sea el comando que más usaremos. Es el pilar para lo que queremos hacer, ya que de el depende que se lleve a cabo la reescritura.

Lo que hace es simple: le das un patrón y una URI de destino, si el patrón coincide se llama a la URI especificada con los parámetros que hayas indicado, etcétera. Las referencias hacia los valores agrupados con paréntesis en las expresiones regulares del patrón pueden ser referidas como $1, $2… hasta $9.

Aquí es donde las expresiones regulares juegan un papel importante, al ser con lo que haremos los patrones de comparación. Es importante conocer su sintaxis básica y poder manejarse con soltura para crear reglas efectivas.

Modificadores de RewriteRule

Esto son las flags de las que se habla en el manual oficial, a falta de una traducción mejor o más certera (banderas me parece cutre) he decidido referirme a ellas como modificadores. Los modificadores sirven para añadir características extra a ciertos comandos del mod_rewrite, como RewriteRule o RewriteCond.

Cada comando tiene sus propios modificadores. Se ponen entre corchetes, tras un espacio al final del comando y se separan (en caso de especificar más de uno) con comas y sin espacios.

Hay varios y no los explicaré todos, ya que para eso está el manual oficial, solo expondré los pertenecientes a RewriteRule que me parecen más útiles para lo que estamos haciendo.

Por encima, los modificadores que más nos interesan son los siguientes:

nocase (NC)

Este útil modificador hará que las expresiones regulares (o simplemente cadenas literales) que pongamos como patrón sean case-insensitive, es decir, que no se distinga entre mayúsculas y minúsculas. Esto nos puede venir bien muchas veces.

redirect (R[=codigo])

Permite redireccionar a una dirección con un código concreto de respuesta del protocolo HTTP. El rango, según el manual oficial, debe estar entre el cñodigo 300 (HTTP_MULTIPLE_CHOICES) y 400 (HTTP_BAD_REQUEST). Para conocer el significado de esas constantes y los códigos que puedes usar debes consultar este protocolo.

Suele interesar que, al redireccionar por este mñetodo, el archivo .htaccess no siga siendo interpretado, para lo que usaremos el modificador L.

Por defecto, si no se especifica un cñodigo, se pone automáticamente el 302 (MOVED TEMPORARILY).

last (L)

Este modificador hace que la condición, en caso de que se cumpla, sea la última en interpretarse del archivo. En caso de no cumplirse seguirá su curso normal. Es bueno especificarlo casi siempre, ya que le va a ahorrar a Apache la interpretación del resto de reglas.

Construcción de reglas o patrones

Puedes construir tantas reglas como quieras, pero ten en cuenta que, cuanto más pesado el archivo, más le costará de interpretar a Apache. No recomiendo que pases de los 2KB (que ya es mucho).

Se usan patrones basados en expresiones regulares (de tipo POSIX a partir de Apache 1.2.x) de manera que, si coinciden, se redirija al archivo que especifiquemos. Es realmente simple crear expresiones regulares para hacer las URIs como las de esta página. Dos buenos manuales para aprender a hacer expresiones regulares efectivas son los siguientes:

Es muy importante que delimitemos el rango de caracteres correcto acorde a nuestras necesidades. Si necesitamos obtener un número… ¿para que permitir letras u otros símbolos en el patrón? Esto nos ahorrará quebraderos de cabeza posteriores en lo referente a la seguridad de la aplicación final.

También suele ser necesario que, en las direcciones que puedan terminar con o sin barra, indiquemos esta eventualidad (es decir, que pongamos la barra del final como caracter opcional), algo como lo siguiente:

RewriteRule ^seccion/([0-9]+)/?$ index.php?seccion=$1

Si se diese el caso de que la barra opcional no está contemplada y es escrita en la dirección se lanzaría un error de tipo 404 (Página no encontrada).

Ejemplos de la vida real

Hasta ahora se ha intentado enseñar lo básico sobre esta extensión y como funciona. Ahora que comprendemos un poco como va podemos poner unos ejemplos ambientados en la vida real.

Lo que vamos a hacer en el siguiente ejemplo es obtener y enviar un identificador numérico a partir de una URL de tipo example.com/articulo/identificador_numérico, o sea, coger el número y pasárselo a una aplicación:

RewriteEngine On
RewriteRule ^articulo/([0-9]+)/?$ articulos.php?id=$1 [L]

Si quisiésemos obtener el nombre de la sección de ese artículo (a partir de una dirección tipo example.com/articulo/sección/identificador_numérico) sería muy sencillo también:

#La sección puede contener letras, guiones y guiones bajos
RewriteRule ^articulo/([a-z_-]+)/([0-9]+)/?$ articulos.php?seccion=$1&id=$2 [NC,L]

Ojo porque el identificador ya no está en $1, sino en $2, ya que es el segundo grupo de captura que hemos indicado. Al interesarnos tanto letras minúsculas como mayúsculas debemos poner el modificador NC.

Problema común y final del escrito

Suele pasar que el programador, al usar el mod_rewrite por primera vez, después de haber hecho funcionar sus primeros patrones, etc. diga… ¡No me funcionan los enlaces! ¡No me carga la hoja de estilos! Bien, esto es muy fácilmente solucionable y totalmente previsible. Solo debemos poner la dirección "base" de la página hacia el dominio principal y hacer las demás rutas relativas. Para esto tenemos el elemento <base /> de XHTML:

<head>
    <base href="http://www.example.com/" />
    …
</head>

Con esto se soluciona cualquier problema de que no cargue algo…

Espero que se haya entendido lo mejor posible todo lo explicado, si te ha quedado cualquier duda tienes varias opciones: