Consultoría IA para empresas — 100% remoto, trabajamos con tu equipo in-house

javi@javadex.es — Diagnóstico gratuito 30 min
Despliega tu proyecto IA hoy — VPS desde 4,99€/mes con SSD NVMeVer Hostinger
Inicio/Blog/Markdown para Agentes de IA: Content Negotiation Explicado [2026]
Volver al Blog
Tutoriales IA13 de abril de 202615 min

Markdown para Agentes de IA: Content Negotiation Explicado [2026]

Content Negotiation permite servir markdown a agentes de IA en vez de HTML, reduciendo tokens un 80%. Guia completa con implementacion en Cloudflare, Next.js y WordPress.

Markdown para Agentes de IA: Content Negotiation Explicado [2026]

TLDR: Content Negotiation es la tecnica HTTP que permite a tu servidor detectar cuando un agente de IA solicita una pagina y servirle markdown limpio en vez de HTML con navegacion, scripts y CSS. Cloudflare lo lanzo como "Markdown for Agents" en febrero de 2026, Claude Code ya envia el header Accept: text/markdown, y la reduccion de tokens es del 80% de media. Implementarlo lleva 30 minutos en Cloudflare Workers, Next.js middleware o Nginx. Es una de las cuatro capas clave de la web legible por maquinas junto con llms.txt, NLWeb y schema.org.


Que es Content Negotiation y por que importa para la IA

Content Negotiation es un mecanismo estandar de HTTP (definido en el RFC 7231) que permite a un cliente y un servidor negociar el formato de la respuesta. Cuando tu navegador pide una pagina web, envia un header Accept: text/html y el servidor responde con HTML. Es algo que lleva funcionando desde los 90.

Lo que ha cambiado en 2026 es que los agentes de IA han empezado a usar este mismo mecanismo para pedir contenido en un formato que les sea mas util: markdown.

La logica es simple:

  • Un navegador necesita HTML para renderizar la pagina visualmente
  • Un agente de IA necesita el contenido textual para entenderlo y procesarlo
  • HTML tiene navegacion, scripts, estilos, widgets, footers, sidebars... ruido
  • Markdown es texto puro con estructura basica: encabezados, listas, enlaces, codigo

Cuando un agente de IA como Claude Code, ChatGPT con Browse, o Perplexity visita tu web, no necesita tu menu de navegacion ni tu footer con enlaces a redes sociales. Necesita el contenido de la pagina en el formato mas limpio posible.

El coste real del HTML para los agentes de IA

Para entender por que Content Negotiation es tan importante, veamos numeros reales.

Tomemos una pagina tipica de blog (como esta que estas leyendo):

MetricaHTML completoSolo contenido markdown
Tamano de la respuesta~45 KB~8 KB
Tokens (GPT-4 tokenizer)~12.000 tokens~2.400 tokens
Tiempo de parseo200-500ms<10ms
Coste por lectura (GPT-4)~$0.06~$0.012
Precision de extraccion85-90%99%+

Esa diferencia de 5x en tokens tiene tres consecuencias directas:

  1. Coste: Cada pagina que lee un agente cuesta 5 veces menos en tokens de API
  2. Ventana de contexto: El agente puede leer 5 veces mas paginas antes de llenar su contexto
  3. Precision: Sin ruido HTML, el agente entiende mejor el contenido y comete menos errores

Multiplica eso por las miles de paginas que un agente puede leer en una sesion de investigacion (como el Deep Research de ChatGPT o Perplexity) y la diferencia es enorme.


Como funciona: el header Accept: text/markdown

El mecanismo es elegante en su simplicidad. Es HTTP estandar, sin nada propietario.

La peticion del agente

Cuando un agente de IA compatible pide una pagina de tu web, envia un header como este:

