La cabecera Vary: sacando partido a la negociación de contenido para hacer (un poco) de todo

Si llevas tiempo trabajando con sistemas de CDN o cachés como Nginx o Varnish, o si eres un experto en el protocolo HTTP y el rendimiento web, probablemente te has topado con la cabecera Vary más de una vez. Es una de esas grandes incomprendidas (por complejidad) en el protocolo, y es un arma de doble filo; puede ser tu mejor aliada para entregar el contenido correcto o tu peor enemiga destrozando tu Hit-Ratio.

Recientemente, uno de los creadores de Django hablaba de nuestra competencia en el mundo del caché y el rendimiento web, evidenciando una limitación (que por supuesto, nosotros no tenemos B-) ). Fuera de eso, me hizo pensar en el desconocimiento que existe, incluso entre profesionales de prestigio, sobre el uso de esta cabecera.

Hoy vamos a desglosar qué hace exactamente Vary, por qué es crucial para la negociación de contenido y cómo domar a la bestia para que tu caché vuele de forma -relativamente- indolora.

¿Qué es exactamente Vary?

La regla de oro básica que usa todo el mundo es: “Una URL = Un objeto en caché.”

Si un usuario pide en una petición GET el /home, la caché guarda la respuesta y se la sirve a la siguiente petición, siempre y cuando las cabeceras de la respuesta lo permitan.

Pero la web moderna es compleja. A veces, la misma URL debe devolver contenidos distintos dependiendo de quién o cómo se pida. Enter negociación de contenido.

La cabecera de respuesta Vary le dice a la caché (y al navegador):

“Oye, para decidir si sirves esta copia guardada, no mires solo la URL. Fíjate también en estas cabeceras de la petición del usuario”.

Y para qué coño me va a servir esto, preguntarás. Vamos con el problema clásico:

El “sota, caballo y rey”: la compresión de respuestas

El caso más habitual es Vary: Accept-Encoding.

  1. Navegador A (Soporta gzip) pide /estilos.css. El servidor responde con el archivo comprimido.
  2. Navegador B (Curl, por ejemplo) pide /estilos.css:

Si la caché ignorara el Vary, podría entregarle al navegador B la versión comprimida (en gzip) que guardó del navegador A. Resultado: el usuario ve basura en pantalla.

Con Vary: Accept-Encoding, la caché almacena variaciones distintas de la misma URL basándose en la capacidad de compresión del cliente.

Este mecanismo es tan sumamente común que la mayoría de los servidores de caché o CDN lo hacen por defecto. Además, como hay pocos “valores” distintos de Accept-Encoding, el impacto en el Hit-ratio es mínimo.

El impacto en el CHR: el ataque de los clones

Aquí es donde debemos ponernos el sombrero de arquitectos de sistemas. Cada variación que añades reduce la probabilidad de un acierto de caché (Hit).

Imagina que tu aplicación backend devuelve:

Vary: User-Agent

Esta es una de las configuraciones más peligrosas. Existen miles de cadenas de User-Agent diferentes (Chrome versión X, Y, Z; Safari en iOS 14, 15…). Si haces Vary por User-Agent sin control, Varnish guardará una copia idéntica de tu página para cada versión milimétrica de cada navegador. No hablemos ya de un Vary: cookie o Vary: Accept-Language. Para eso, quitas la caché y ya te ahorras un paso en tu suicidio.

Consecuencias de un error de este tipo:

  1. Llenado de memoria: si estás usando una caché compartida, vas a desahuciar contenido importante para llenar la caché de duplicados. Y más vale que tu sistema de caché sea capaz de soportar el aumento de la memoria o la vas a llenar muy rápido.
  2. Hit-Ratio bajo: la mayoría de usuarios recibirán un MISS porque su User-Agent varía ligeramente del anterior.
  3. Estrés en backend: tu servidor de origen trabajará mucho más.

Casos de uso habituales (y cómo manejarlos)

1. Accept-Encoding (Compresión)

Es obligatorio si sirves contenido comprimido dinámicamente. La buena noticia es que la mayoría de los servidores de caché o CDNs lo hacen por defecto.

  • Riesgo: los navegadores envían cabeceras complejas (gzip, deflate, br).
  • Solución: normaliza la cabecera. Ordenando los ítems de la cabecera de entrada, puedes convertirlo en una cabecera simple como gzip o br, y actuar en consecuencia.

2. Origin (CORS)

Si tu API es consumida por diferentes dominios y utilizas Cross-Origin Resource Sharing, el servidor devolverá cabeceras Access-Control-Allow-Origin diferentes según quién haga la petición.

  • Uso: Vary: Origin
  • Solución: necesario para seguridad en API, pero asegúrate de normalizar los orígenes si es posible y bloquear los que no deban ser servidos.

3. Accept-Language (Idioma)

Sirves el mismo HTML en inglés o español según la preferencia del navegador en la misma URL.

  • Uso: evítalo si puedes.
  • Best practice: es mucho mejor para el SEO y la caché usar URLs distintas (/en/page, /es/page). Si usas Vary: Accept-Language, la caché se fragmentará enormemente dado que las combinaciones de idiomas de los usuarios son cuasi infinitas.

4. Accept

Esto es la base de muchos frameworks de contenido. Si en la cabecera Accept el navegador dice que admite, por ejemplo, text/markdown, el servidor puede devolver una versión del contenido (usando la misma URL) en este formato en lugar de devolver el HTML. En Transparent Edge utilizamos este “truco” para servir imágenes adaptadas con nuestro optimizador i3 bajo la misma URL.

  • Uso: Vary: Accept
  • ¡Precaución! Si no normalizas Accept, te expones al Ataque de los clones que comentábamos antes.

Recetas de Transparent Edge (como Gallina Blanca, pero sin ultraprocesar nada)

El secreto para un Hit-Ratio alto usando Vary es la normalización. No confíes en la cabecera cruda que envía el usuario. Transfórmala en categorías genéricas antes de que la caché decida buscar el objeto. En Transparent Edge damos cabeceras auxiliares para que el sistema pueda hacer Vary por ellas. Un ejemplo es la cabecera X-Device.

sub  vcl_recv {
    set req.http.X-Vary-TCDN = req.http.X-Vary-TCDN + ";" +req.http.X-Device;
    # Solo tendrás 3 variantes por URL (mobile, tablet, desktop), no 20.000.
}

Qué te llevas a casa

En Transparent Edge te animamos a que uses Vary con moderación y cabeza, y que uses nuestra cabecera auxiliar X-Vary-TCDN para que el sistema pueda hacer Vary por cualquier criterio. Si necesitas más información, no dudes en preguntarnos y en soporte te echaremos una mano con tu caso de uso.

Autor: Diego Suárez es director de Tecnología de Transparent Edge.

Si alguna vez buscas a Diego, lo encontrarás con casi toda probabilidad donde haya un sistema operativo y no lo hallarás buscando el camino fácil, sino afrontando la alternativa más compleja. Estar siempre en el filo de la navaja le confiere la versatilidad y la visión necesarias para desarrollar la estrategia de tecnología que es el centro gravitacional de Transparent Edge. A caballo siempre entre desarrollo y sistemas, no por ello ha dejado de lado otras áreas, preocupándose por mantener el contacto con el usuario, que es para quien trabaja toda empresa tecnológica.