Urls amigables o "seo friendly" con php y htaccess

Por todos es de sobras conocido que el posicionamiento de una web es muy importante en cualquier proyecto online. El tema es muy extenso y hay muchos parámetros que no están en nuestras manos para conseguir la mejor posición en los motores de búsqueda, como google u otros.. pero otros tantos que si lo están y sólo requieren que sepamos cómo aplicarlos a nuestra web.

En esta ocasión vamos a hablar de las famosas urls amigables para nuestra web, que viene a ser algo así como traducir las rutas de nuestras páginas dinámicas llenas de parametros por url con palabras que tienen relación con la página que se está visualizando. Bien usada, esta técnica puede ser determinante a la hora de posicionar nuestra página en google frente a otras con contenidos parecidos. Aunque en este articulo sólo hablaremos de la parte técnica, el tema de elegir las mejores palabras para nuestras url merecen un estudio aparte.

Para poder aplicar url amigables a nuestro proyecto haremos uso de php (aunque es extrapolable a cualquier lenguaje web) y del fichero .htaccess de nuestro servidor. Antes debemos tener claro si nuestro servicio de hosting tiene activado el mod rewrite, para ello podéis echar un vistazo a nuestro post como saber si mod_rewrite funciona en nuestro hosting

Una vez hemos comprobado ésto y el resultado es satisfactorio ya estamos listos para preparar nuestro módulo de interpretación de urls amigables en php para nuestra página web. Hay varios puntos a desarrollar, intentaré llevar cierto orden lógico.

Crear el fichero .htaccess

Lo primero que debemos hacer es crear nuestro fichero .htacess en la raiz del servidor que se encargará de gestionar las redirecciones. El código que debe contener es el siguiente :

RewriteEngine On
RewriteCond %{SCRIPT_FILENAME} !-d  
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteRule ^([A-Za-z0-9-]+)/?$ selector.php?seoparam=$1  [L]

Con RewriteEngine On activamos el mod_rewrite, las dos siguientes lineas ( RewriteCond %{SCRIPT_FILENAME} !-d y RewriteCond %{SCRIPT_FILENAME} !-f ) sirven para indicar al servidor que si la url que hemos indicado pertenece a una carpeta o a un fichero existente ejecute ese fichero o carpeta en vez de interpretar la url

Y por último la linea más interesante RewriteRule ^([A-Za-z0-9-]+)/?$ selector.php?seoparam=$1 [L] es donde indicamos al mod_rewrite que hacer cuando nos llegue una url amigable. En este caso le decimos que cualquier url la redirija hacia un fichero en el servidor llamado selector.php que será el encargado de redirigir hacia donde toca a los usuarios.

En el caso que vamos a hacer estamos suponiendo url amigables con un solo parámetro como por ejemplo "http://www.dominio.com/contenido-a-mostrar" si queremos implementarla con dos por ejemplo ésta última linea podría ampliarse así RewriteRule ^([A-Za-z0-9-]+)/([A-Za-z0-9-]+)/?$ selector.php?category=$1&seoparam=$2 [L] y podríamos tener url como "http://www.dominio.com/categoria-de-contenido/contenido-a-mostrar" . Nótese que el primer parámetro lo recogemos con $1 y el segundo con $2, podríamos interpretar tantos como quisieramos.

Otra cosa que debemos tener en cuenta es que estamos interpretando que nuestra url amigable solo contendrá letras, numeros y guiones medios (-) a través de la expresión regular ([A-Za-z0-9-]+)

Crear una tabla de urls en la base de datos

Dando por supuesto que tenemos una web dinámica (tal vez con nuestro propio gestor de contenidos) la mejor manera de vincular las urls a nuestros contenidos es creando una tabla en nuestra base de datos (en el ejemplo la llamaremos "urlSeo") con dos campos : url y pageId (por ejemplo..) donde url es la url amigable y pageId es el identificador del contenido en nuestra base de datos.

Si nuestra página es algo más simple o el contenido no es dinámico podemos prescindir de ésta tabla y en el siguiente paso en vez de buscar los contenidos que corresponden a la url por la base de datos lo podemos hacer a través de un array, o algo similar, que interprete a donde queremos llevar al usuario.

