Guía Avanzada sobre renderizado

Aviso al lector:
Este es un artículo avanzado sobre renderizado en el SEO. Si no sabes qué es el renderizado, te RECOMIENDO leer la guía básica de renderizado en el SEO antes.
El renderizado en el mundo web consiste en el proceso de convertir el código a píxeles para que sea entendido por humanos. Es decir, los humanos no procesamos código en tiempo real, dicho código se transforma en píxeles que nos permiten entender visualmente lo que quiere decir una web. Por lo que a ese proceso de transformación y entendimiento que hacen los navegadores del código HTML, CSS y JavaScript (son los lenguajes que por estándar entienden todos los navegadores), se llama renderizado.
En cuanto a SEO, los motores de búsqueda y los LLMs no necesitan convertir el código en un aspecto visual para comprender el contenido, sin embargo deben hacer una interpretación para poder procesar el contenido y "entender" si sería relevante para un humano.
El renderizado es una de las partes más complicadas de entender dentro del mundo del SEO, y esto se debe a que debes tener una base muy sólida y clara acerca de como funciona una web, pues un mal renderizado puede provocar que un contenido de gran calidad no tenga un buen posicionamiento o ni siquiera sea considerado para la indexación.

Es inevitable pensar en JavaScript cuando se menciona el renderizado dentro del SEO. Pero el renderizado es muchísimo más e intervienen muchísimos elementos para ese proceso.
Desde el servidor siempre se le envían distintos recursos al navegador, HTML, CSS, JS y distintos tipos de archivos multimedia.
El resultado de todo esto en conjunto es lo que construye una página final y en última instancia como se renderiza en un navegador (y en motores de búsqueda).
Así que veamos en profundidad recursos distintos al JavaScript que nos puede afectar en la renderización final.

