Compresión con Diccionario Compartido en 2026: Delta Compression con Brotli (dcb), Zstandard (dcz) y Use-As-Dictionary
Aprende a implementar compresión con diccionario compartido (SDC) en 2026: dcb (Brotli), dcz (Zstandard), Use-As-Dictionary y passthrough en Cloudflare, con ejemplos de código y métricas RUM.
La compresión con diccionario compartido (Shared Dictionary Compression, SDC) es una técnica HTTP, estandarizada en el RFC 9842 Compression Dictionary Transport, que permite al servidor comprimir una respuesta usando como diccionario una versión anterior del mismo recurso que el navegador ya tiene en caché, enviando solo el delta binario. En pruebas internas de Cloudflare, un bundle de JavaScript de 272 KB pasó de 92,1 KB (gzip) a 2,6 KB con DCZ, un 97% menos. Disponible en Chromium 130+ y con passthrough beta en Cloudflare desde abril de 2026.
SDC extiende Brotli (codificación dcb) y Zstandard (codificación dcz); no funciona con gzip.
El origen marca la respuesta base con Use-As-Dictionary; el navegador envía Available-Dictionary: :<sha256>: en la siguiente petición.
El passthrough de Cloudflare (beta abierta desde el 30 de abril de 2026) evita reescribir cabeceras y sirve las respuestas dcb/dcz sin recomprimir.
Requisitos estrictos: HTTPS, mismo origen para el diccionario y respuesta, y CORS con preflight si cruzas orígenes.
Casos de uso ideales: bundles JS con hash de contenido, CSS crítico y respuestas JSON estructuralmente similares en despliegues frecuentes.
Chrome 130+, Edge 130+ y otros Chromium ya lo soportan; Firefox está en desarrollo.
¿Qué es la compresión con diccionario compartido?
La compresión HTTP tradicional es stateless: cada respuesta se comprime como si el cliente nunca hubiera visto nada. Cuando despliegas app.a1b2c3.js y luego app.d4e5f6.js, el compresor no sabe que existe la versión anterior aunque el 98% de los bytes coincidan. La compresión con diccionario compartido cambia esto: le da memoria al compresor. El navegador retiene una respuesta previa como diccionario, envía su hash SHA-256 en la siguiente petición, y el servidor devuelve únicamente el delta comprimido contra ese diccionario.
Honestamente, migrando pipelines de despliegue continuo llegué a la conclusión de que el problema no es el tamaño del bundle inicial (eso se resuelve con code splitting agresivo), sino el coste de actualización. Cada minor rev que rompe el hash fuerza al navegador a redescargar cientos de KB por un cambio de una línea. SDC ataca ese coste directamente: los usuarios recurrentes pagan solo el diff, mientras los nuevos siguen recibiendo el archivo completo comprimido con Brotli o Zstandard. Es exactamente el patrón stale-while-revalidate aplicado a bytes en lugar de a frescura de caché.
El estándar admite dos casos: diccionario delta (versión N sirve como diccionario para N+1 del mismo archivo) y diccionario compartido entre recursos (un diccionario común para todas las páginas de producto de un e-commerce, por ejemplo). El primero es trivial de configurar; el segundo requiere generar el diccionario offline con brotli --large_window o zstd --train.
dcb vs dcz: Brotli o Zstandard con diccionario
Los tokens br-d y zstd-d del borrador original fueron renombrados a dcb y dcz antes de la publicación del RFC. Un navegador compatible añade ambos al Accept-Encoding solo cuando ya tiene un diccionario disponible en caché para el recurso solicitado. Sin Available-Dictionary, se cae al comportamiento normal (br, zstd, gzip).
Característica
dcb (Brotli con diccionario)
dcz (Zstandard con diccionario)
Versión mínima librería
Brotli 1.1.0
Zstandard 1.0+
Diccionario estático incorporado
Sí (~120 KB de web common)
No
Ventana de compresión
Hasta 16 MB
Configurable, típico 8 MB
Velocidad de decompresión
Media (~350 MB/s)
Alta (~1,5 GB/s)
Ratio en JS/HTML
Muy alto
Alto
Content-Encoding token
dcb
dcz
Cabecera del stream
SHA-256 + magic bytes Brotli
Skippable frame Zstd + SHA-256
Mi regla práctica: si sirves assets estáticos precomprimidos (bundles con hash, CSS crítico), usa dcb por su ratio ligeramente mejor en texto estructurado. Si comprimes respuestas dinámicas (JSON de APIs, HTML server-rendered, respuestas de agentes LLM), usa dcz: la velocidad de decompresión importa más que un 3-5% adicional de ratio, sobre todo si terminas hidratando en un dispositivo de gama baja. La guía oficial de Chrome sobre diccionarios compartidos incluye benchmarks que confirman este trade-off en corpus reales.
Cómo funciona Use-As-Dictionary paso a paso
El protocolo negocia el diccionario en tres viajes, sin intervención de JavaScript. Todo ocurre en la capa HTTP y es transparente para tu aplicación.
Paso 1: primer request, marcado del diccionario
El navegador pide /static/app.a1b2c3.js por primera vez. El origen responde con Brotli o Zstd tradicional, pero añade la cabecera Use-As-Dictionary:
La directiva match es un patrón URLPattern que define qué peticiones futuras podrán usar este recurso como diccionario. ttl limita cuánto tiempo el navegador retiene el diccionario en su almacén dedicado (separado del HTTP cache normal).
Paso 2: navegador ofrece el diccionario
Tras un despliegue, el navegador pide /static/app.d4e5f6.js. Como matchea el patrón anterior y aún tiene el diccionario válido, añade dos cabeceras:
El valor de Available-Dictionary es el hash SHA-256 del diccionario codificado en Base64 dentro de la sintaxis de Structured Fields (los dos puntos indican Byte Sequence).
Paso 3: servidor responde con delta
El origen (o el CDN) verifica el hash, comprime la nueva versión contra el diccionario y responde:
El cliente reconstruye la respuesta completa combinando el diccionario cacheado con el delta. Si en cualquier punto el hash no coincide o el diccionario expiró, se degrada limpiamente al comportamiento tradicional: el servidor ignora Available-Dictionary y devuelve Content-Encoding: br.
Configuración en el servidor de origen
La compresión con diccionario requiere una librería que sepa aceptar un diccionario externo en el momento del compress(). Ni nginx ni Apache lo soportan out-of-the-box en agosto de 2026, así que necesitas un middleware en tu aplicación o un edge worker delante. Este ejemplo usa Node.js 22 con el binding oficial @shopify/brotli-wasm (que expone la API dictionary de Brotli 1.1):
prependDcbHeader añade los 40 bytes de cabecera exigidos por el RFC: 0xFF 0x44 0x43 0x42 (magic ÿDCB) seguidos del hash SHA-256 crudo. Para Zstandard el marco skippable se genera con 0x184D2A5E como magic number y el mismo hash.
Integración con Cloudflare (passthrough beta)
El 30 de abril de 2026 Cloudflare abrió el passthrough beta de shared dictionaries en todos los planes. Passthrough significa que el edge no comprime con diccionario por ti; simplemente forwardea las cabeceras críticas sin stripearlas y ajusta la cache key para variar por Available-Dictionary. Toda la lógica de compresión sigue viviendo en tu origen.
Para activarlo, en el dashboard: Speed > Optimization > Content Optimization > Shared Dictionaries > Enable. O vía API con wrangler:
Cloudflare extiende automáticamente la clave de caché para incluir Available-Dictionary y Accept-Encoding, lo que evita el problema clásico de servir una respuesta comprimida con el diccionario incorrecto a un cliente que no lo tiene. Los headers Use-As-Dictionary, Available-Dictionary y los content-encodings dcb/dcz pasan sin modificación desde tu origen.
La fase 2 (Cloudflare comprimiendo con diccionarios en el edge) está anunciada pero sin fecha. Mientras tanto, si tienes un backend Node/Go/Rust, el patrón que uso es: origen en Fly.io o Railway aplicando la compresión, Cloudflare delante como caché con passthrough activado. Los ahorros de bandwidth se reflejan en la factura de egress dentro de la primera semana.
Requisitos de seguridad y limitaciones
SDC amplifica la superficie de ataques de compresión histórica (CRIME, BREACH) porque el diccionario contiene bytes controlados por el atacante potencial si mezclas contenido user-generated con secretos. El RFC 9842 impone restricciones estrictas para mitigarlo:
HTTPS obligatorio: no hay excepción, ni para desarrollo local sobre HTTP. Chrome bloquea la negociación en http://.
Mismo origen por defecto: el diccionario y la respuesta comprimida deben compartir origen. Si el diccionario vive en un CDN de otro dominio, necesitas CORS con preflight OPTIONS y Access-Control-Allow-Headers: available-dictionary, dictionary-id, sec-fetch-dest.
No mezclar secretos y user input en el mismo diccionario. Si comprimes cookies de sesión contra un diccionario que contiene HTML del atacante, expones el side channel BREACH.
Fallback siempre disponible: si el navegador limpia caché, el diccionario desaparece. El servidor debe poder devolver la respuesta completa sin diccionario.
La sección 9 del RFC 9842 (Security Considerations) detalla el modelo de amenaza completo. Léela antes de aplicar SDC a respuestas con datos autenticados; para assets estáticos con hash de contenido no hay riesgo material.
Cómo medir el impacto real en producción
El impacto de SDC no aparece en Lighthouse (mide el primer request, no la actualización) ni en la mayoría de dashboards RUM por defecto. Necesitas instrumentación específica. En RUM para Core Web Vitals con web-vitals.js y CrUX API ya cubrí la base; para SDC añado tres métricas custom:
// Reporta bytes transferidos vs bytes descomprimidos por recurso
new PerformanceObserver((entries) => {
for (const entry of entries.getEntries()) {
if (entry.encodedBodySize === 0) continue;
const ratio = entry.decodedBodySize / entry.encodedBodySize;
const usedDictionary = entry.responseStatus === 200 &&
entry.contentType?.includes('javascript') &&
ratio > 20; // heurística: ratio típico br=4x, dcb con delta pequeño >20x
beacon('/rum/compression', {
url: entry.name,
encoded: entry.encodedBodySize,
decoded: entry.decodedBodySize,
ratio,
usedDictionary,
});
}
}).observe({ type: 'resource', buffered: true });
Agrupa en tu backend por usedDictionary=true|false y compara el p75 de tiempo de descarga por bucket de bandwidth. En despliegues frecuentes he visto reducciones de 60-80% en bytes y 200-400 ms menos de LCP en el percentil 75 para usuarios recurrentes con conexiones 4G. Los usuarios nuevos ven el mismo tiempo que antes: no hay regresión.
Complementa la instrumentación con presupuestos de rendimiento como los que describo en cómo optimizar scripts de terceros con Partytown y Facade Pattern: fija un umbral máximo de bytes por deploy y falla el CI si un cambio rompe el diccionario para más del X% de tus usuarios activos.
Cuándo NO usar diccionarios compartidos
No es una técnica universal. Descarta SDC cuando:
Los recursos cambian estructuralmente entre versiones. Un rewrite completo de tu framework anula el diccionario; el delta es del tamaño del archivo entero y añades overhead por nada.
Sirves respuestas únicas por usuario sin patrón compartido. Un JSON de dashboard personalizado no tiene diccionario común.
Tu tráfico es mayoritariamente de primera visita. Landing pages virales con bounce rate alto no verán la mejora; el primer request nunca usa diccionario.
Tu CDN no soporta ni passthrough ni control fino de Vary. Servir SDC detrás de una caché rota corrompe respuestas.
Para el resto (SPAs con despliegue continuo, marketplaces con miles de páginas producto similares, PWAs con actualizaciones frecuentes), es la mejor inversión de perf/hora del ingeniero que hemos tenido desde que Brotli reemplazó a gzip. Combinada con la reducción de TTFB que documenté en cómo reducir el TTFB con Early Hints, Edge SSR y Server-Timing, el efecto compuesto sobre LCP para visitantes recurrentes es difícil de superar con otra técnica.
Preguntas frecuentes
¿Qué navegadores soportan la compresión con diccionario compartido en 2026?
Chrome 130+ y Edge 130+ (octubre 2024), junto con el resto de navegadores basados en Chromium en la misma versión o superior. Firefox tiene la implementación en desarrollo activo pero no ha marcado fecha. Safari aún no ha anunciado soporte. El fallback a Brotli/gzip es automático, así que activarlo no rompe a los usuarios sin soporte.
¿Puedo usar gzip con diccionarios compartidos?
No. El RFC 9842 solo define codificaciones para Brotli (dcb) y Zstandard (dcz). Gzip carece de soporte para diccionarios externos en su especificación DEFLATE, así que si tu stack solo emite gzip necesitas migrar a Brotli o Zstd antes de considerar SDC.
¿Cuánto ahorra realmente un diccionario compartido frente a Brotli normal?
Depende de cuánto cambien las versiones. Para un bundle JavaScript con un patch típico (una feature nueva, sin refactor mayor), el delta suele ser 90-98% más pequeño que la respuesta Brotli completa. Cloudflare reportó 272 KB a 2,6 KB en uno de sus tests. Para archivos que cambian por completo entre versiones, el ahorro tiende a cero.
¿Necesito modificar mi código de aplicación?
No para consumir SDC: el navegador y el HTTP stack lo gestionan de forma transparente. Sí para producirlo: tu servidor o edge worker necesita librerías que acepten diccionarios externos (Brotli 1.1+ o cualquier versión de Zstandard) y lógica para verificar Available-Dictionary y añadir la cabecera Use-As-Dictionary.
¿Cómo se compara con HTTP/3 y QUIC para el rendimiento?
Son capas complementarias, no alternativas. HTTP/3 reduce la latencia de setup y elimina head-of-line blocking; SDC reduce la cantidad de bytes que viajan por esa conexión. Los mayores ahorros los verás combinando ambas: QUIC entrega los pocos KB del delta con RTT mínimo.
Cómo optimizar el bfcache en 2026: usa NotRestoredReasons para diagnosticar por qué Chrome expulsa tu página, arregla los 8 bloqueadores comunes (unload, WebSocket, no-store) y sube tu hit rate al 60% con impacto directo en LCP e INP.
Los scripts de terceros (GTM, Intercom, píxeles, chat) son la causa número uno de INP y LCP degradados. Esta guía práctica muestra cómo auditarlos con Lighthouse y third-party-web, moverlos al Web Worker con Partytown, aplicar el patrón facade a embeds pesados y bloquear regresiones con presupuestos en Lighthouse CI.
Implementa RUM de Core Web Vitals con web-vitals.js v5 y la CrUX API. Recolecta LCP, INP y CLS reales, calcula p75 por dispositivo y compara contra el CrUX oficial en menos de 30 líneas.