http
1GET /blog/mi-articulo HTTP/2
2Host: tusitio.com
3Accept: text/markdown, text/html;q=0.9, */*;q=0.8
4User-Agent: Claude-Code/4.6 (Anthropic)

El header Accept dice: "prefiero markdown (text/markdown), pero acepto HTML (text/html;q=0.9) si no tienes markdown". El parametro q indica la preferencia (1.0 es maximo, 0.9 es "lo acepto pero prefiero otro").

La respuesta del servidor

Si tu servidor soporta Content Negotiation, detecta que el cliente prefiere markdown y responde:

http
1HTTP/2 200 OK
2Content-Type: text/markdown; charset=utf-8
3Vary: Accept
4X-Content-Source: markdown-negotiated
5 
6# Mi Articulo
7 
8Este es el contenido del articulo en markdown limpio...
9 
10## Seccion 1
11 
12Parrafo con **negrita** y [enlaces](https://ejemplo.com)...

El header Vary: Accept es critico: le dice a las caches (CDN, proxies) que la respuesta varia segun el header Accept del cliente. Sin el, una cache podria servir markdown a un navegador o HTML a un agente.

Si no soportas Content Negotiation

El agente recibe HTML normal, lo parsea como puede, y pierde tokens y precision. No se rompe nada; simplemente es menos eficiente.


Quien ya envia Accept: text/markdown

A abril de 2026, varios agentes de IA ya envian (o pueden configurarse para enviar) el header Accept: text/markdown:

AgenteEnvia text/markdownUser-Agent tipico
Claude CodeSi, por defectoClaude-Code/4.6 (Anthropic)
ChatGPT BrowseSi (desde marzo 2026)ChatGPT-User
PerplexitySi, por defectoPerplexityBot
Copilot / Bing ChatSi (via Cloudflare)bingbot con header extendido
Claude DesktopParcial (en web search)Claude-Web/1.0
Google GeminiNo confirmado-
Cursor IDESi (hereda de Claude/GPT)Varia segun el modelo backend
WindsurfSiVaria segun configuracion

El dato clave: Claude Code, que es el asistente de terminal mas usado por desarrolladores, ya envia Accept: text/markdown por defecto. Esto significa que cada vez que un desarrollador le pide a Claude Code que lea documentacion o una web, tu servidor podria estar sirviendo HTML innecesario en vez de markdown limpio.

Si tu web es documentacion tecnica, una API reference, o un blog de programacion, tus visitantes mas frecuentes probablemente son agentes de IA. Y les estas sirviendo el formato equivocado.


Cloudflare Markdown for Agents: la solucion de un click

En febrero de 2026, Cloudflare lanzo "Markdown for Agents", una funcionalidad del dashboard que implementa Content Negotiation automaticamente para cualquier sitio detras de Cloudflare.

Como funciona

Cloudflare actua como proxy reverso entre el visitante y tu servidor. Cuando detecta que el visitante envia Accept: text/markdown:

  1. Deja pasar la peticion a tu servidor normalmente
  2. Recibe el HTML de respuesta
  3. Convierte el HTML a markdown usando su motor de parseo (basado en el proyecto readability de Mozilla)
  4. Devuelve el markdown al agente

Tu servidor no se entera de nada. No necesitas cambiar codigo, no necesitas middleware, no necesitas tocar nada en tu stack.

Activar Markdown for Agents en Cloudflare

  1. Ve a tu dashboard de Cloudflare
  2. Selecciona el dominio
  3. Ve a AI > AI Readable
  4. Activa "Serve markdown to AI agents"
  5. Listo

Cloudflare maneja:

  • La deteccion del header Accept: text/markdown
  • La conversion HTML-to-markdown
  • El header Vary: Accept en la respuesta
  • El cache diferenciado (HTML para navegadores, markdown para agentes)
  • La extraccion del contenido principal (eliminando nav, footer, sidebar)

Limitaciones de Cloudflare Markdown for Agents

  • Solo funciona para sitios detras de Cloudflare (plan gratuito incluido)
  • La conversion HTML-to-markdown no es perfecta para layouts complejos
  • No convierte imagenes (las referencia con sintaxis markdown pero la URL sigue siendo la misma)
  • No funciona para paginas que requieren JavaScript para renderizar contenido (SPAs puras)
  • No tienes control granular sobre que se incluye/excluye del markdown

Para la mayoria de sitios, estas limitaciones son menores. Pero si necesitas control total, la implementacion manual es mejor.


Implementacion manual en Next.js

Si tu sitio usa Next.js (como muchos sitios modernos en 2026), puedes implementar Content Negotiation con un middleware que intercepta las peticiones.

Opcion 1: Middleware global

typescript
1// middleware.ts (raiz del proyecto)
2import { NextResponse } from 'next/server'
3import type { NextRequest } from 'next/server'
4 
5export function middleware(request: NextRequest) {
6 const accept = request.headers.get('accept') || ''
7 
8 // Detectar si el cliente prefiere markdown
9 if (prefersMarkdown(accept)) {
10 // Reescribir la URL para servir la version markdown
11 const url = request.nextUrl.clone()
12 url.searchParams.set('format', 'markdown')
13 
14 const response = NextResponse.rewrite(url)
15 response.headers.set('Vary', 'Accept')
16 return response
17 }
18 
19 return NextResponse.next()
20}
21 
22function prefersMarkdown(accept: string): boolean {
23 // Parsear el header Accept y comprobar prioridades
24 const types = accept.split(',').map(t => {
25 const [type, ...params] = t.trim().split(';')
26 const q = params.find(p => p.trim().startsWith('q='))
27 return {
28 type: type.trim(),
29 quality: q ? parseFloat(q.split('=')[1]) : 1.0,
30 }
31 })
32 
33 const markdownQ = types.find(t => t.type === 'text/markdown')?.quality || 0
34 const htmlQ = types.find(t => t.type === 'text/html')?.quality || 0
35 
36 return markdownQ > htmlQ
37}
38 
39export const config = {
40 matcher: ['/blog/:slug*', '/docs/:path*'],
41}

Opcion 2: En la pagina del blog directamente

typescript
1// app/blog/[slug]/page.tsx
2import { headers } from 'next/headers'
3import { NextResponse } from 'next/server'
4import fs from 'fs'
5import path from 'path'
6 
7export default async function BlogPost({
8 params,
9 searchParams,
10}: {
11 params: { slug: string }
12 searchParams: { format?: string }
13}) {
14 // Si se pide formato markdown (via middleware)
15 if (searchParams.format === 'markdown') {
16 return serveMarkdown(params.slug)
17 }
18 
19 // Renderizado normal HTML
20 return <article>{/* ... contenido JSX ... */}</article>
21}
22 
23async function serveMarkdown(slug: string) {
24 const filePath = path.join(process.cwd(), 'app/blog', `*-${slug}.md`)
25 const files = fs.readdirSync(path.join(process.cwd(), 'app/blog'))
26 const file = files.find(f => f.endsWith(`${slug}.md`))
27 
28 if (!file) {
29 return new NextResponse('Not found', { status: 404 })
30 }
31 
32 const content = fs.readFileSync(
33 path.join(process.cwd(), 'app/blog', file),
34 'utf-8'
35 )
36 
37 // Eliminar frontmatter YAML
38 const markdownContent = content.replace(/^---[\s\S]*?---\n/, '')
39 
40 return new NextResponse(markdownContent, {
41 headers: {
42 'Content-Type': 'text/markdown; charset=utf-8',
43 'Vary': 'Accept',
44 },
45 })
46}