El archivo selector.php

En realidad se puede llamar como queramos, siempre que referenciemos a él desde nuestro htaccess. Nosotros lo hemos llamado así para que quede más claro el concepto. La finalidad de este archivo es encontrar que contenidos se corresponden con la url que estamos pidiendo y si no existen redirigir a un error 404.

El contenido de éste fichero podría ser algo parecido a esto :

 require 'conexion.php';
 $seoparam = limpiar($_GET["seoparam"]);

En primer lugar se encontraría nuestro fichero común con los datos de conexión a la base de datos (yo aprovecho para definir en él funciones que necesito en distintos lugares de la web) y en segundo lugar la linea donde recogemos el parametro que nos llega por $_GET de url amigable. La función "limpiar()" simboliza una función que podemos tener donde hagamos limpieza por seguridad de lo que recibimos por url antes de tratarlo. Hablaremos de ella en otro post más adelante.

Una vez hemos recogido el parametro que nos llega por url, lo hemos limpiado y tenemos conexion a la base de datos, llega el momento de encontrar que conenidos tenemos que mostrar. Para ello haremos una consulta a nuestra tabla "urlSeo" definida anteriormente en nuestra base de datos (si no hacemos uso de la base de datos, comparamos con un array o cualquier otro sistema de relación url -> fichero a ejecutar) :

 $query="SELECT * FROM urlseo WHERE url = '$seoparam'";
 $output=@mysql_query($query,$db_link);

 if(mysql_num_rows($output)>0) { // SI hay seccion con la urlSeo recibida

  ... procesamos la llamada a los contenidos ...

 } else { // NO hay seccion con la urlSeo recibida

  include 'notfound.php';

 }

En las dos primeras líneas ejecutamos nuestra llamada a la base de datos para ver si existe algún registro que coincida con la url recibida. Si la consulta a la base de datos devuelve resultados procesamos la petición, si no redirigimos al fichero notfound.php donde devolvemos un error 404 personalizado.

Para procesar la petición tenemos varias opciones, la más sencilla es crear una variable con el pageId resultante de la consulta e incluir un fichero que muestre uno u otro contenido en función de ese valor, algo como :

 $page = mysql_fetch_array($output);
 $pageId = $page['pageId'];
  include 'page.php'; // aquí mostramos los contenidos en función de la variable $pageId

El fichero notfound.php para los errores 404

Ya que procesamos todas las peticiones, es muy probable que nos encontremos con casos de peticiones erróneas. Aprovechando el sistema, podemos redirigir a los usuarios a una página con error 404 personalizado para así ayudarles a encontrar lo que buscan en nuestro sitio web.

El contenido del fichero de error 404 personalizado debe contener en las primeras lineas :

<?php 
 header("HTTP/1.0 404 Not Found");
 header("Status: 404 Not Found");
?>

Y a partir de aqui el contenido que creamos que es más útil para que el usuario sepa rápidamente encontrar las opciones que tiene para navegar por nuestros contenidos

Conclusión final

Si hemos seguido los pasos correctamente, tenemos funcionando nuestro sistema de url amigables para nuestra pagina web en php. Éste es el punto que corresponde a la interpretación de las url en nuestra web, quedaría por tanto programar un sistema para la grabación y relación de url con nuestros contenidos desde el gestor de contenidos, pero eso será en otro post.

