en Optimización

Usar ETags para mejorar la performance

Este es mi primer artículo en el blog de los que hacemos minube. Estaba dudando en escribir porque no sabía qué podía aportar que les pudiera servir. Y dándole vueltas a la cabeza me acordé de los protocolos.. y más concretamente del HTTP. Así que hoy les voy a contar un poco acerca de las ETags. Creo recordar que entre las recomendaciones de Google, el uso de ETags está bien visto, porque nos habla de un sitio optimizado. Pero vamos al tema…

ETag. ¿eso con qué se come?

Un ETag no es más que parte de la información que se transmite dentro de las cabeceras (If-None-Match) de las solicitudes de HTTP. Básicamente sirve para indicarle al servidor web qué versión de una página (documento, archivo, etc) tenemos almacenada localmente, para que éste determine si ha cambiado y nos mande nuevamente la página. Es decir. La primera vez que visitamos una página -y si tenemos activada la caché- nuestro navegador la almacena localmente junto a algunos de los recursos adjuntos en dicha página. Para simplificar el concepto voy a ejemplificar con una página como “un todo”, pero el concepto se aplica a cada request que hacemos. Cuando el servidor nos responde con la página nos envía una suma de comprobación o un código identificativo de la versión del documento en cuestión. De esta manera, la segunda vez que pidamos dicho documento, nuestro navegador (o User Agent en general), debería enviar dicho código dentro de las cabeceras. El servidor se encargará de determinar si el documento ha cambiado o si es el mismo. Si no ha cambiado nos devolverá un 304 (Not Modified), en caso contrario nos dará la versión más reciente del documento con su nuevo y respectivo código “de versión”, por llamarle de alguna manera.

Cuando solicitamos un documento por primera vez, el servidor nos devuelve una cabecera como esta:

ETag: "64233457696a7c876b7e"

Y cuando la solicitamos por segunda vez, nosotros enviamos esta cabecera:

If-None-Match: "64233457696a7c876b7e"

Como no ha cambiado la suma el servidor nos devuelve un 304.

¿Y eso cómo me beneficia?

Si aún no te estás frotando las manos deberías empezar a hacerlo. Básicamente porque la implementación de los ETags nos permite ahorrar ancho de banda, tiempos de generación de páginas, consultas, etc etc etc. Está claro que esto es hilar bastante fino, porque también podemos utilizar cachés desde el lado del servidor, pero no es exactamente lo mismo.

Supongamos que tenemos una web de recomendación de productos, donde además de la ficha del producto, tenemos los comentarios de los usuarios, fotos, hojas de estilo, etc. Primero que nada tenemos el tiempo de generación de la página y las consultas a la base de datos. Por más de que sean querys livianas, por más de que la página no tenga mucha lógica interna, sigue siendo un instante de carga para el servidor. ¡Y nos lo podemos ahorrar!. Además, también tenemos el ancho de banda de volver a enviar la página, las fotos, las hojas de estilo, los archivos de javascript, etc. Si fuéramos capaces de aplicar las ETags a todos estos documentos, nos encontraríamos con el siguiente panorama:

– La primer petición sería normal, con su tiempo de generación, con su gasto de CPU, memoria, ancho de banda, etc. O podemos enviar una página cacheada desde el servidor. Aún así tenemos gasto de ancho de banda.

– Pero si esta página, por algún motivo es muy visitada por el mismo usuario, a la segunda petición el servidor le enviaría cabeceras 304 y el navegador automáticamente nos mostraría la versión almacenada en local.

Imagínate las posibilidades!

¡Me encanta! pero, ¿cómo lo aplico a mi sitio?

Lo cierto es que para poder aplicar esto, nuestro servidor web debe aceptar el manejo de los ETags. Afortunadamente los servidores serios y robustos como puede ser Apache sí lo permiten y son muy fácilmente configurables.

Esto nos serviría si tenemos contenido estático. Pero si tenemos contenido generado dinámicamente, muchas veces el servidor no es muy eficiente a la hora de determinar los cambios, por lo que podemos hacerlo a mano. Les voy a poner un ejemplo en PHP:

– Supongamos que tenemos la página http://misitio.com/noticias/munix-gana-el-euromillones-1090.html y que esta está generada dinámicamente con PHP y consultas MySQL. Esta página acepta comentarios de los usuarios y, por tanto, es susceptible de permanecer sin cambios, o tenerlos al recibir un comentario. Este ejemplo nos vale para aplicar los ETags. Para esto, añadimos el siguiente header:

$etag = md5 ($_SERVER[‘REQUEST_URI’] . $last_modification); //Donde $last_modification es la fecha del último comentario, por ejemplo. Esto nos permite controlar la temporalidad.

if (trim($_SERVER[‘HTTP_IF_NONE_MATCH’]) == $etag) {

header(“HTTP/1.1 304 Not Modified”);

exit;

}else{

header(“Etag: $etag”);

}

Con este sencillo código lo que hacemos es enviar la cabecera del ETag la primera vez o cuando no haya coincidencia, y a las siguientes que no haya cambiado el contenido mandamos el 304.

Está claro que esta solución no es válida para todos los proyectos, pero seguramente te dará una noción de los manejes internos dentro del protocolo HTTP

Escribe un comentario

Comentario

  1. Hombre, si te fijas:

    $etag = md5 ($_SERVER[‘REQUEST_URI’] . $last_modification);

    Al final lo que hacemos es un MD5 de todo. El tema es tener ALGO que te permita hacer invariable ese hash md5 mientras no cambie el contenido.

    Supongamos que lo aplicamos a los posts de un wordpress, podríamos usar para el last_modification la fecha y hora del último comentario. Porque sabríamos que hasta que nadie vuelva a comentar, la fecha del último comentario va a permanecer invariable. Y apenas cambie, cambiará el hash md5 y por tanto invalidará la comprobación 🙂

    Espero haberte ayudado

  2. Mira he probado el código y jamás se cumple la condición:

    if (trim($_SERVER[‘HTTP_IF_NONE_MATCH’]) == $etag)

    Pero si se “modifica” para que ingrese, o se envía:

    header(“HTTP/1.1 304 Not Modified”);

    Lo que hace es forzar una descarga de un pequeño archivo .gz

    Me puedes indicar que estoy haciendo mal?

    Gracias

  3. Gabriel:
    Primero que nada, es importante que la primera vez no se cumpla la condición, así el servidor web envía la cabecera con el Etag header(“Etag: $etag”);
    donde la variable $etag como vimos antes, es una referencia única que sólo varía cuando varía el contenido.
    Una vez que se ha mandado esta cabecera la primera vez, el navegador se la va a devolver siempre al servidor web y la recibirás mediante $_SERVER[‘HTTP_IF_NONE_MATCH’] (o también puedes usar la función de php apache_request_headers();
    Ejemplo:

    $headers = apache_request_headers();
    echo $headers[‘If-None-Match’];
    tanto $_SERVER como, en este caso, $headers, tendrán algo siempre y cuando se haya enviado una vez el header(‘Etag: TU_TAG’);
    En ese caso ya se va a cumplir la condición

    http://www.webpagescreenshot.info/img/984164-482011100133am.png

  4. Despues de mandar el estado 304, ¿se sale del script o se envia la pagina de todas formas?

  5. Sergio: Si miras el código del post, luego de mandar la cabecera con el 304 hace inmediatamente después un “exit”, que detiene la ejecución, por lo que la página no se manda!

  6. Gracias por compartir el artículo! Conocía la existencia de las Etags pero nunca las había implementado. Tu breve explicación ha sido muy clara y ya las tengo en la mitad de los módulos de mi aplicación! 🙂