El servidor es una parte clave de una página web. Hay un "pre-proceso" de renderizado por parte del lenguaje de programación del servidor para mandar un HTML resultante.
La clave está aquí, ya que con ciertos ajustes con un lenguaje de servidor como puede ser PHP (también se puede hacer en webs con Python, Java o Ruby) podemos modificar el contenido del HTML final antes de ser enviado. De forma que tanto el usuario, como los motores de búsqueda, como los LLMs verán exactamente el contenido que nosotros queramos.
¿Qué estoy queriendo decir? Que hay un truco que podemos solicitar los SEOs a los programadores cuando estos nos digan "La implementación que pides no se puede hacer en el código de esta página".
A este truco se le llama modificar el "output buffering". Lo que se hace es "retener" el HTML final, el que se le iba a enviar al usuario y hacer modificaciones necesarias ANTES de que le llegue al usuario.
Gracias a este tipo de implementación, podemos conseguir cambios útiles para SEO mucho más limpios que cuando se hacen con JavaScript.
El motivo por el que es más limpio, es porque cuando se hacen modificaciones con JavaScript lo que hacemos desde el servidor es mandarle al usuario un HTML que modificamos en tiempo real en su navegador. Es decir, en principio se "imprimiría" el código inicial y después las modificaciones que queramos realizar, si sigues leyendo el artículo, entenderás por qué esto puede ser un problema.
Para ir al grano y no desviarnos, ¿Qué podemos hacer gracias al Buffer?
Es decir, con esta práctica, independientemente de la lógica de la página podemos eliminar JS y CSS de páginas donde no se vayan a usar dichos elementos (y mejorar de dicha forma la velocidad de nuestra web), modificar encabezados o generar un sistema de enlazado automático mediante CTA rápidamente y de forma automática.
Lo que nos permite es hacer cualquier cambio que podríamos hacer con JavaScript (exceptuando la interacción) y que le aparezca directamente al usuario en su HTML inicial, como si nunca hubiera habido un cambio, como si siempre hubiera estado ahí.
Aquí tenemos un ejemplo que funcionaría en WordPress, Drupal, Prestashop, Laravel o PHP Vanila
<?php
ob_start(function ($buffer) {
$is_home = ($_SERVER['REQUEST_URI'] == '/' || $_SERVER['REQUEST_URI'] == '/index.php');
if ($is_home) {
$buffer = preg_replace_callback(
'/<h1([^>]*)>/i',
function ($matches) {
$attributes = $matches[1];
if (preg_match('/class=["\']([^"\']*)["\']/', $attributes, $classMatches)) {
$existingClasses = $classMatches[1];
$newClasses = trim($existingClasses . ' buffering-is-working');
$attributes = preg_replace(
'/class=["\'][^"\']*["\']/',
'class="' . $newClasses . '"',
$attributes
);
} else {
$attributes .= ' class="buffering-is-working"';
}
return '<h1' . $attributes . '>';
},
$buffer
);
}
return $buffer;
});
register_shutdown_function(function () {
if (ob_get_length() > 0) {
ob_end_flush();
}
});
?>
Así que esto significa que por complicada que sea la web, podemos conseguir cambios de una forma rápida y eficiente, permitiéndonos eliminar elementos a voluntad. Basta de ser cutres con el clásico "display:none" o eliminandolos del DOM por medio de un parche con JavaScript.
Además esto nos permite hacer cambios en masa por toda la web, acelerando considerablemente las implementaciones.
Si este tipo de implementación se hace junto a una buena implementación de caché, puede ahorrar mucho tiempo permitiendo implementaciones rápidas, incluso aunque la web utilice sistemas drag and drop como Elementor o Divi.
Ni Google ni los usuarios lo experimentarán como si hubiera habido un cambio, simplemente el HTML que les llegará lo hará con los cambios realizados.
En cualquier caso, como he mencionado, el cambio de última instancia en el DOM (en tiempo real en su navegador), a pesar de la UX, puede funcionar para modificar el contenido siempre que el código HTML original permanezca indexable.
Esta es también la razón por la cual ciertas configuraciones SEO, como los datos estructurados o incluso una directiva noindex, pueden implementarse exitosamente a través de Google Tag Manager (GTM).
Dado que Google ejecuta JavaScript durante su fase de renderizado, cualquier cambio que afecte el HTML final renderizado — incluyendo meta tags, JSON-LD o actualizaciones de canonical — será visto por el Web Rendering Service (WRS) de Google, siempre que ocurra dentro de la ventana de renderizado de 5 segundos.
Cuando pensamos en cuestiones avanzadas como podría ser la renderización, tendemos a poner el foco en lo difícil y olvidarnos de lo básico.
Lo básico no implica solo que algo sea "sencillo", sino que es la base, y el contenido de nuestra web está estructurado en los cimientos que son el HTML, así que para evitar problemas de renderizado tanto por parte de los navegadores, como en última instancia de los rastreadores de motores de búsqueda y LLM, tenemos que respetar los estándars.
Además te contaré algunos trucos para tener en cuenta:
El HTML que lanza el servidor, no tiene por qué ser el mismo que ve el usuario, ya que el JavaScript hace modificaciones.
Sin embargo el HTML inicial que lanza el servidor es muy importante de cara al renderizado.
Por ejemplo, ese HTML debe estar indexable (etiqueta index y self-canonical o nada de canonical), ya que si Google considera que la página no es indexable al rastrearla, no llevará dicha página al proceso de renderizado, la considerará directamente no indexable, aunque luego el javascript cambie el contenido de la canonical o de la meta de la indexación.
Sin embargo, el proceso al revés si funcionaría. Puedes marcar la página como indexable y si luego con JS se modifica para no ser indexable, la página acaba siendo no indexada.
Así que basados en ese principio, tenemos que conseguir a toda costa que el HTML inicial se considere indexable.
Así que debemos evitar la palabra "404" en el título o h1 (aunque hablemos de los 404 mejores consejos sobre cualquier temática), ya que es posible que automáticamente se detecte la página como "soft 404" y se considere no indexable, por lo cual nunca llegaría al proceso de renderizado.
También hay otra cuestión de HTML básica que debemos tener en cuenta tanto en el HTML inicial como final, y es que en la etiqueta <head> solo se deben poner etiquetas de HTML válidas dentro del head, sino cualquier navegador (incluido headless Chromium, el que usa Google), cerrará la etiqueta head de forma automática.
No, no me he vuelto loco, el CSS afecta al renderizado.
Si bien (sin obsesionarse) hay que tener en cuenta los elementos no visibles u ocultos que se muestran por medio de la interacción (por eso recomiendo utilización de summary details en HTML con respecto a otras configuraciones de "Click y mostrar/ocultar" que se pueden hacer con JS, ya que tenemos una opción nativa que sirve para eso mismo compatible en todos los navegadores. Hay otras cuestiones de CSS que nos pueden afectar mucho, en concreto respecto a Google.
Google cuando renderiza, no hace scroll ni interactúa, lo hace con una simulación de dispositivos gigantes, tanto en el móvil como en el ordenador.
De hecho lo hace con alturas exactas. En el móvil lo hace con exactamente 1750px de alto y en el ordenador con 10.000 píxeles de alto. Algo que provoca un fallo muy común en las webs. El fallo del vh y dvh en el "hero" (el hero es el área de la parte superior de una web que actua como primera impresión visual, donde suelen haber sliders, vídeos...) .
Cuando en el "hero" se marca 100vh (una medida para marcar todo lo alto del dispositivo) sin ningún max-height, Cuando lo renderiza Google genera un problema muy grande, ya que ese "hero" pasa a ocupar todo el "main content", generando un problema de renderizado en el sentido de que el verdadero main content quede oculto y no se considere como la parte relevante de la página.
Pudiendo ocasionar que Google considere tu contenido como "Rastreada: actualmente sin indexar".
Esto provoca lo conocido como "thin content". Si bien en una marca muy consolidada y no suele generar tanto problema cuando aparece en la home debido a que por la autoridad, Google lo puede seguir considerando relevante (la página de amstel.es posiciona primera por marca a pesar de estar bloqueada por un robots.txt).
Tener un "100vh" sin un max-height puede ocasionar que productos o PLP no sean relevantes e incluso no indexadas ya que el "main content" real está por debajo de su captura.
Aquí os puedo mostrar como ocurre por ejemplo en la página de Pandora. Si no tenemos acceso a la Search Console, podemos ver como renderiza Google usando la página de prueba de resultados enriquecidos.

También hay trucos para poner contenido en texto con CSS sin que cuente como texto (lo inverso a un display none o visibility hidden, pero que además funciona). Siempre tienes la opción de insertar contenido que se consideraría como decorativo por medio de pseudoelementos como ::before o ::after que no será contabilidazo como texto.
No se consideraría texto de cara al renderizado, aunque así se viese visualmente. Sin necesidad de un data-nosnippet, tampoco aparecería en los resultados enriquecidos.
El WPO también puede afectar al renderizado.
Martin Splitt confirmó que Google tarda 5 segundos en hacer el snapshot (para capturar los cambios principales del DOM).
Yo mismo he hecho comprobaciones en varios proyectos con un contador de JavaScript en metaetiquetas inventadas. La respuesta que da Google son siempre 5 segundos exactos en hacer el rendering.
Esto quiere decir que TODO lo que tarde más de 5 segundos en aparecer por medio de JavaScript, son elementos que Google no verá. (Espero que no seáis malos y no aprovechéis esto para hacer una suerte de "cloacking")

Al margen de la metodología que utilicen los navegadores, los motores de búsqueda e incluso los LLM para renderizar. Nuestra web también utiliza distintas metodologías para servir su contenido, y por tanto renderizarlo.
En las webs clásicas no suele haber mucho misterio, pero a día de hoy hay múltiples formas de servir contenido, y algunas de ellas son más "SEO-Friendly" que otras.
Curiosamente las páginas que más se ven afectadas por cuestiones de renderizado son las webs hechas en algún framework de JavaScript.
Independientemente de que uses Next, React, Angular, Vue o similares, estos son los tipos de renderizados más comunes en el mundo:
| Tipo | Descripción | Ventajas SEO | Consideraciones |
|---|---|---|---|
| CSR | Renderiza en el navegador con JS | Flexible, rápido si se gestiona bien | Riesgo de contenido vacío para Google si falla JS |
| SSR | Renderiza en el servidor en cada petición | Contenido visible e indexable desde el inicio | Carga al servidor; requiere hidratar para SPA |
| Renderizado dinámico | CSR para usuarios y SSR para bots | Solución rápida para LLMs | Obsoleto y desaconsejado por Google |
| Híbrido | Combina CSR, SSR y SSG según el caso | Muy flexible según contenido | Complejo de implementar y mantener |
| SSG | Genera HTML estático en build | Muy rápido y eficiente | No sirve para contenido dinámico |
| DSG | Renderiza bajo demanda y cachea | Velocidad de la web | Menos personalizable que ISR |
| ISR | Renderiza estáticamente y revalida al acceder | Velocidad de la web, escalabilidad y personalización | Mejor opción si se puede elegir en un proyecto grande |
Tienes que tener en cuenta que los LLM y otros motores de búsqueda no renderizan JavaScript, pero Google sí, y cuando Google lo hace, debes tener en cuenta que Google renderiza el JS, pero no interactua con él ni hace Scroll.
No diría que un CSR no vale para SEO ni mucho menos, sobre todo si lo que te enfocas es en Google. Pero hay que hacer un esfuerzo extra, especialmente enfocado en la renderización.
Es imperativo recordar que todas las páginas deben ser indexables antes de renderizarse en JS (un fallo muy común es poner canonicals hacia la home que luego se modifican en el DOM, pero es un error ya que de inicio solo sería indexable la home).
Si has llegado hasta este punto del artículo, ya tienes una muy buena base sobre el renderizado. Así que ahora vamos a sintetizar los puntos más importantes para conseguir el mejor resultado cuando trabajemos el SEO.
Google primero rastrea el contenido sin JavaScript (es mucho más eficiente). Luego tiene un microservicio que es el "WRS" Web rendering Service que con el contenido cacheado de las páginas, utilizando headless Chromium realiza el renderizado de las páginas.
Esto quiere decir que el momento del renderizado y del rastreo no es el mismo, y que con logs no veremos en nuestra web el momento en el que realiza el renderizado.
Yo recomiendo poner unas metaetiquetas inventadas donde me sirven:

De esta forma puedo trackear exactamente el timing del comportamiento en mi web cuando inspecciono una URL desde la Search Console.
Para auditar el renderizado de Google, yo recomiendo utilizar Search Console para proyectos propios y el test de rich snippets para proyectos donde no tengo acceso a la Search Console, es lo más exacto. Aunque otras herramientas nos pueden ayudar
<meta name="user-date" content="<?php echo $date;?>">
<meta name="user-date-js" content="<?php echo $date;?>">
<meta name="snapshot-counter" content="0">
Código PHP para la fecha del servidor:
<?php
$date = date('d/m/Y H:i:s');
date_default_timezone_set('Europe/Madrid');
?>
Código JS:
Para la fecha:
function updateMetaDate(){const metaTag=document.querySelector('meta[name="user-date-js"]');if(metaTag){setInterval(()=>{const now=new Date();const formattedDate=now.toLocaleString('es-ES',{year:'numeric',month:'2-digit',day:'2-digit',hour:'2-digit',minute:'2-digit',second:'2-digit'}).replace(',','');metaTag.setAttribute('content',formattedDate)},1000)}}
document.addEventListener("DOMContentLoaded",updateMetaDate)
Para verificar el tiempo del snapshot:
document.addEventListener('DOMContentLoaded', () => {
const meta = document.querySelector('meta[name="snapshot-counter"]');
let count = parseInt(meta.getAttribute('content')) || 0;
const intervalId = setInterval(() => {
count++;
meta.setAttribute('content', count.toString());
console.log('time a:', count);
const currentValue = meta.getAttribute('content');
console.log('Dom value:', currentValue);
}, 1000);
window.snapshotIntervalId = intervalId;
});
Estos ejercicios nos sirven para probar la salud del renderizado según Google de nuestras páginas. Es decir, comprobar la fecha de rastreo, la de renderizado y el tiempo que se tarda en renderizar en nuestra página (deberían ser 5 segundos ).
Esta herramienta nos sirve para comprobar el renderizado, tanto el DOM, como la respuesta del servidor como el aspecto visual.
No es lo mismo la "inspección de URL" que "probar URL publicada". (Es conveniente recordar que puedes "probar la URL publicada" de cualquier web con el comprobador de datos estructurados).
La inspección de URL nos sirve para comprobar que vio Google exactamente, mientras que el "live test" o "probar la URL publicada" nos sirve para comprobar como se vería (en teoría) si pasase el crawler. También para comprobar el aspecto visual.
La "captura de pantalla" nos sirve a nosotros como humanos para comprobar como se vería y cómo lo interpreta. El hecho de que no llegue hasta el footer, es que no lo necesita para el renderizado, Google verá todo, pero nos muestra lo que considera una estimación del main content. El main content tampoco debería estar tan abajo en teoría.
Esta herramienta además de para el renderizado nos puede servir para comprobar "paywalls" y "age gates".
Recuerda que la Search Console no utiliza exactamente el mismo rango de IPs que Googlebot para su ranking, por lo que es posible que cuando tengas el live-test te salga algo distinto a cuando inspeccionas una URL.
Los LLMs convencionales (chatgpt, claude, perplexity) a día de hoy son capaces de renderizar JavaScript.
¿Pero Bing? ¿Renderiza JavaScript?
Bueno, desafortunadamente no es tan fácil como una respuesta de sí o no.
"JavaScript: Bing puede procesar JavaScript, sin embargo, existen limitaciones para procesar JavaScript a escala mientras se minimiza el número de peticiones HTTP. Bing recomienda el Renderizado Dinámico para alternar entre contenido renderizado del lado del cliente y contenido pre-renderizado para user agents específicos como Bingbot, especialmente en sitios web grandes." — Bing Webmaster
Como podemos ver, las directrices de Bing afirman que renderizan JavaScript, pero también recomiendan el renderizado dinámico para servir a Bingbot una versión renderizada del lado del servidor.
Eso es sospechoso: si realmente pueden renderizar JavaScript, ¿por qué necesitarían un respaldo pre-renderizado?
Siempre he tenido problemas con Bingbot en sitios CSR grandes, así que ejecuté la misma prueba que hice con Google. Para mi sorpresa (en realidad no), los cambios de JavaScript no aparecieron en el DOM, y obtuve los mismos resultados en otros proyectos.

También podemos ver esto con proyectos enormes, incluso si tienen buenas métricas:

Así que he elegido la home del sitio para mostrarte el renderizado:

Incluso podemos ver que no hay renderizado en el índice de Bing (parte superior de la imagen). El informe del índice dice que no tengo canonical, metadescription ni h1. Pero en realidad sí los hay, solo que es un sitio CSR.
Cuando vas al test en vivo (parte inferior de la imagen) ya aparece correctamente (a pesar de algunos alts que siguen faltando).
Así que incluso en los informes puedes comprobar que Bing Webmaster muestra reportes falsos simplemente porque no renderizan JavaScript por defecto.
En mi opinión, no renderizan JavaScript o al menos no lo hacen correctamente ni de forma avanzada como lo hace Google.
Por lo que las páginas que no sirvan ningún tipo de contenido en CSR se pierden el coste de oportunidad de aparecer en estas plataformas, al menos de una forma eficiente.
Una opción viable, especialmente de cara a los LLMs en el caso de querer tener una web con framework de JS sin una versión optimizada en SSR o derivados sería la opción de "renderizado dinámico".
Seleccionando sólo los user-agents de los LLMs pensados para servirles dicho contendio.
Hay que tener cuidado que no todos los LLMs utilizan user-agents reconocidos, por ejemplo, Grok usa el user-agent de Iphone pero sin JS.
Te falta mi máster. Accede a una formación avanzada que te permitirá aplicar e implementar SEO en cualquier tipo de WEB
¡Accede al Máster de SEO Técnico!