Diccionarios compartidos y compresión delta
Cómo usar una versión anterior de un recurso como diccionario para comprimir la siguiente, con ratios que llegan al 98%, y cómo se negocia todo eso por HTTP.
Si el usuario ya tiene en su caché la versión anterior de tu bundle, y la versión nueva se parece muchísimo a ella, transferirla entera es un desperdicio enorme. La compresión con diccionarios compartidos permite comprimir la versión nueva usando la anterior como referencia, y los ratios que se alcanzan no son incrementales: se ha documentado pasar de 53 kilobytes a poco más de 4 en un caso real.
- Explicar qué es un diccionario de compresión y por qué multiplica el ratio.
- Describir la negociación completa por HTTP con sus cabeceras.
- Configurar el flujo para recursos estáticos versionados.
- Evaluar cuándo compensa y qué precauciones exige.
Qué es un diccionario de compresión
Un compresor sustituye repeticiones por referencias a apariciones anteriores dentro del mismo archivo. Un diccionario amplía esa idea: es un contenido externo, conocido por el compresor y por el descompresor, contra el que también se pueden hacer referencias.
Brotli ya usa esta idea con su diccionario estático incorporado, orientado a texto web genérico. Lo nuevo es poder aportar un diccionario propio y específico.
El caso que hace obvia la utilidad es la compresión delta: usar la versión anterior de un recurso como diccionario para comprimir la nueva. Si tu bundle cambia poco entre despliegues, casi todo el contenido nuevo ya está en el diccionario y se referencia en lugar de codificarse.
El ejemplo publicado por el equipo de Chrome es contundente. Una versión de una biblioteca de unos 172 KiB sin comprimir queda en unos 53 KiB con Brotli en su configuración por defecto, un ratio cercano al 70%. Comprimiendo una versión posterior de esa misma biblioteca usando la anterior como diccionario, la salida baja a algo más de 4 KiB: un ratio cercano al 98%.
El requisito es evidente y es también el límite: el descompresor necesita exactamente el mismo diccionario. En la web eso significa que el usuario tiene que tener la versión anterior en su caché, es decir, tiene que haber visitado el sitio antes de ese despliegue.
La negociación por HTTP
El mecanismo tiene tres piezas y está estandarizado.
Uno: declarar que un recurso sirve como diccionario. El servidor añade a la respuesta una cabecera que indica a qué recursos futuros aplica, usando la sintaxis de patrones de URL:
HTTP/1.1 200 OK
Content-Type: text/css
Use-As-Dictionary: match="/dist/estilos.*.css"
El navegador guarda ese recurso y lo asocia al patrón. La cabecera aplica a versiones futuras del recurso que encajen en el patrón.
Dos: anunciar el diccionario disponible. Cuando el navegador va a pedir un recurso que encaja con un patrón para el que tiene diccionario, añade dos cosas a la petición: unos identificadores adicionales de codificación aceptada, y el hash del diccionario que tiene.
GET /dist/estilos.v2.css HTTP/1.1
Accept-Encoding: gzip, br, zstd, dcb, dcz
Available-Dictionary: :pZGm1Av0IEBKARczz7exkNYsZb8LzaMrV7J32a2fFG4=:
Los dos identificadores nuevos son dcb para Brotli con diccionario y dcz para Zstandard con diccionario. El valor del hash va codificado como secuencia de bytes de campo estructurado, de ahí los dos puntos que lo delimitan.
Un detalle importante: el navegador solo envía esa cabecera si tiene el diccionario en su caché. Si el usuario nunca visitó el sitio cuando estaba desplegada la versión anterior, no la envía y el flujo cae al camino normal sin que nada se rompa.
Tres: el servidor comprime con ese diccionario. Comprueba el hash recibido, localiza el diccionario correspondiente, comprime la respuesta contra él y responde declarando la codificación usada:
HTTP/1.1 200 OK
Content-Type: text/css
Content-Encoding: dcb
Vary: Accept-Encoding, Available-Dictionary
La cabecera Vary tiene que incluir ahora también el diccionario disponible, porque respuestas comprimidas contra diccionarios distintos no son intercambiables.
El flujo asume recursos del mismo origen. Para recursos de otro origen, se aplican las reglas habituales de compartición entre orígenes y hace falta la cabecera correspondiente. Y hay una precaución específica del caso dinámico: si usas un diccionario para comprimir respuestas que pueden contener información personal, ese diccionario no debe contener información personal de nadie, porque es un artefacto compartido entre usuarios.
El flujo para recursos dinámicos
Además del caso de versión anterior, se puede cargar un diccionario a propósito, pensado para comprimir contenido que cambia. El caso típico es un documento HTML: la estructura, la navegación y las plantillas son casi idénticas entre páginas, y solo cambia el contenido.
Se declara con un elemento de enlace en la propia página:
<link rel="compression-dictionary" href="/diccionario.dat">
El navegador puede descargarlo cuando la página esté ociosa, con prioridad baja para no competir por ancho de banda. La respuesta del propio diccionario tiene que declarar a qué rutas aplica:
HTTP/1.1 200 OK
Use-As-Dictionary: match="/producto/*"
A partir de ahí el flujo es idéntico al de los recursos estáticos.
Este caso es especulativo por naturaleza: estás gastando ancho de banda en descargar un diccionario con la esperanza de amortizarlo en navegaciones posteriores. Solo compensa si tu sitio recibe suficiente tráfico por usuario para que ese coste se reparta entre muchas navegaciones.
Cuándo compensa
Un balance honesto, porque esta técnica tiene un ámbito de aplicación estrecho y muy rentable dentro de él.
Compensa claramente cuando se dan tres condiciones a la vez: despliegas código nuevo con frecuencia, tienes una proporción alta de visitantes recurrentes, y tus recursos cambian poco entre versiones. Es el perfil de una aplicación web con usuarios diarios y despliegue continuo.
No compensa si despliegas cada varios meses, porque entre despliegues la caché del usuario habrá expirado o se habrá desalojado; si tu tráfico es mayoritariamente de visitantes nuevos, porque no tendrán diccionario; o si tus recursos cambian radicalmente entre versiones, porque el diccionario no se parecerá al contenido nuevo.
Hay una consideración de planificación que la documentación señala explícitamente: el diccionario disponible para una versión concreta de un recurso puede ser cualquiera de las versiones anteriores, no necesariamente la inmediatamente previa. Cada usuario tendrá la que tuviera cacheada. Eso significa que el servidor tiene que ser capaz de comprimir contra varias versiones distintas, y que conviene analizar el patrón de visitas para decidir cuántas versiones anteriores mantener disponibles como diccionario.
El estado y las precauciones
Un resumen del estado a agosto de 2026, con lo que hay que verificar antes de desplegarlo.
La técnica está estandarizada y disponible en Chromium. La sintaxis definitiva difiere de la de los primeros anuncios: los identificadores pasaron de br-d y zstd-d a dcb y dcz, y el valor del atributo de relación del elemento de enlace pasó de dictionary a compression-dictionary. Si encuentras documentación con la sintaxis antigua, está desactualizada.
Del lado del servidor hace falta soporte explícito, porque hay que almacenar los diccionarios indexados por hash y comprimir contra ellos en el momento. Los proveedores de CDN han ido incorporándolo, y conviene comprobar el estado concreto del tuyo antes de planificar.
Las precauciones antes de activarlo:
- Comprueba que el respaldo funciona. Un cliente sin soporte, o sin diccionario en caché, debe recibir la respuesta comprimida de forma normal. Prueba explícitamente ese camino.
- Vigila
Vary. Si tu CDN no incluye el diccionario disponible en la clave de caché, puede servir una respuesta comprimida contra un diccionario a un cliente que tiene otro, y el resultado son bytes ilegibles. - Ningún dato personal en un diccionario. Es un artefacto compartido.
- Mide el coste del diccionario en el caso dinámico. Si lo descargas especulativamente y tus usuarios hacen una sola navegación, has añadido peso a cambio de nada.
Hasta ahora había una tensión irreducible en la estrategia de recursos estáticos. Si pones todo tu código en un archivo grande, cacheas muy bien pero cualquier cambio invalida los trescientos kilobytes enteros. Si lo divides en muchos archivos, un cambio invalida solo uno, pero pagas cadenas de dependencias, más cabeceras y peor compresión, porque cada archivo comprime peor por separado que todos juntos. Todo el arte del empaquetado consistía en encontrar el punto medio de ese compromiso, y ese punto medio depende de la frecuencia de cambio de cada parte, que nadie sabe de antemano. La compresión delta disuelve la tensión: puedes volver a poner todo en un archivo grande, que comprime óptimamente y cachea óptimamente, porque cuando cambias una línea el usuario recurrente descarga cuatro kilobytes en lugar de trescientos. La granularidad de la invalidación deja de estar atada a la granularidad de los archivos. Si esto se generaliza, buena parte de la sofisticación actual de la división de bundles se volverá innecesaria, y eso es un cambio de arquitectura mucho más profundo que un porcentaje de ahorro.