Metadatos de Next.js en body: nueve condiciones probadas
Neaptide · 12 de octubre de 2026 · 8 min de lectura
Probamos Next.js 15.5.22 en nueve condiciones: HTML recibido, DOM del navegador y ubicación de canonical. Resultados y límites para SEO.
En este artículo

Encontrar metadatos después del cierre de head puede deberse al streaming de Next.js. En nuestra aplicación local compilada para producción, los metadatos asíncronos aparecieron en body con dos perfiles de petición y en head con el tercero. Los valores estaban presentes en todas las respuestas completas, pero eso no demuestra que un buscador acepte cada etiqueta en esa ubicación.
Identifica primero la etiqueta, el perfil de petición y la representación del documento que estás revisando. El experimento explica cómo se entregan los metadatos. Para canonical, aplica también el requisito de Google de situarlo en head: encontrar la URL correcta en cualquier parte del documento no valida la implementación.
Qué probamos
El 12 de octubre de 2026 probamos Next.js 15.5.22 con React y React DOM 19.2.6 y Node v25.2.1. Tres modos y tres perfiles HTTP User-Agent produjeron nueve condiciones, repetidas dos veces. Conservamos 18 respuestas HTTP 200 completas, sus cabeceras y su HTML. Las dos rondas coincidieron en cantidad, valores y ubicación de las etiquetas.
Comparamos un objeto metadata estático, una función generateMetadata asíncrona con la configuración predeterminada y la misma función con htmlLimitedBots: /.*/. Ambas rutas usaban force-dynamic. Estático se refiere al modo de declarar los metadatos, no a una página prerenderizada. La función asíncrona esperaba 250 ms de forma deliberada, sin peticiones externas. Esa espera permite observar la entrega, pero no representa una carga real ni una puntuación de rendimiento.
La respuesta HTML varió según el perfil

| Modo de metadatos | UA navegador | UA Googlebot | UA Twitterbot |
|---|---|---|---|
| Objeto · predeterminado | head | head | head |
| Async · predeterminado | body | body | head |
| Async · bloqueante | head | head | head |
El colector usó una cadena similar a Chrome 134, Googlebot/2.1 o Twitterbot/1.0. Son valores de cabecera, no visitas de esos servicios. Todas las peticiones usaron Accept: text/html y Accept-Encoding: identity, sin cookies ni cabeceras de navegación del cliente. Las cadenas exactas están en los resultados descargables.
El navegador mantuvo las etiquetas async en body
También cargamos los tres modos en el navegador conectado, cuyo User-Agent recibido indicaba Chrome 154. En el DOM, title, description y canonical estaban en head en los modos estático y bloqueante. Permanecieron en body con el modo async predeterminado: no observamos un traslado a head. Estas tres cargas son observaciones distintas de las 18 peticiones HTTP y no simulan el renderizado de Googlebot.
| Modo | DOM: las tres etiquetas |
|---|---|
| Objeto · predeterminado | head |
| Async · predeterminado | body |
| Async · bloqueante | head |
Canonical requiere una decisión propia
Next.js documenta la entrega de metadatos mediante streaming en body y la entrega bloqueante para bots con capacidades HTML limitadas. Esto explica el patrón que reprodujimos. Sin embargo, Google indica que el enlace HTML canonical se acepta en head; su ejemplo JavaScript también lo añade a document.head. La explicación general del framework no resuelve este requisito específico.
En nuestro modo async predeterminado, canonical permanece en body tanto en la respuesta como en el DOM. No podemos concluir que Google lo acepte o rechace: la aplicación era local, incluía noindex y no se envió a indexar. Comprueba la ruta publicada. Si canonical sigue fuera de head, utiliza una implementación que cumpla la ubicación documentada por Google y repite la verificación. La configuración bloqueante de abajo consiguió head en el experimento; no demuestra una mejora de posiciones.
Cómo revisar tu propia ruta
- Guarda la respuesta completa de la URL en producción, las cabeceras de petición y la versión desplegada. Revisa elementos reales title, description y canonical; una cadena en un script React no es otra etiqueta HTML.
- Comprueba el DOM renderizado por separado: valores, duplicados y pertenencia a head o body. Un título de pestaña correcto no valida canonical.
- Compara los perfiles relevantes para el servicio que falla. Separa navegador, cadena de bot y petición real del servicio externo: cambiar User-Agent solo prueba cómo el servidor selecciona la respuesta.
- Aplica el requisito de la etiqueta concreta. Para canonical, revisa la página publicada con las herramientas de Google disponibles y sus datos de canonicalización, asegurando que el enlace HTML esté en head. Un DOM local no muestra qué canonical seleccionó Google.
- Cambia la parte necesaria, recompila y repite las mismas comprobaciones. Si modificas el streaming, evalúa también la carga con tráfico y datos reales; nuestro retraso artificial no estima el rendimiento de producción.
La configuración que probamos
Para el tercer modo recompilamos la misma aplicación con esta opción. Las tres etiquetas aparecieron en head con todos los perfiles probados. La opción sustituye la regla de bots predeterminada; /.*/ coincide con cualquier User-Agent. Conserva los demás ajustes y evalúa el alcance antes de aplicarla.
// next.config.mjs
export default {
htmlLimitedBots: /.*/,
};Esta opción cambia la entrega de metadatos en toda la aplicación. No probamos una expresión limitada a bots concretos, cachés de producción ni navegación entre rutas. El experimento no justifica pasar todos los sitios al modo bloqueante.
Repite el experimento
Descarga la aplicación y su archivo de bloqueo de dependencias. El README incluye las órdenes de compilación y arranque de ambas configuraciones, seguidas del colector Python. Inicia los servidores en dos terminales y ejecuta el colector desde la raíz extraída. Mantén el experimento en local: contiene una espera artificial, noindex y canonical con example.test. El archivo de evidencias incluye HTML original y capturas del DOM; results.json contiene perfiles completos y observaciones procesadas.
Consulta las evidencias
- Protocolo, resultados y fuentes · JSON
- Método y límites · TXT
- Aplicación reproducible · ZIP
- Respuestas HTTP y DOM originales · ZIP
Probamos una versión, dos compilaciones locales y tres cargas en navegador. No probamos CDN, robots remotos reales, navegación del cliente, fallos de metadatos ni Cache Components. No medimos indexación, posiciones, citas de IA, tráfico, conversiones ni Core Web Vitals. La misma persona recogió y verificó los datos. Revisa el comportamiento de tu versión y despliegue.