Opcion 3: API Route dedicada

Si prefieres un enfoque mas limpio, crea un API route que sirva markdown:

typescript
1// app/api/markdown/[slug]/route.ts
2import { NextRequest, NextResponse } from 'next/server'
3import fs from 'fs'
4import path from 'path'
5 
6export async function GET(
7 request: NextRequest,
8 { params }: { params: { slug: string } }
9) {
10 const blogDir = path.join(process.cwd(), 'app/blog')
11 const files = fs.readdirSync(blogDir)
12 const file = files.find(f => f.endsWith(`${params.slug}.md`))
13 
14 if (!file) {
15 return NextResponse.json({ error: 'Not found' }, { status: 404 })
16 }
17 
18 const content = fs.readFileSync(path.join(blogDir, file), 'utf-8')
19 
20 // Limpiar frontmatter
21 const markdown = content.replace(/^---[\s\S]*?---\n/, '')
22 
23 return new NextResponse(markdown, {
24 headers: {
25 'Content-Type': 'text/markdown; charset=utf-8',
26 'Vary': 'Accept',
27 'Cache-Control': 'public, max-age=3600',
28 },
29 })
30}

Para proyectos reales de implementacion de Content Negotiation en Next.js, puedes ver ejemplos de sitios que ya lo aplican en el portfolio de Javadex, donde Javier Santos documenta implementaciones de IA en produccion.


Implementacion en WordPress

WordPress tiene dos caminos para Content Negotiation: el plugin de Joost de Valk y la implementacion manual.

Plugin de Joost de Valk

Joost de Valk (fundador de Yoast) publico un plugin gratuito independiente de Yoast SEO que implementa Content Negotiation en WordPress. El plugin:

  1. Detecta el header Accept: text/markdown en las peticiones
  2. Convierte el contenido del post de HTML (el editor de bloques) a markdown
  3. Sirve el markdown con los headers correctos (Content-Type, Vary)

Instalacion:

  1. Descarga desde el repositorio de GitHub de Joost (jdevalk/wordpress-content-negotiation)
  2. Sube a wp-content/plugins/
  3. Activa desde el panel de plugins
  4. No requiere configuracion

Implementacion manual con functions.php

Si prefieres no usar un plugin:

