# Negociación de contenido (content negotiation) en HTTP

> Qué es la negociación de contenido en HTTP, cómo elige el servidor entre HTML, Markdown o idiomas con Accept y Vary, y qué cambia para los bots.

URL: https://clione.ai/es/glosario/negociacion-de-contenido/
Lang: es

La negociación de contenido no tiene nada que ver con negociar un precio, ni con un agente que regatea por ti. Es una pieza de HTTP, el protocolo con el que tu servidor responde a cada visita, y resuelve una pregunta muy concreta: **cuando una URL tiene varias versiones, cuál entrega a quién**. A una tienda le importa más que hace unos años porque ya no la visitan solo navegadores. Un [agente de IA](/es/glosario/agente-de-ia/) puede pedir la misma [ficha de producto](/es/glosario/ficha-de-producto/) en Markdown, un [rastreador](/es/glosario/rastreador/) puede pedirla sin decir en qué idioma la quiere, y entre los dos y tu servidor suele haber una caché que guarda copias. Si la negociación está mal hecha, cada uno puede acabar viendo la versión de otro.

## Cómo funciona

El estándar es el RFC 9110, publicado en junio de 2022. Su sección 12 parte de una observación: el servidor suele tener varias maneras de representar la misma información (formatos, idiomas, codificaciones), y quien la pide tiene capacidades y preferencias distintas, así que HTTP ofrece mecanismos para negociar cuál se entrega.[^rfc9110] Define tres patrones:[^rfc9110]

- **Negociación proactiva** (_server-driven_): el cliente manda sus preferencias y **elige el servidor**. Para decidir puede usar las cabeceras de negociación y también rasgos implícitos de la petición, como la dirección de red o partes del campo [User-Agent](/es/glosario/user-agent/).
- **Negociación reactiva** (_agent-driven_): el servidor devuelve una lista de alternativas y **elige el cliente**, con una segunda petición.
- **Negociación del contenido de la petición**: el servidor dice en su respuesta qué formatos prefiere recibir en las peticiones siguientes.

La proactiva es la que se ve en la web. Las cabeceras que la llevan son cuatro: `Accept` (el formato), `Accept-Charset` (el juego de caracteres), `Accept-Encoding` (la compresión) y `Accept-Language` (el idioma). Cada preferencia puede llevar un peso `q` entre 0 y 1, donde 0 significa «no aceptable» y, si no hay peso, vale 1. Una petición sin una de esas cabeceras significa que el cliente no tiene preferencia en esa dimensión. Y si ninguna versión encaja, el servidor puede responder 406 (_Not Acceptable_) o ignorar la cabecera y servir la de siempre.[^rfc9110]

El propio estándar no la vende como gratis. Enumera sus inconvenientes: el servidor nunca sabe del todo qué es lo mejor para cada cliente, describir capacidades en cada petición es ineficiente y puede afectar a la privacidad, complica el servidor y **limita que las cachés compartidas reutilicen las respuestas**.[^rfc9110]

### La cabecera Vary

Ese último inconveniente se gestiona con `Vary`. Es una cabecera de respuesta que enumera qué partes de la petición pudieron influir en la versión elegida, y tiene dos funciones: avisar a las cachés de que no pueden usar esa respuesta para otra petición salvo que las cabeceras listadas coincidan, y avisar al cliente de que hay otras versiones. El RFC pide enviarla en cualquier respuesta que se pueda guardar en caché cuando el contenido se ha adaptado a esas cabeceras, por ejemplo al elegir el idioma por `Accept-Language`.[^rfc9110] El estándar de caché lo remata: una caché no puede reutilizar sin revalidar una respuesta guardada si las cabeceras que nombra `Vary` no coinciden con las de la nueva petición.[^rfc9111] Por eso la negociación y la [caché y CDN para bots](/es/glosario/cache-cdn-bots/) van siempre juntas.

## En una tienda

