Google ha dejado de reparar tus datos estructurados. La extracción de JSON-LD aplica ahora una sola pasada de unescaping de HTML, así que un bloque que antes se enderezaba en silencio simplemente no parsea y desaparece. No hay error en Search Console, no hay aviso, solo un resultado enriquecido que deja de salir. Este texto muestra cómo medir tu propio corpus en unos minutos en lugar de adivinar, de dónde sale esa escritura en WordPress y por qué una auditoría puntual no basta. Nuestra propia medición sobre 68 055 bloques está dentro, con el script.
Qué cambió Google en realidad
La declaración es breve y merece cita completa, porque todo lo demás se deriva de una frase:
To bring our parser up to JSON and other standards, we changed our JSON-LD extraction and are now only applying a single pass of HTML unescaping
Gary Illyes añadió dónde está definida la corrección: RFC 8259, la especificación de JSON. No es una recomendación de SEO, es la referencia a un estándar que sigue cualquier parser de JSON.
La consecuencia práctica se enuncia con la misma brevedad: las entidades con doble escape, como & o ✔, ya no se despliegan. Antes el parser hacía una pasada extra y lo enderezaba. Ahora hace una pasada y se queda con texto que no es JSON válido.
Conviene nombrar de entrada lo que esa información no trae. No hay fecha de despliegue. No hay enlace a documentación actualizada de Google. La fuente es una publicación en LinkedIn, recogida por Search Engine Roundtable el 21 de agosto de 2026. Trátalo como un estado que Google describe y no como una especificación que puedas poner delante de un cliente.
Qué es el doble escape y de dónde sale
Toma un caso sencillo: el nombre de empresa «García & Hijos» en el campo name de un bloque JSON-LD.
| escrito como | lo que ve el parser de JSON | estado |
|---|---|---|
"García & Hijos" | García & Hijos | válido, JSON no exige escapar el ampersand |
"García & Hijos" | García & Hijos | válido, escape universal |
"García & Hijos" | García & Hijos | parsea, pero el valor es incorrecto |
"García & Hijos" | García & Hijos | esto es doble escape |
Un ampersand por sí solo no revienta el bloque, porque JSON no exige escaparlo. Lo grave empieza con la comilla. Si la plantilla escribe " donde corresponde \", tras una pasada queda " y no una comilla. La cadena no cierra y el bloque deja de ser JSON.
¿De dónde sale en WordPress? Casi siempre de procesar dos veces el mismo valor. El contenido pasa por esc_html() al guardar, por un filtro del tema al mostrar y acaba en un campo JSON-LD que habría escapado bien por su cuenta. Cada paso por separado es correcto. Compuestos producen una escritura que funcionó durante años solo porque Google la enderezaba por nosotros.
Cómo revisar tu propio corpus en cinco minutos
La regla que más importa: revisas el HTML construido, no la fuente de la plantilla. En la fuente todo se ve bien, porque el escape se añade en la salida. Si publicas un build estático, escaneas el directorio de salida. En un WordPress clásico, bajas una muestra de URLs con wget o curl y escaneas lo que devolvió el servidor.
La comprobación en sí son poco más de diez líneas. Extrae cada bloque <script type="application/ld+json">, intenta parsearlo y busca aparte el patrón de entidad doble:
const RE = /<script[^>]*type=["']application\/ld\+json["'][^>]*>([\s\S]*?)<\/script>/gi;
let m;
while ((m = RE.exec(html))) {
const body = m[1];
if (/&(quot|amp|lt|gt|#\d+);/.test(body)) report("entidad doble", file);
try { JSON.parse(body); } catch (e) { report("no parsea: " + e.message, file); }
}
Dos pruebas separadas, porque cazan cosas distintas. JSON.parse falla con una cadena rota, pero acepta sin problema &amp; dentro de un valor que sigue siendo JSON válido y solo contiene basura. La prueba del patrón caza justo ese segundo caso: el bloque parsea y en el resultado enriquecido aparece & donde debería ir un carácter.
Una tercera prueba que conviene añadir son las entidades HTML simples en los valores. No son un error, pero tras el cambio se despliegan exactamente una vez, así que el resultado puede diferir del que conocías. Mejor saber que están.
Nuestra medición: 68 055 bloques, cero fallos
Pasamos este escaneo por nuestro propio corpus el 30 de agosto de 2026, contra una versión de producción recién construida.
| métrica | resultado |
|---|---|
| páginas con al menos un bloque JSON-LD | 15 742 |
| bloques JSON-LD en total | 68 055 |
| bloques que no parsean como JSON | 0 |
| páginas con entidad de doble escape | 0 |
| páginas con entidad HTML simple dentro del JSON-LD | 0 |
Cero en las tres categorías. No lo escribimos como alarde, sino como información sobre qué significa ese resultado y qué no. Nuestro stack genera datos estructurados desde el frontmatter mediante componentes de Astro y los inserta con el patrón set:html={JSON.stringify(...)}. JSON.stringify produce JSON válido por definición y set:html no añade escape de HTML propio. Dicho de otro modo: no sacamos cero por ser cuidadosos, sino porque ese patrón concreto no tiene manera de producir doble escape.
Eso importa más que la cifra. Si tu stack compone el JSON-LD concatenando cadenas en la plantilla, o mediante un plugin que pega un campo de contenido dentro de un JSON preparado, el riesgo es real y tu resultado será distinto. Mide el tuyo, no copies el nuestro.
Dónde se rompe más a menudo en WordPress
De las auditorías que hacemos en clientes salen tres fuentes recurrentes.
La primera es un plugin de SEO que rellena description con un campo que ya pasó por wp_kses o esc_attr. Suele romper en el apóstrofo y en las comillas, y en castellano se suman las comillas angulares y los signos de apertura que muchos gestores insertan de forma automática.
La segunda es un bloque JSON-LD escrito a mano y pegado en header.php o en las opciones del tema, donde los valores entran con echo y sin wp_json_encode. Es la variante habitual en temas a medida construidos hace años y la más difícil de encontrar, porque no aparece en ninguna pantalla de plugin.
La tercera es un maquetador visual que guarda el contenido con entidades HTML ya en la base de datos. Entonces incluso un generador de JSON-LD bien escrito recibe texto con & y lo codifica obedientemente una segunda vez.
El denominador común es siempre el mismo: un valor escapado dos veces, una para HTML y otra para JSON, por dos capas que no saben la una de la otra.
Cómo codificarlo bien
La regla es inequívoca porque existe un estándar para ella. Dentro de un valor JSON escapas a la manera de JSON, no a la manera de HTML.
En PHP eso significa wp_json_encode() sobre la estructura completa, nunca cadenas montadas a mano. En JavaScript, JSON.stringify(). En una plantilla de Astro, el patrón que usamos nosotros:
<script type="application/ld+json" set:html={JSON.stringify(schema)} />
Si de verdad necesitas un carácter que podría cerrar el bloque de script antes de tiempo, usa un escape universal. & y < son seguros en cualquier parser de JSON y no exigen conocimiento de HTML a quien lea los datos.
Lo que no hay que hacer: no metas una entidad HTML en un valor JSON esperando que alguien la despliegue. Durante años ese alguien fue Google. A partir de ahora la despliega exactamente una vez, y cualquier otro consumidor de tus datos estructurados, de Bing a un asistente de IA, nunca tuvo esa obligación.
Una auditoría puntual no basta, conviértela en una puerta
Esta es la parte que más se salta. Los datos estructurados no son un texto que se escribe una vez. Los genera una plantilla, un plugin o una integración, y cualquier actualización puede devolver el escape. Una auditoría de hoy habla del build de hoy y de nada más.
Nosotros convertimos el escaneo en una puerta que corre después del build. Lee el directorio de salida, cuenta bloques, intenta parsear cada uno y falla solo ante un problema real, es decir, un bloque que no parsea o una entidad doble. Si falta el directorio de salida termina en cero, para no producir un rojo falso en un entorno donde nadie ha construido todavía.
Tres detalles deciden si esa puerta vale algo. Primero, tiene que leer el artefacto y no la fuente, porque la fuente no demuestra nada sobre el escape. Segundo, tiene que contar bloques y mostrar la cifra, para que alguien note que cae de sesenta y ocho mil a doscientos porque una integración dejó de generarlos. Tercero, tiene que separar el error del aviso: las entidades HTML simples se reportan pero no tumban el build, porque no son una avería sino algo que conviene saber.
Qué hacer cuando el escaneo encuentra algo
Un escaneo da una lista de archivos, no un diagnóstico. Antes de corregir nada, averigua qué capa produce esa escritura, porque un arreglo en el sitio equivocado vuelve con la siguiente actualización.
Coge una URL de la lista y mira la respuesta cruda del servidor, por ejemplo con curl -s URL | grep -A5 "application/ld+json". Comprueba si el valor dañado viene del título de la entrada, de la descripción SEO o de un campo personalizado. Eso señala la capa más rápido que leer código.
Después decides según el origen:
- Si el valor viene de un plugin de SEO, comprueba si tienes dos plugins generando el mismo tipo de schema. Un generador duplicado causa valores raros con más frecuencia que un fallo en cualquiera de los dos por separado.
- Si el bloque está en el tema, reescríbelo con
wp_json_encode()sobre el array completo. La concatenación manual de cadenas es aquí el único error verdadero y no admite parches parciales. - Si las entidades ya están en la base de datos porque las puso un maquetador, no lo arregles en el generador. Decodifica el valor una vez antes de pasarlo al codificador JSON, por ejemplo con
html_entity_decode()con la bandera de comillas y UTF-8 explícito.
El error clásico se ve así:
echo '{"name":"' . esc_html( $title ) . '"}';
Lo correcto se ve así:
echo wp_json_encode( array( 'name' => $title ) );
La diferencia no está en el número de caracteres, sino en quién se encarga del escape. En la primera versión lo hace una función de HTML en un contexto que no es HTML. En la segunda lo hace un codificador JSON en contexto JSON.
Tras el arreglo, escanea un build nuevo y no el artefacto viejo. Suena obvio, y la mitad de los avisos de «lo arreglé y sigue roto» son un escaneo sobre un directorio de salida caducado.
Qué pierdes de verdad si lo ignoras
Perder schema no duele de inmediato, y eso es lo peor. No hay caída de posiciones de un día para otro, hay una desaparición gradual de lo que destacaba el resultado: estrellas de valoración, datos de producto, una lista de preguntas frecuentes, migas de pan. El efecto lo ves en el CTR y no en la posición, y el CTR baja despacio y es fácil atribuirlo a otra cosa.
El segundo destinatario de estos datos es más nuevo y menos indulgente. Los motores de respuesta leen datos estructurados porque es la vía más barata para establecer de qué va una página sin interpretar todo el texto. En nuestro sitio el tráfico de agentes ya es medible y no marginal: del orden de cien visitas diarias, dos tercios a través de nuestro propio endpoint MCP. Esos sistemas no tienen motivo para reparar el escape ajeno. Google lo reparó durante años por cortesía con la web que se encontró. Un consumidor nuevo de tus datos nunca tuvo esa costumbre.
La tercera capa es Search Console, y aquí toca decir qué no vas a averiguar. Los informes de resultados enriquecidos mostrarán una caída de elementos válidos, pero no dirán «este bloque no parseó por una entidad doble». Verás que faltan elementos y tendrás que deducir por qué. Por eso el escaneo en tu lado vale esos minutos: da la causa y no solo el síntoma.
Lo que no sabemos y qué hacer igualmente
No conocemos la fecha de despliegue. No sabemos si el cambio afecta por igual a todos los tipos de schema, ni si Search Console llegará a reportar una entidad perdida o simplemente dejará de mostrar el resultado enriquecido. No hay documentación actualizada a la que remitir a un cliente. Son huecos reales y es mejor decirlo que dar a una publicación de LinkedIn el rango de una especificación.
Pese a los huecos, la decisión operativa es sencilla y no depende de ninguno de los datos que faltan. El JSON válido ya era válido cuando Google corregía los errores por ti. Escanea tu HTML construido, repara lo que no parsea, sustituye las entidades HTML de los valores por escapes JSON y ancla la prueba en el proceso para que no vuelva. Si el resultado sale a cero, como el nuestro, también es un resultado: sabes que tu generador no tiene forma de producir este fallo y dejas de pensar en ello con cada actualización de plugin.
El mayor riesgo de esta historia no está en el cambio, sino en que es silencioso. No llega ninguna alerta. El resultado enriquecido deja de aparecer y tres meses después un informe muestra una caída de visibilidad sin causa evidente. Cinco minutos de escaneo hoy salen más baratos que esa investigación.