php
1// functions.php
2add_action('template_redirect', function() {
3 $accept = $_SERVER['HTTP_ACCEPT'] ?? '';
4 
5 if (strpos($accept, 'text/markdown') !== false && is_singular()) {
6 global $post;
7 
8 // Convertir el contenido a markdown
9 $content = $post->post_content;
10 $markdown = html_to_markdown($content);
11 
12 // Anadir titulo
13 $markdown = "# " . $post->post_title . "\n\n" . $markdown;
14 
15 // Anadir metadata
16 $metadata = sprintf(
17 "> **Publicado**: %s | **Autor**: %s | **URL**: %s\n\n",
18 get_the_date('Y-m-d', $post),
19 get_the_author_meta('display_name', $post->post_author),
20 get_permalink($post)
21 );
22 
23 header('Content-Type: text/markdown; charset=utf-8');
24 header('Vary: Accept');
25 echo $metadata . $markdown;
26 exit;
27 }
28});
29 
30function html_to_markdown($html) {
31 // Conversion basica HTML a markdown
32 $md = $html;
33 
34 // Encabezados
35 $md = preg_replace('/<h([1-6])[^>]*>(.*?)<\/h[1-6]>/i',
36 function($matches) {
37 return str_repeat('#', $matches[1]) . ' ' . strip_tags($matches[2]) . "\n\n";
38 }, $md);
39 
40 // Parrafos
41 $md = preg_replace('/<p[^>]*>(.*?)<\/p>/is', "$1\n\n", $md);
42 
43 // Negrita
44 $md = preg_replace('/<(strong|b)[^>]*>(.*?)<\/(strong|b)>/i', '**$2**', $md);
45 
46 // Cursiva
47 $md = preg_replace('/<(em|i)[^>]*>(.*?)<\/(em|i)>/i', '*$2*', $md);
48 
49 // Enlaces
50 $md = preg_replace('/<a[^>]*href="([^"]*)"[^>]*>(.*?)<\/a>/i', '[$2]($1)', $md);
51 
52 // Listas
53 $md = preg_replace('/<li[^>]*>(.*?)<\/li>/i', "- $1\n", $md);
54 $md = preg_replace('/<\/?[uo]l[^>]*>/i', "\n", $md);
55 
56 // Codigo
57 $md = preg_replace('/<code[^>]*>(.*?)<\/code>/is', '`$1`', $md);
58 $md = preg_replace('/<pre[^>]*><code[^>]*>(.*?)<\/code><\/pre>/is', "
\n$1\n`\n", $md);

// Eliminar tags restantes

$md = strip_tags($md);

// Limpiar espacios

$md = preg_replace('/\n{3,}/', "\n\n", $md);

return trim($md);

}

code
1Para conversiones mas robustas, usa la libreria PHP `league/html-to-markdown`:
bash

composer require league/html-to-markdown

code
1 
php

use League\HTMLToMarkdown\HtmlConverter;

function html_to_markdown($html) {

$converter = new HtmlConverter([

'strip_tags' => true,

'remove_nodes' => 'nav footer aside script style',

]);

return $converter->convert($html);

}

code
1---
2 
3## Implementacion con Nginx
4 
5Si usas Nginx como proxy reverso o servidor web, puedes implementar Content Negotiation a nivel de configuracion sin tocar codigo.
6 
7### Estrategia: archivos .md junto a archivos .html
8 
9Si generas tu sitio estaticamente (Hugo, Jekyll, Next.js export), genera tambien una version `.md` de cada pagina. Luego Nginx sirve uno u otro segun el header `Accept`:
nginx

server {

listen 443 ssl;

server_name tusitio.com;

# Mapa para detectar preferencia de formato

set $preferred_format "html";

if ($http_accept ~* "text/markdown") {

set $preferred_format "md";

}

location /blog/ {

# Intentar servir markdown si el agente lo prefiere

try_files $uri.$preferred_format $uri.html $uri/ =404;

# Headers de Content Negotiation

add_header Vary "Accept" always;

# Content-Type correcto segun formato

if ($preferred_format = "md") {

add_header Content-Type "text/markdown; charset=utf-8";

}

}

}

code
1### Estrategia: conversion on-the-fly con lua
2 
3Si tienes Nginx con OpenResty (o el modulo lua):
nginx

location /blog/ {

content_by_lua_block {

local accept = ngx.req.get_headers()["Accept"] or ""

if string.find(accept, "text/markdown") then

-- Leer el HTML y convertir a markdown basico

local file = io.open("/var/www/html" .. ngx.var.uri .. ".html", "r")

if file then

local html = file:read("*a")

file:close()

-- Conversion basica (en produccion, usar libreria completa)

local md = html:gsub("(.-)", function(level, text)

return string.rep("#", tonumber(level)) .. " " .. text .. "\n\n"

end)

md = md:gsub("

(.-)

", "%1\n\n")

md = md:gsub("<[^>]+>", "")

ngx.header["Content-Type"] = "text/markdown; charset=utf-8"

ngx.header["Vary"] = "Accept"

ngx.say(md)

return

end

end

-- Fallback: servir HTML normal

ngx.exec("@html_fallback")

}

}

code
1---
2 
3## Implementacion con Cloudflare Workers (personalizada)
4 
5Si usas Cloudflare pero quieres mas control que la opcion de dashboard, puedes crear un Worker personalizado.
javascript

// worker.js

export default {

async fetch(request, env) {

const accept = request.headers.get('Accept') || ''

const url = new URL(request.url)

// Solo aplicar a rutas de contenido

if (!url.pathname.startsWith('/blog/') && !url.pathname.startsWith('/docs/')) {

return fetch(request)

}

// Detectar si el agente prefiere markdown

if (!prefersMarkdown(accept)) {

return fetch(request)

}

// Obtener el HTML del origen

const response = await fetch(request)

const html = await response.text()

// Convertir a markdown

const markdown = htmlToMarkdown(html)

return new Response(markdown, {

headers: {

'Content-Type': 'text/markdown; charset=utf-8',

'Vary': 'Accept',

'Cache-Control': 'public, max-age=3600',

'X-Content-Negotiation': 'markdown',

},

})

},

}

function prefersMarkdown(accept) {

const types = accept.split(',').map(t => {

const [type, ...params] = t.trim().split(';')

const qParam = params.find(p => p.trim().startsWith('q='))

return {

type: type.trim(),

q: qParam ? parseFloat(qParam.split('=')[1]) : 1.0,

}

})

const md = types.find(t => t.type === 'text/markdown')

const html = types.find(t => t.type === 'text/html')

return md && (!html || md.q > html.q)

}

function htmlToMarkdown(html) {

// Extraer contenido principal (entre

o
)

let content = html

const articleMatch = html.match(/]>([\s\S]?)<\/article>/i)

const mainMatch = html.match(/]>([\s\S]?)<\/main>/i)

if (articleMatch) content = articleMatch[1]

else if (mainMatch) content = mainMatch[1]

// Convertir elementos HTML a markdown

content = content

// Encabezados

.replace(/]>(.?)<\/h1>/gi, '# $1\n\n')

.replace(/]>(.?)<\/h2>/gi, '## $1\n\n')

.replace(/]>(.?)<\/h3>/gi, '### $1\n\n')

.replace(/]>(.?)<\/h4>/gi, '#### $1\n\n')

// Formato de texto

.replace(/<(strong|b)[^>]>(.?)<\/(strong|b)>/gi, '$2')

.replace(/<(em|i)[^>]>(.?)<\/(em|i)>/gi, '$2')

.replace(/]>(.?)<\/code>/gi, '$1')

// Enlaces

.replace(/]href="([^"])"[^>]>(.?)<\/a>/gi, '$2')

// Imagenes

.replace(/]src="([^"])"[^>]alt="([^"])"[^>]*\/?>/gi, '

$2
$2
')

// Listas

.replace(/]>(.?)<\/li>/gi, '- $1\n')

.replace(/<\/?[uo]l[^>]*>/gi, '\n')

// Parrafos

.replace(/]>(.?)<\/p>/gi, '$1\n\n')

// Bloques de codigo

.replace(/]>]class="language-(\w+)"[^>]>([\s\S]?)<\/code><\/pre>/gi,

'$1\n$2\n\n')

.replace(/]>]>([\s\S]*?)<\/code><\/pre>/gi, '\n$1\n\n')

// Limpiar tags restantes

.replace(/<[^>]+>/g, '')

// Entidades HTML

.replace(/&/g, '&')

.replace(/</g, '<')

.replace(/>/g, '>')

.replace(/"/g, '"')

.replace(/'/g, "'")

// Limpiar espacios

.replace(/\n{3,}/g, '\n\n')

return content.trim()

}

code
1---
2 
3## Por que reducir tokens importa tanto
4 
5La reduccion de tokens no es un detalle tecnico abstracto. Tiene impacto directo en tres dimensiones que afectan a como los agentes de IA interactuan con tu web.
6 
7### 1. Coste de API
8 
9Los proveedores de LLM cobran por token. A precios de abril de 2026:
10 
11| Modelo | Coste por 1M tokens input | Pagina HTML (12K tokens) | Pagina markdown (2.4K tokens) |
12|--------|--------------------------|------------------------|-------------------------------|
13| **GPT-4o** | $2.50 | $0.030 | $0.006 |
14| **Claude Opus 4** | $15.00 | $0.180 | $0.036 |
15| **Claude Sonnet 4** | $3.00 | $0.036 | $0.007 |
16| **Gemini 2.5 Pro** | $1.25 | $0.015 | $0.003 |
17 
18Para un agente que lee 50 paginas en una sesion de investigacion (un escenario comun con Deep Research), la diferencia es:
19 
20- **Con HTML**: 600.000 tokens = $1.50 (GPT-4o) o $9.00 (Claude Opus)
21- **Con markdown**: 120.000 tokens = $0.30 (GPT-4o) o $1.80 (Claude Opus)
22 
23**Ahorro del 80% en coste de API.** Los proveedores de agentes (OpenAI, Anthropic, Perplexity) prefieren sitios que sirvan markdown porque les cuesta menos operar.
24 
25### 2. Ventana de contexto
26 
27Los LLMs tienen ventanas de contexto limitadas. Aunque modelos como Claude Opus 4 tienen 1M de tokens, la ventana efectiva para razonamiento de calidad es mucho menor.
28 
29Si un agente necesita leer 10 paginas de tu documentacion:
30 
31- **Con HTML**: 120.000 tokens (ya ocupa una porcion significativa del contexto)
32- **Con markdown**: 24.000 tokens (deja espacio para razonar y responder)
33 
34Cuanto menos espacio ocupe tu contenido en la ventana de contexto, mejor razonara el agente sobre el y mas probable es que cite tu web correctamente.
35 
36### 3. Precision y fidelidad
37 
38El HTML esta lleno de elementos que confunden a los LLMs:
39 
40- Menus de navegacion que el modelo puede interpretar como contenido
41- Textos de botones ("Suscribete", "Comprar", "Leer mas") mezclados con el contenido real
42- Scripts inline y atributos CSS que ocupan tokens sin aportar nada
43- Footers con disclaimers legales que el modelo incluye en sus resumenes
44 
45Con markdown, el agente recibe **solo el contenido**. No hay ambiguedad sobre que es el articulo y que es navegacion. La precision de extraccion pasa del 85-90% con HTML al 99%+ con markdown.
46 
47---
48 
49## Content Negotiation y las otras capas de la web legible por maquinas
50 
51Content Negotiation no funciona en el vacio. Es una de cuatro capas complementarias que estan formando la infraestructura de la web legible por maquinas.
52 
53### Las cuatro capas
54 
55| Capa | Estandar/Protocolo | Funcion | Estado en 2026 |
56|------|-------------------|---------|----------------|
57| **1. Directorio** | `llms.txt` | Lista el contenido del sitio para LLMs | Adopcion creciente |
58| **2. Formato** | Content Negotiation | Sirve markdown en vez de HTML a agentes | Cloudflare nativo, adopcion rapida |
59| **3. Consulta** | NLWeb (Microsoft) | Endpoint conversacional para preguntas | Temprano, Yoast lo integra |
60| **4. Estructura** | schema.org / JSON-LD | Datos estructurados para parsing automatico | Maduro, ampliamente adoptado |
61 
62Si quieres entender NLWeb en detalle, tenemos una [guia completa del protocolo NLWeb de Microsoft](/blog/nlweb-microsoft-protocolo-conectar-web-agentes-ia-2026) con implementacion practica.
63 
64### Orden de implementacion recomendado
65 
66Si partes de cero, este es el orden que recomendamos:
67 
681. **schema.org / JSON-LD** (si no lo tienes ya): Es la base. Los LLMs ya lo entienden y Google lo usa para rich snippets. Herramientas como [Yoast para WordPress](/blog/chatgpt-para-seo-guia-completa-2026) lo generan automaticamente.
69 
702. **Content Negotiation**: Alto impacto, baja complejidad. Si usas Cloudflare, es un click. Si no, es un middleware de 50 lineas.
71 
723. **llms.txt**: Un archivo de texto en la raiz del dominio. 30 minutos de trabajo maximo.
73 
744. **NLWeb**: El mas complejo pero tambien el mas potente. Requiere un endpoint con logica de busqueda.
75 
76### Verificar que capas tienes implementadas
bash

1. Comprobar schema.org

curl -s https://tusitio.com | grep -o 'application/ld+json' | head -1

2. Comprobar Content Negotiation

curl -s -H "Accept: text/markdown" https://tusitio.com/blog/mi-post \

-o /dev/null -w "%{content_type}"

Deberia devolver: text/markdown

3. Comprobar llms.txt

curl -s https://tusitio.com/llms.txt | head -5

4. Comprobar NLWeb

curl -s https://tusitio.com | grep 'rel="nlweb"'

code
1---
2 
3## Medicion y analisis: saber quien consume tu markdown
4 
5Una vez implementado Content Negotiation, quieres saber quien lo esta usando.
6 
7### Logging de peticiones markdown
8 
9Anade logging a tu middleware o servidor para registrar peticiones con `Accept: text/markdown`:
typescript

// lib/markdown-analytics.ts

export function logMarkdownRequest(request: Request, slug: string) {

const userAgent = request.headers.get('user-agent') || 'unknown'

const accept = request.headers.get('accept') || ''

// Identificar el agente

const agent = identifyAgent(userAgent)

console.log(JSON.stringify({

timestamp: new Date().toISOString(),

type: 'markdown_request',

slug,

agent,

userAgent,

accept,

ip: request.headers.get('x-forwarded-for') || 'unknown',

}))

}

function identifyAgent(ua: string): string {

if (ua.includes('Claude')) return 'claude'

if (ua.includes('ChatGPT')) return 'chatgpt'

if (ua.includes('Perplexity')) return 'perplexity'

if (ua.includes('bingbot')) return 'bing-copilot'

if (ua.includes('Googlebot')) return 'google'

if (ua.includes('Cursor')) return 'cursor'

return 'unknown'

}

code
1### Metricas clave a seguir
2 
3| Metrica | Que mide | Por que importa |
4|---------|---------|-----------------|
5| **% peticiones markdown vs HTML** | Proporcion de agentes IA en tu trafico | Cuanto trafico IA recibes |
6| **Agentes unicos por dia** | Diversidad de agentes que te consultan | Alcance en ecosistema IA |
7| **Paginas mas solicitadas en markdown** | Que contenido buscan los agentes | Donde invertir en contenido |
8| **Tamano medio HTML vs markdown** | Eficiencia de la conversion | Ahorro real en tokens |
9| **Tasa de cache hit** | Si Cloudflare/CDN esta cacheando bien | Rendimiento |
10 
11### Dashboard basico con Cloudflare Analytics
12 
13Si usas Cloudflare, puedes filtrar en Analytics por:
14 
15- **Content-Type de respuesta**: `text/markdown` vs `text/html`
16- **User-Agent**: Filtrar por agentes de IA conocidos
17- **Cache status**: Ver si las respuestas markdown se cachean correctamente
18 
19---
20 
21## Casos especiales y edge cases
22 
23### SPAs y sitios renderizados en cliente
24 
25Si tu sitio es una SPA (React, Vue, Angular) que renderiza contenido con JavaScript en el cliente, Content Negotiation tiene un problema: el HTML que devuelve el servidor no tiene el contenido, solo un `<div id="root"></div>` vacio.
26 
27Soluciones:
28 
291. **SSR/SSG**: Migra a Server-Side Rendering o Static Site Generation. Next.js, Nuxt, SvelteKit lo hacen trivial.
302. **Pre-rendering**: Usa un servicio como Prerender.io que genera HTML estatico para bots y agentes.
313. **API separada**: Expone tu contenido como API REST con formato markdown, independiente del frontend.
32 
33### Contenido con componentes interactivos
34 
35Si tu blog tiene demos interactivas, calculadoras o widgets embebidos, el markdown no puede representarlos. Opciones:
36 
37- Incluye una nota en el markdown: `> Este articulo incluye demos interactivas. Visita la version completa en [URL]`
38- Describe el widget en texto: "La calculadora muestra que para un equipo de 5 personas, el coste mensual es de..."
39- Enlaza a la version HTML para componentes complejos
40 
41### Imagenes y media
42 
43Las imagenes se mantienen como referencias markdown (`![alt](url)`), pero el agente no las "ve" a menos que soporte multimodal. Asegurate de que el texto `alt` sea descriptivo:
markdown

imagen
imagen

Grafico comparativo de tokens: HTML usa 12.000 tokens vs markdown que usa 2.400 tokens, una reduccion del 80%
Grafico comparativo de tokens: HTML usa 12.000 tokens vs markdown que usa 2.400 tokens, una reduccion del 80%

code
1### Paginas multiidioma
2 
3Si tu sitio sirve contenido en varios idiomas, Content Negotiation puede combinarse con el header `Accept-Language`:
typescript

// Negociar formato Y idioma

const acceptType = request.headers.get('accept') || ''

const acceptLang = request.headers.get('accept-language') || ''

const format = prefersMarkdown(acceptType) ? 'md' : 'html'

const language = prefersSpanish(acceptLang) ? 'es' : 'en'

// Servir la version correcta

const content = await getContent(slug, { format, language })

code
1---
2 
3## Rendimiento y caching
4 
5Content Negotiation introduce una variable en el caching: la misma URL ahora puede devolver dos formatos diferentes. Esto hay que manejarlo correctamente.
6 
7### El header Vary es obligatorio
http

Vary: Accept

code
1Este header le dice a caches y CDNs que mantengan versiones separadas segun el header `Accept` del cliente. Sin el, un CDN podria cachear la version markdown y servirla a navegadores.
2 
3### Configuracion de Cloudflare
4 
5Si usas Cloudflare como CDN, asegurate de que tu Cache Rules respetan el `Vary: Accept`:
6 
71. Ve a **Caching > Cache Rules**
82. Crea una regla para las rutas de contenido
93. Activa **"Respect Vary header"**
10 
11### Cache a nivel de aplicacion
12 
13Para evitar regenerar markdown en cada peticion:
typescript

// lib/markdown-cache.ts

const markdownCache = new Map

content: string

etag: string

timestamp: number

}>()

const CACHE_TTL = 3600 * 1000 // 1 hora

export function getCachedMarkdown(slug: string) {

const cached = markdownCache.get(slug)

if (cached && Date.now() - cached.timestamp < CACHE_TTL) {

return cached

}

return null

}

export function setCachedMarkdown(slug: string, content: string) {

const etag = "${Buffer.from(content).toString('base64').slice(0, 32)}"

markdownCache.set(slug, { content, etag, timestamp: Date.now() })

return etag

}

code
1Usa el `ETag` para respuestas condicionales (`If-None-Match`), evitando transferir contenido que el agente ya tiene cacheado localmente.
2 
3---
4 
5## El papel de Joost de Valk y la comunidad open source
6 
7Vale la pena destacar el papel de Joost de Valk (fundador de Yoast) en el ecosistema de web legible por maquinas. Joost ha sido un defensor activo de multiples estandares complementarios:
8 
91. **Yoast Schema Aggregation**: Integracion nativa de NLWeb en WordPress via Yoast SEO
102. **Plugin de Content Negotiation para WordPress**: Implementacion independiente y gratuita
113. **Contribuciones a la especificacion NLWeb**: Colaboracion directa con Microsoft
124. **Advocacy de llms.txt**: Promocion del estandar en la comunidad WordPress
13 
14La vision de Joost es que WordPress (que alimenta el 43% de la web) sea "AI-ready" por defecto, sin que los propietarios de sitios necesiten conocimientos tecnicos. La combinacion de Yoast + Cloudflare cubre la mayoria de sitios web del mundo.
15 
16---
17 
18## Errores comunes al implementar Content Negotiation
19 
20### 1. Olvidar el header Vary
21 
22**Problema**: Sin `Vary: Accept`, los CDNs sirven markdown a navegadores o HTML a agentes.
23 
24**Solucion**: Anade SIEMPRE `Vary: Accept` en la respuesta.
25 
26### 2. Servir markdown a crawlers de busqueda
27 
28**Problema**: Googlebot y Bingbot no piden markdown (envian `Accept: text/html`). Pero si tu logica de deteccion es demasiado agresiva, podrias servir markdown a crawlers que no lo esperan.
29 
30**Solucion**: Solo sirve markdown cuando el header `Accept` incluye **explicitamente** `text/markdown` con una prioridad mayor que `text/html`.
31 
32### 3. Markdown incompleto
33 
34**Problema**: La conversion HTML-to-markdown pierde informacion (tablas complejas, iframes, formularios).
35 
36**Solucion**: Para contenido estatico (blog, docs), el markdown suele ser suficiente. Para paginas dinamicas, incluye una nota con enlace a la version HTML completa.
37 
38### 4. No cachear las respuestas markdown
39 
40**Problema**: Cada peticion regenera el markdown, aumentando la carga del servidor.
41 
42**Solucion**: Cachea las respuestas markdown con un TTL razonable (1-24 horas segun la frecuencia de actualizacion).
43 
44### 5. Incluir navegacion y footer en el markdown
45 
46**Problema**: Si conviertes TODO el HTML a markdown, incluyes el menu, breadcrumbs, sidebar y footer.
47 
48**Solucion**: Extrae solo el contenido principal (`<article>` o `<main>`) antes de convertir. Los agentes no necesitan tu navegacion.
49 
50---
51 
52## El futuro de Content Negotiation para IA
53 
54Content Negotiation para IA va a evolucionar en varias direcciones en los proximos meses.
55 
56### Mas tipos MIME
57 
58Ademas de `text/markdown`, es probable que veamos:
59 
60- `application/json+article`: Contenido estructurado como JSON
61- `text/plain+summary`: Resumen del articulo en texto plano
62- `application/nlweb+json`: Respuesta NLWeb (ya en uso)
63 
64### Negociacion bidireccional
65 
66Hoy la negociacion es unidireccional: el agente pide y el servidor responde. En el futuro, el servidor podria informar de sus capacidades:
http

HTTP/2 200 OK

Content-Type: text/markdown

Accept-Post: application/nlweb+json

Link: ; rel="nlweb"

code
1"Te sirvo markdown, pero tambien acepto preguntas NLWeb en esta URL."
2 
3### Integracion en standards web
4 
5Es posible que el W3C o IETF formalicen Content Negotiation para IA como una extension del RFC 7231, con tipos MIME registrados oficialmente y comportamientos estandarizados.
6 
7---
8 
9## Preguntas frecuentes sobre Content Negotiation para IA
10 
11### ¿Content Negotiation afecta al SEO de mi sitio?
12 
13**No negativamente.** Los crawlers de Google y Bing envian `Accept: text/html` y reciben HTML normal. Content Negotiation solo afecta a los agentes que piden explicitamente markdown. Ademas, si implementas el header `Vary: Accept` correctamente, los buscadores sabran que hay versiones diferentes y no penalizaran por contenido duplicado. De hecho, tener Content Negotiation puede mejorar tu visibilidad en buscadores de IA como Perplexity, lo que complementa tu [estrategia de SEO con herramientas de IA](/blog/chatgpt-para-seo-guia-completa-2026).
14 
15### ¿Necesito reescribir todo mi sitio para soportar Content Negotiation?
16 
17**No.** Si usas Cloudflare, activar "Markdown for Agents" es un toggle en el dashboard. Si no, un middleware de 30-50 lineas de codigo en tu framework (Next.js, Express, Django) es suficiente. No necesitas cambiar tu HTML, tu CMS ni tu flujo de publicacion. La conversion se hace al vuelo.
18 
19### ¿Que pasa si un agente pide markdown y mi servidor no lo soporta?
20 
21**Nada malo.** El agente recibe HTML y lo procesa como siempre ha hecho. Content Negotiation es un mecanismo de mejora progresiva: si esta disponible, se usa; si no, el agente funciona igual que antes, solo que de forma menos eficiente.
22 
23### ¿Como se si agentes de IA estan visitando mi sitio?
24 
25Revisa los logs de tu servidor buscando User-Agents como `ChatGPT-User`, `Claude-Web`, `PerplexityBot`, `Claude-Code`, `bingbot` (Copilot). Tambien puedes filtrar por peticiones con el header `Accept: text/markdown`. En Cloudflare, la seccion "Bot Analytics" muestra trafico de bots clasificado.
26 
27### ¿Content Negotiation funciona con formularios y paginas dinamicas?
28 
29**Parcialmente.** Content Negotiation funciona mejor con contenido estatico o semi-estatico: articulos, documentacion, paginas de producto, FAQs. Para formularios, dashboards o aplicaciones interactivas, el markdown no tiene sentido porque el agente no puede interactuar con los elementos dinamicos. En esos casos, es mejor exponer una [API o endpoint NLWeb](/blog/nlweb-microsoft-protocolo-conectar-web-agentes-ia-2026) que permita al agente consultar los datos directamente.
30 
31### ¿Cuanto cuesta implementar Content Negotiation?
32 
33**Desde cero euros.** Cloudflare Free Plan incluye "Markdown for Agents". El plugin de WordPress de Joost de Valk es gratuito. Implementar un middleware en Next.js o Express es trabajo de desarrollo (1-2 horas para un desarrollador). No hay costes recurrentes: es logica del servidor que no consume recursos adicionales significativos.
34 
35### ¿Content Negotiation y llms.txt son lo mismo?
36 
37**No, son complementarios.** `llms.txt` es un archivo estatico que lista tu contenido (como un sitemap para LLMs). Content Negotiation es un mecanismo HTTP que sirve tus paginas en formato optimizado. `llms.txt` dice "tengo estos articulos"; Content Negotiation dice "te sirvo este articulo en markdown". Son dos capas diferentes de la web legible por maquinas.
38 
39### ¿Puedo ver una demo de Content Negotiation en accion?
40 
41Si. Ejecuta esto en tu terminal:
bash

Pedir HTML (comportamiento normal)

curl -H "Accept: text/html" https://docs.anthropic.com/en/docs/overview

Pedir markdown (si el sitio lo soporta)

curl -H "Accept: text/markdown" https://docs.anthropic.com/en/docs/overview

`

Compara las dos respuestas. La version markdown sera dramaticamente mas corta y limpia. Muchos sitios de documentacion tecnica (Cloudflare Docs, Vercel Docs, Anthropic Docs) ya soportan Content Negotiation.


Conclusion: sirve a tus visitantes en el formato que necesitan

Content Negotiation para agentes de IA es una de esas mejoras que tiene un ratio esfuerzo/impacto excelente. Treinta minutos de trabajo (o un click en Cloudflare) y tu sitio pasa de ser "legible con esfuerzo" a "optimizado para agentes de IA".

Los numeros hablan solos: 80% menos tokens, 80% menos coste, 99% de precision de extraccion. Los agentes que pueden elegir entre un sitio que sirve HTML y otro que sirve markdown van a preferir el segundo. Y en la era donde la mitad de tu "trafico" son agentes de IA, eso importa.

La web legible por maquinas no es una moda. Es la evolucion natural de un ecosistema donde los humanos ya no son los unicos que leen paginas web. Content Negotiation, junto con NLWeb, llms.txt y schema.org, forma la infraestructura que va a definir como se descubre y consume informacion en los proximos anos.

Implementalo hoy. Tus visitantes (los de carne y hueso y los de silicio) te lo agradeceran.

Mas analisis de herramientas y protocolos de IA en Upliora. Para implementaciones de IA en produccion y consultorias tecnicas, visita Javadex.

Recomendado

¿Listo para poner tu proyecto en producción?

Si estás siguiendo este tutorial, necesitas un servidor donde desplegarlo. Yo uso Hostinger para mis proyectos porque el panel es intuitivo, los VPS van con SSD NVMe, y a 4,99€/mes no hay nada comparable en relación calidad-precio.

SSL gratis + IP dedicada
SSD NVMe ultra rápido
Soporte 24/7 en español

* Enlace de afiliado. Si contratas a través de este enlace, nos ayudas a mantener este contenido gratuito.

Posts Relacionados

JS

Javier Santos Criado

Consultor de IA y Automatización | Fundador de Javadex

Experto en implementación de soluciones de Inteligencia Artificial para empresas. Especializado en automatización con n8n, integración de LLMs, y desarrollo de agentes IA.

RECOMENDADO

Lleva tu proyecto a producción

Hosting web desde 2,99€/mes o VPS con SSD NVMe desde 4,99€/mes. Panel intuitivo, IP dedicada y soporte 24/7 en español.

SSL gratis SSD NVMe Soporte 24/7 Panel intuitivo
Explorar planes de Hostinger

¿Quieres más contenido de IA?

Explora nuestras comparativas y guías

Consultoría y formación en IA para empresas

Implementamos soluciones de inteligencia artificial adaptadas a tu negocio. Proceso 100% remoto — trabajamos con tu equipo in-house sin que tengas que desplazarte.

javi@javadex.esSesión de diagnóstico gratuita · 30 min