El caso que más se ve en 2026 es el del formato. Cloudflare ofrece una función, [Markdown for Agents](/es/glosario/markdown-para-agentes/), que hace exactamente esto: cuando un cliente añade `Accept: text/markdown` a la petición, pide al origen la versión HTML, la convierte a Markdown y la sirve con `Content-Type: text/markdown; charset=utf-8`, **con `Accept` incluido en `Vary`** para que las cachés guarden las dos versiones por separado. Está disponible en los planes Pro, Business y Enterprise.[^cloudflare-markdown] El formato en sí tiene su propia entrada, [Markdown para agentes](/es/glosario/markdown-para-agentes/).

Así se vería el intercambio en una tienda **ejemplo ficticio**. El agente prefiere Markdown (peso 1, por omisión) y acepta HTML como segunda opción (peso 0,8):

```http
GET /productos/moka-inox-6 HTTP/1.1
Host: cafeteras-ejemplo.es
Accept: text/markdown, text/html;q=0.8

HTTP/1.1 200 OK
Content-Type: text/markdown; charset=utf-8
Vary: Accept
```

La línea que importa es la última. Sin `Vary: Accept`, una caché compartida que guardó esta respuesta podría dársela al siguiente navegador que pida la misma URL.[^rfc9111]

El otro caso es el idioma, y ahí el riesgo lo pone el rastreador. Google dice que [Googlebot](/es/glosario/googlebot/) hace sus peticiones **sin la cabecera `Accept-Language`** y que sus direcciones IP por defecto parecen estar en Estados Unidos, aunque también rastrea desde fuera. Si tu tienda elige el idioma por esa cabecera, Google avisa de que puede no rastrear, indexar ni clasificar todo tu contenido en cada idioma, y recomienda URL separadas por idioma, anotadas con [hreflang](/es/glosario/hreflang/).[^google-locale] Con la regla de ausencia del RFC, una petición sin `Accept-Language` no expresa preferencia,[^rfc9110] así que Googlebot verá la versión por defecto y solo esa.


Llamar negociación de contenido a servir otra página a los bots. La negociación entrega **otra representación del mismo recurso**: el mismo producto, el mismo precio, en otro formato o idioma. El RFC deja claro que esa coherencia la decide por completo quien genera las respuestas, no HTTP.[^rfc9110] Y Google llama _cloaking_ a presentar contenido distinto a las personas y a los buscadores con intención de manipular la clasificación, con un ejemplo muy literal: insertar texto o palabras clave solo cuando quien pide la página es un buscador.[^google-spam]


## Cómo comprobarlo

1. **Pide tu ficha en Markdown** con `curl -sI -H "Accept: text/markdown" https://tu-dominio/productos/tu-producto` y mira `Content-Type`. Si sigue siendo `text/html`, tu servidor ignora esa preferencia, que es una respuesta válida según el RFC.[^rfc9110]
2. **Mira `Vary` cada vez que cambie el formato o el idioma.** Si la respuesta cambia según `Accept` o `Accept-Language` y `Vary` no nombra esa cabecera, una caché compartida puede servir la versión equivocada.[^rfc9111]
3. **Pide la página sin `Accept-Language`.** Es lo que hace Googlebot, y lo que recibas es lo que puede rastrear.[^google-locale]
4. **Compara el precio y la disponibilidad** en todas las versiones. Si no coinciden, el problema ya no es de formato.

## Concepto cercano: user agent

El [_user agent_](/es/glosario/user-agent/) es el nombre con el que se presenta el cliente, no una preferencia. El RFC admite que la negociación proactiva use partes de ese campo como señal implícita,[^rfc9110] pero son cosas distintas: `Accept` dice **qué quiere** el cliente; el _user agent_ dice **quién dice ser**, y cualquiera puede escribirlo. Elegir la versión por el _user agent_ es adivinar; elegirla por `Accept` es responder a lo que se pidió. Para comprobar quién llama de verdad hace falta otra cosa, como [Web Bot Auth](/es/glosario/web-bot-auth/).

Para seguir, la [ruta de lectura](/es/aprender/rastreo-e-indexacion/) de rastreo e [indexación](/es/glosario/indexacion/) ordena el resto de piezas que deciden qué ve un bot en tu tienda.
