Introducción a la compresión de datos en la web
Optimiza el tamaño y velocidad de tu aplicación web gracias al uso correcto de la compresión de datos.
Introducción
La compresión web, esa pequeña e incomprendida funcionalidad que utilizamos constantemente para enviar y recibir datos de manera más rápida y generando un volumen de datos menor.
En este artículo veremos cómo funciona y las distintas opciones de las que disponemos.
Negociación
El cliente y el servidor acuerdan qué metodo de compresión utilizar mediante la negociación.
Accept-Encoding
Cuando el cliente solicita un recurso web al servidor, puede indicarle qué formatos de compresión desea utilizar. Esto lo hace a través de la cabecera Accept-Encoding
(RFC 7231, sección 5.3.4), indicando los algoritmos de compresión que acepta. Ejemplo:
En este caso el cliente indica al servidor que acepta los métodos de compresión gzip
y deflate
. El orden es importante ya que indica la preferencia del cliente (gzip
sería la primera opción a utilizar y deflate
la segunda).
Para controlar la preferencia también puede utilizarse el modificador ;q=
, indicando un valor cualitativo (q-value). Esto expresa la prioridad en unidades "weight" que van desde 0 hasta 1. Ejemplo:
En este caso, a pesar de que gzip
se encuentra segundo en la lista, sería el primero en cuanto a prioridad.
Content-Encoding
Tras la petición, llega la respuesta. En este caso el servidor es quién decide el método de compresión a utilizar. Realmente, el servidor puede hacer lo que le plazca, pero lo normal sería respetar los deseos del cliente y utilizar el algorimo de compresión con mayor prioridad especificado por el cliente. En el ejemplo anterior sería gzip
. Si gzip
no estuviera disponible se utilizaría deflate
, y así sucesivamente. Si ninguno estuviera disponible, el servidor no debería comprimir el contenido.
Aparte de comprimir el cuerpo de la respuesta, el servidor añade la cabecera Content-Encoding
(RFC 7231, sección 3.1.2.2) en la que especifica el algoritmo de compresión que finalmente se ha utilizado. Ejemplo:
No hay que confundir Content-Type
con Content-Encoding
. Aunque se comprima la respuesta, ésta sigue teniendo un formato, como por ejemplo: text/html
, video/mp4
o image/jpg
. Por lo tanto, no olvides especificar la cabecera Content-Type
para indicar el contenido del mensaje, esté comprimido o no.
zlib
Jean-loup Gailly y Mark Adler crearon en 1995 la librería de compresión zlib. Con el mismo nombre nació el formato zlib (especificado en el RFC 1950), que consiste en una cabecera (header), un cuerpo (body) comprimido mediante un algoritmo de compresión (generalmente deflate
, aunque se pueden implementar otros) y por último incluye una suma de verificación (checksum) en formato Adler-32.
Así pues, la librería zlib producía ficheros en formato zlib
.
Esta breve información nos será útil para entender los varios algoritmos de compresión.
Formatos de compresión
Formatos de compresión hay unos cuantos pero sin duda gzip
domina la web.
Los valores posibles en las cabeceras Accept-Encoding
y Content-Encoding
son: deflate
, gzip
, br
, identity
, compress
y *
.
Vamos a echarles un vistazo.
deflate
El caso de deflate
es curioso a la vez que confuso. Está definido como:
El formato "zlib" definido en RFC 1950 en combinación con el mecanismo de compresión "deflate" descrito en RFC 1951.
The "zlib" format defined in RFC 1950 in combination with the "deflate" compression mechanism described in RFC 1951.
Vamos por partes para intentar entenderlo mejor.
Resulta que según el RFC 2616, deflate
equivale al formato zlib
, comprimiendo el cuerpo utilizando el algortimo deflate
(es decir, ningún otro algoritmo puede ser utilizado).
¿Confuso verdad? Resulta que deflate
es tanto un formato como un algoritmo utilizado dentro de ese formato.
Esto llevó a la confusión también a los ingenieros de Microsoft que implementaron el formato, por lo que durante años los navegadores no sabían si deflate
se refería al formato o al algoritmo de compresión, por lo que funcionaban por descarte: si no era uno, era otro.
Esta ambigüedad a la hora de definir el formato zlib
/deflate
provocó que el formato gzip
se extendiera rápidamente y se acabase convirtiendo en el rey de la compresión web.
Para entenderlo mejor, veamos la implementación en Node.js de la librería zlib.
Resulta que tenemos estos dos métodos:
- zlib.Deflate: Compress data using deflate.
- zlib.DeflateRaw: Compress data using deflate, and do not append a zlib header.
Ya lo habrás adivinado, pero el método zlib.Deflate
se refiere al formato deflate
(incluyendo la cabecera y la suma de verificación), mientras que el método zlib.DeflateRaw
se refiere al algoritmo de compresión en sí, sin ninguna cabecera ni envoltorio de ningún tipo.
Cómo diferenciar Deflate y DeflateRaw
Primero, vamos a ver que ocurre cuando comprimimos la cadena foo
utilizando ambos métodos:
El resultado de deflate
como formato produce una cabecera con los bytes 78 9c
, un cuerpo que contiene los bytes 4b cb cf 07 00
(en común con deflate
como algoritmo) y una suma de verificación con los bytes 02 82 01 45
.
Es en los bytes de cabecera, más concretamente en el primer byte (78 en nuestro ejemplo) en donde está la distinción.
Un byte se compone de dos nibbles. Al ser 78
un valor hexadecimal, el 7
es un nibble y el 8
es el otro nibble. Siendo el primero el alto (más significante) y el segundo el bajo (menos significante), aunque este orden depende de la arquitectura/plataforma en la que te encuentres, pero vamos a obviar esto.
Si el nibble bajo (el segundo) es un 8
, entonces estamos ante el formato deflate
/zlib
, mientras que si no es un 8
, estaremos ante el algoritmo de compresión. Es una regla que siempre se cumple.
Por lo tanto, en nuestro ejemplo podemos ver claramente cual es un formato deflate
y cual es un algoritmo de compresión deflate
(conocido en la implementación de zlib de Node.js como deflateRaw
).
gzip
Basado en el algoritmo deflate
, gzip es un formato abierto que comprime el mensaje utilizando el algoritmo LZ77
y añade, al igual que el formato zlib
, una cabecera y una suma de verificación, aunque ésta en formato CRC-32.
La cabecera del formato gzip
contiene más bytes de los que contiene el formato zlib
(en total 12 bytes más). Además, la suma de verificación utilizando CRC-32 es más lenta de generar que mediante Adler-32, pero estas desventajas apenas son perceptibles con la potencia de los dispositivos de hoy en día. Ésto, sumado a que evitaremos la confusión del formato/algoritmo deflate
, ha convertido a gzip en la opción ideal para adoptar cuando hablamos de compresión web.
Brotli
Brotli es un formato de compresión creado por Google en 2015 para comprimir fuentes para la web en formato WOFF
, pero rápidamente encontró su sitio en el campo de la compresión web por sus ventajas. Utiliza un algoritmo de compresión propio, basado en LZ77
.
Su compatibilidad en navegadores es actualmente del 62%, más que suficiente teniendo en cuenta que si Brotli no estuviera disponible se utilizaría el siguiente de la lista.
Hoy en día su compatiblidad es prácticamente total, situándose en un 96.45%.
En las cabeceras HTTP Accept-Encoding
y Concent-Encoding
debe especificarse con el valor br
.
En cuanto a las ventajas frente a gzip
, promete en torno a un 20% extra de compresión siendo prácticamente igual de rápido en compresión/descompresión.
* (asterisco)
Cuando se utiliza el valor *
se está indicando al servidor que se acepta compresión de datos pero no se especifica ninguna preferencia en cuanto al formato.
Éste es el valor por defecto cuando omitimos la cabecera Accept-Encoding
.
identity
La función de identity
es la de indicar de manera explícita al servidor que no se desea ninguna compresión.
compress
Este formato basado en el algoritmo LZW cayó en desuso debido a problemas de patentes y apenas quedan navegadores que lo soporten.
Zstandard
Facebook posee un proyecto open source llamado Zstandard (zstd), que promete mejores resultados que Brotli, tanto en compresión como en velocidad. Desgraciadamente, por el momento no cuenta con soporte en ningún navegador así que el uso de este formato no es realmente válido en compresión web. Aún así, se merece una mención.
Comprimiendo lo comprimido
Hay formatos de archivo que pueden estar ya comprimidos como por ejemplo algunas imágenes PNG
y GIF
o las tipografías web en formato WOFF
.
En el caso de que estos datos ya estuvieran comprimidos, comprimirlos nuevamente sería un malgasto de recursos, ya que el tamaño final sería incluso superior (añadiendo una cabecera y una suma de verificación innecesarias) y además el servidor estaría empleando ciclos de CPU en comprimir algo que no va a aportar ningún beneficio.
Tampoco es favorable comprimir dos veces el mismo fichero. Si algo ya está comprimido, mejor dejarlo así.
Conclusión
Aplicar de manera inteligente la compresión de datos en nuestras aplicaciones web puede ahorrarnos mucha transferencia de datos mientras conseguimos un extra de velocidad para nuestros usuarios.
En cuanto a los formatos, lo óptimo sería hacer uso de nuevas opciones como Brotli para optimizar los recursos al máximo. Como la compresión web degrada fácilmente, no hay motivo para dejar de lado a los navegadores más antiguos, así que podemos utilizar gzip
como segunda opción y dejar que sea el cliente el que elija.
Puedes apoyarme para que pueda dedicar aún más tiempo a escribir artículos y tener recursos para crear nuevos proyectos. ¡Gracias!