DEV Community

Manuel Canga
Manuel Canga

Posted on • Updated on

Optimización web con ETags. Ejemplo con WordPress

Optimización web con ETags. Ejemplo con WordPress

Este post ya fue publicado inicialmente en 2019 en Optimización web con ETags. Ejemplo con WordPress

Contenido cacheado

Hace tiempo que no escribo sobre optimización. Ya sabéis lo que me conocéis a qué fue debido. Sin embargo, no puedo dejar que vende humos de supuesto WPO me quiten de escribir sobre algo que me gusta. Así que, aquí tenéis un nuevo post.

Seguro que te ha pasado. Llegas un día a tu lugar de trabajo, enciendes tu equipo, abres tu correo y después de un vistazo a este, abres un terminal y escribes: git pull. La respuesta del terminal no se deja esperar: Already up-to-date..

¿Has pensado alguna vez qué ocurre detrás de ese git pull? Yo sí. Conjeturando, yo diría que al hacer un git pull estás mandando al servidor, de manera transparente, la fecha del último cambio que tú tienes. El repositorio comprueba la fecha del último cambio que tú le envías contra la fecha del último cambio que él tiene, de manera que:

  • Si tu fecha es más antigua, te manda todos los push/cambios que se han realizado desde entonces. También te mandará, junto a esos cambios, en la fecha que se hicieron. Así, si escribieras otra vez git pull mandarías la fecha del último de esos cambios y volvería todo a empezar.
  • Si tu fecha corresponde con la fecha que tiene el repositorio para el último cambio, te devolverá que tienes todo al día.

Este procedimiento, que para mí era el más lógico, no es el real. El real es parecido, pero no exacto. Cada vez que se hace un push. El repositorio asocia un token(código alfanumérico identificativo, algo como ae3d9735f280381d0d97d3cdb26066eb16f765a5) al último commit. Cuando haces un git pull se compara el último token que tú tienes con la lista de token que él tiene. Si tu token es uno antiguo, te manda los cambios desde entonces con sus correspondientes tokens. Si el token era el último, te dirá que estás al día.

Llegado a este punto, tú me dirás: Manuel, ¿pero este post no iba de optimizar webs con WordPress? Ciertamente, y sigue siendo. Tanto el primer caso presentado(el de la fecha), como el segundo(el del token) son formas de trabajar del protocolo HTTP. Veámoslo.

Last-Modified

Imagínate que tu navegador manda una petición a mi servidor para descargar el favicon de mi web. En la respuesta de mi servidor a tu navegador irá la cadena(o cabecera HTTP ): Last-Modified: Thu, 29 Dec 2016 11:55:29 GMT. Con ella, mi servidor está informando a tu navegador de cuando se modificó el favicon por última vez. Así que el navegador, una vez descargada la imagen, la guardará en su caché con el metadado “Last-Modified” y valor Thu, 29 Dec 2016 11:55:29 GMT

Si pasados unos segundos, unos días o unos meses, te decides a entrar otra vez en mi web, tu navegador necesitará nuevamente el favicon de mi sitio. Sin embargo, recuerda que también tiene una copia de la imagen en su caché. ¿Cómo sabe si el favicon que tiene en caché es el último o debe bajárselo de nuevo? Simple, haciendo un “git pull”. Es decir, el navegador vuelve a mandar una petición de favicon a mi servidor, pero informando que tiene una versión de la imagen de una determinada fecha. Hay dos respuestas posibles de mi servidor:

  • El favicon que se utiliza ahora en mi web es más nueva, por lo que mi servidor mandará la nueva imagen a tu navegador, junto con la nueva fecha de última modificación que tiene esta nueva imagen.
  • El favicon que se utiliza ahora en mi web es igual a la fecha que le indica tu navegador. Es decir, que tanto la imagen del servidor como la imagen de caché del navegador son iguales. Entonces, mi servidor le indica a tu navegador que la imagen no se ha modificado (con el código HTTP 304 Not Modified). Tu navegador entonces usa la imagen de la caché y se ahorra de tener que bajar de nuevo la imagen (ahorrando así muchos bytes de tu tarifa de datos).

ETags

Si recuerdas, al principio del post, te contaba que Git trabajaba con tokens para determinar cuando se realizó cambios. HTTP, además de la fecha de última modificación, permite trabajar con unos tokens llamados ETags (De Entity Tags). Un ETag es un código alfanumérico (como podría ser 5864f9b1-47e) sin formato predeterminado(es decir, el estándar HTTP no marca, o casi no marca, qué formato debe tener el token). Es el dueño de un sitio el que determina cuál será su formato.

Por defecto, los servidores webs como Apache crean el ETag de cada archivo basado en su fecha de modificación (y algunas veces también el tamaño del archivo). Esto es redundante (la cabecera HTTP de fecha de última modificación se basa en el mismo criterio) y poco óptimo (porque se añade más información a las peticiones que no sirve de nada). Lo recomendable en ese caso es configurar tu servidor web para que no use ETags con archivos. Por ejemplo, para desactivar los ETags de archivos (o FileETags) para Apache, añade el siguiente código en tú.htacess: FileETag None

Seguro que te estarás preguntado que si el diálogo entre navegador/servidor usando un ETag es el mismo que hemos visto para la fecha de última modificación y usar ambas formas es poco óptima y redundante. ¿Para qué usar ETags?

La fecha de modificación es suficiente para peticiones HTTP a archivos, pero con peticiones HTTP a páginas webs(HTML) se queda corta. Una página web depende de muchos factores/elementos interrelacionados(contenido, comentarios, estructura HTML, … ) y no de un único archivo. Por tanto, sería muy complicado encontrar una fecha de última modificación unificada para todos esos elementos. Se que lo digo es complicado de seguir, trataré de explicarlo de otra manera:

Imagínate que asigno como fecha de modificación de la página web( HTTML ) de esta entrada, la fecha de modificación del texto de la entrada. Tu navegador al entrar cachearía esta página junto a la fecha de última modificación del post. Si vuelves a entrar un minuto más tarde, como el post no ha cambiado( y, por tanto, su fecha de modificación ), tu navegador volverá a usar la versión que tiene en caché. Si alguien me escribiera un comentario y tu volvieras a entrar, tu no verías el comentario. Pues el texto del post no ha cambiado, por tanto, la fecha de última modificación tampoco, así que tu navegador volvería a mostrarte la versión de su caché. Lo mismo pasaría si cambio el HTML y añado un nuevo CSS. El contenido del post no ha cambiado,la fecha tampoco, y tu navegador seguiría mostrando la versión del cache.

Si en vez de trabajar con fechas de última modificación del post, asignamos a la página web del post un ETag con el siguiente formato: {fecha_modificacion_contenido_post}_{fecha_ultimo_comentario_post}_{numero_version_del_tema_WP}

Al entrar tu navegador por primera vez en el post, guardará en caché la página web(HTML) con su ETag asociado como metadato. Si cambiara alguno de los criterios del token(la fecha de modificación del post, la fecha del último comentario o la versión del tema actual de WP ), el ETag asociado a la página web sería diferente. Por lo que si entraras de nuevo al post, mi servidor notificará que el ETag de tu navegador no es el último y le volverá a mandar toda la página web, junto con el nuevo ETag.

Si nada hubiera cambiado, el token/ETag sería el mismo( tanto en navegador como en servidor ), por lo que al visitar la página con tu navegador, mi servidor le mandaría un 304 avisándole que nada ha cambiado( en términos WPO se dice que aún está fresca ) y que, por tanto, use la versión de su caché.

Beneficio de Etags

Algo que no he comentado hasta ahora son los beneficios de ETags. Aquí algunos de ellos:

  • Menos datos transferido entre servidor/navegador. Eso significa un ahorro de datos en el usuario para que tu web no le salga tan cara a tus usuarios y también en el servidor (importante si tienes contratado el alojamiento basado en pago por cantidad de datos transferido).
  • El servidor se ahorra el tener que generar el HTML, con todo lo que eso implica: ahorro de memoria y CPU y liberar a la base de datos de trabajo.
  • Una carga mucho más rápida de tu sitio web, mejorando con ello la experiencia del usuario.

Plugin WordPress

Todo lo que hemos visto es a alto nivel, vamos a ver un pequeño plugin que use ETag para páginas/posts de WordPress.

# etags.php
<?php

namespace trasweb\webperf\ETags;

/*
 * Plugin Name:       ETags en posts
 * Plugin URI:        https://trasweb.net/webperf/optimizacion-web-con-etags
 * Description:       Usa el cache en navegador para tus posts.
 * Version:           0.0.1
 * Author:            Manuel Canga / Trasweb
 * Author URI:        https://trasweb.net
 * License:           GPL
 */

add_action('wp', function () {
    if (is_admin() || ! is_singular()) {
        return;
    }

    $etag_from_navigator = $_SERVER[ 'HTTP_IF_NONE_MATCH' ]??'';
    $current_ETag        = get_current_ETag();

    if ($etag_from_navigator === $current_ETag) {
        status_header(304);
        exit;
    }

    header('ETag: ' . $current_ETag);
});

function get_current_ETag()
{
    $last_modified_time_of_content = (int)get_post_time();
    $date_of_last_comment          = get_date_of_last_comment();
    $theme_version                 = wp_get_theme()[ "Version" ]??'0.0.0';

    return md5("{$last_modified_time_of_content}_{$date_of_last_comment}_{$theme_version}");
}

function get_date_of_last_comment()
{
    $query = [
        'post_id' => get_the_ID() ?: 0,
        'orderby' => ['comment_date_gmt'],
        'status'  => 'approve',
        'order'   => 'DESC',
        'number'  => 1,
    ];

    $last_comment = get_comments($query)[ 0 ]??null;

    return $last_comment->comment_date_gmt??0;
}
Enter fullscreen mode Exit fullscreen mode

Antes que nada decir que este plugin es sólo formativo. Como con cualquier técnica de optimización web, como minificación/unificación de recursos CSS/JS o el uso de caché en servidor, se requiere primero un estudio del sitio.

Como se puede ver más sencillo no puede ser. Primero se obtiene el ETag del navegador si es que lo hubiera( línea 20 ). En segundo lugar se obtiene el Etag asociado al post/page actual( línea 21 ).

Si ambos son iguales se manda al navegador un código 304( línea 24, este es el caso que muestra la imagen principal de este post ) y se acaba la ejecución. Al navegador le llegará el código 304 y sabrá que tiene que hacer uso de la versión de la página de su caché.

Si los Etags son diferentes( bien porque entra el navegador por primera vez o bien porque el token ha cambiado ), se manda el ETag al navegador y se deja que WP siga su curso( que manda el contenido del post/page actual ).

El Etag se genera en la funcion get_current_ETag( línea 31 a 38 ) basado en la última vez que se modificó el post/page, la fecha del último comentario del post y la versión del tema actual. Si algunos de esos parámetros cambia, cambiara el token, forzando al navegador a bajar la nueva versión de la web.

Esto es todo. Espero que os haya gustado y os sirva para hacer más rápida vuestra página web.


Porfa, compártelo

Top comments (0)