8 comentarios:

  1. muy buen articulo, gracias por tu tiempo.

    ResponderEliminar
  2. Excelente artículo. Muy claro, por fin he conseguido hacerlo!!
    Cambia en el contenido del htaccess el nombre de la variabla "seoName" ya que luego en el resto del ejemplo usas "seoparam".

    Una pregunta que dejo, en mi caso estoy haciendo que se pueda acceder al contenido tanto desde la url original (y ya indexada en Google) de producto.php?id=XXX como con la modificacion de poder acceder con seat-panda-blanco.html En la práctica, es un contenido duplicado y podría perjudicarme de cara a Google, ¿qué podría hacer para evitar esto?

    Gracias y saludos.

    ResponderEliminar
    Respuestas
    1. Gracias por la anotación, primero empecé con seoName pero luego pensé que quedaria mas claro con seoparam. Ya está arreglado.

      Para evitar el caso que comentas debes hacer uso de redirecciones canónicas para que google entienda que las dos url llevan al mismo contenido pero realmente hay una "que manda". Googleando un poco seguro que encuentras más información al respecto, sobretodo en sitios relacionados con seo y posicionamiento.

      Un saludo!

      Eliminar
  3. Excelente, me ha sido muy útil. ¡Gracias por explicarlo tan sencillo!

    ResponderEliminar
  4. Hola,
    Por favor a ver si podeis ayudarme. Veo que todos lo habeis podido hacer bien, pero a mi no me sale, voy a intentar explicarme...

    Tengo una galería de proyectos creada en una unica página php (index.php). Cuando pinchas en algun proyecto paso tres variables en el enlace nombre_proyecto
    en la misma pagina recojo dichas variables y en funcion de las variables aparece un proyecto u otro. hasta aquí creo que me entendereis..
    Luego en esa carpeta he puesto un htacces con el siguiente codigo, en el cual me crea la url amigable proyecto-106. En tonces cuando pongo en la URL /proyecto-106/, me coge perfectamente ese proyecto, por lo que creo que la url amigable está funcionando. En el htacces lo que hago es que solo me conserve la variable de proyecto y le ponga un guión medio con el numero del proyecto. Todo lo demás no me interesa que se vea en la url amigable.
    RewriteEngine On
    RewriteRule ^(.*)-(.*)$ index.php?info=dis&$1=$2&tipo=Espacios
    HAsta aqui entiendo mas o menos como funciona.
    Mi duda es el proceso de como se transforma la url dinamica (que envío yo misma desde el enlace de mi galería) a url amigable. Es lo que se explica en este artículo, pero no logro conseguirlo. Explicaré punto por punto lo que he hecho:
    1- Lo primero que hice fue crear la base de datos tal y como se explicó aquí. Nombre tabla "urlseo", columnas "url" y "pageld"....en la columna url escribí "proyecto-106" y en la columna pageld escribi "index.php?info=XXX&proyecto=106&tipo=ZZZ"
    2- Después cambié el htacces y lo dejé así:
    RewriteEngine On
    RewriteCond %{SCRIPT_FILENAME} !-d
    RewriteCond %{SCRIPT_FILENAME} !-f
    RewriteRule ^([A-Za-z0-9-]+)/?$ selector.php?seoparam=$1 [L]
    3- Despues cree el archivo selector.php:
    0) { // SI hay seccion con la urlSeo recibida

    // ... procesamos la llamada a los contenidos ...

    } else { // NO hay seccion con la urlSeo recibida

    include 'notfound.php';
    }
    ?>
    Pero donde pone procesamos la llamada a los contenidos, no sé muy bien que tengo que poner

    No sé si me podeis orientar sobre como voy...Muchas gracias!!!

    ResponderEliminar
    Respuestas
    1. Hola, tal cual lo has planteado no has seguido del todo lo marcado en el post. El pageId deberia ser un identificador del contenido que quieres mostrar, de tal manera que una vez que has mirado en la base de datos con el "seoparam" recibido si existe el contenido, en la parte de "procesarlos" lo unico que deberias hacer es mostrar por pantalla los contenidos leidos de la base de datos (por ejemplo leidos de otra tabla con "pageId" y "content" como campos)

      Un saludo

      Eliminar
  5. realmente esto funciona para cualquier web en php? es decir, con estas instrucciones tambien hacemos url amigable en un foro phpbb o en una instalacion de joomla!?

    ResponderEliminar
    Respuestas
    1. Hola. Yo creo que se podria llegar a hacer, pero en el caso de sistemas modulares como los que mencionas estoy seguro que existen ya algunos pluguins para incorporar el tema de las url amigables y probablemente con mas opciones que lo expuesto en este post, que pretende ser una introducción o un ejemplo de funcionamiento, pero es mejorable y ampliable en muchos aspectos segun los requerimientos de cada web (por ejemplo con multiples parametros, multiples idiomas, etc..)

      Un saludo

      Eliminar