SEO en Next.js: qué renderizado usar y qué falla

SEO en Next.js: qué renderizado usar y qué falla

Next.js no es solo una forma más rápida de construir en React: es un conjunto de decisiones sobre cuándo y dónde se genera el HTML de cada página, y esas decisiones determinan si un rastreador ve tu contenido de inmediato o tiene que ejecutar JavaScript primero para encontrarlo. Esa elección se hace ruta por ruta, no una vez para todo el sitio, y es más determinante para el SEO que cualquier ajuste que hagas después en el contenido.

Este artículo no repite los fundamentos del renderizado en JavaScript; eso ya está cubierto en otro lugar. Aquí nos quedamos en lo específico de Next.js: qué modo elegir para cada tipo de página, dónde cambia el comportamiento entre el App Router y el Pages Router, cómo se gestiona el metadata por ruta, y los fallos concretos que aparecen una y otra vez en proyectos reales.

Los modos de renderizado, en lo que de verdad importa: qué ve el rastreador

Next.js ofrece varias formas de generar el HTML de una página, y la diferencia entre ellas no es de rendimiento sino de cuándo existe el contenido. Esa distinción es la que le importa a un motor de búsqueda.

La regla práctica es sencilla: si el contenido solo existe después de que se ejecute JavaScript en el cliente, le estás pidiendo al rastreador que haga un trabajo extra para encontrarlo. Google puede ejecutar JavaScript, pero lo hace en una segunda pasada de renderizado que no es inmediata ni gratuita, y otros rastreadores —incluidos los que alimentan resúmenes o asistentes de IA— muchas veces ni siquiera llegan a esa segunda pasada. Para cualquier página que dependa de tráfico de búsqueda, la pregunta por cada ruta no es "qué tan rápido carga" sino "¿existe ya el contenido en el HTML que se sirve, o depende de que el cliente lo construya?".

  • Generación estática (SSG): el HTML se construye en el momento del build y se sirve tal cual, igual a todas las visitas hasta que se vuelve a desplegar. Es el escenario más simple para un rastreador: el contenido ya está ahí cuando llega la petición.
  • Renderizado en servidor (SSR): el HTML se construye en cada petición, en el servidor. El rastreador recibe contenido completo igual que con SSG, pero el coste lo paga tu servidor en cada visita, no una vez en el build.
  • Regeneración incremental (ISR): una mezcla entre las dos anteriores. La página se sirve como estática pero puede revalidarse en segundo plano después de un tiempo o de un evento, sin necesidad de rehacer el build completo.
  • Renderizado en cliente (CSR): el servidor entrega un HTML mínimo y el contenido real aparece después, cuando el navegador ejecuta el JavaScript. Aquí es donde se juega la mayor parte del riesgo de SEO.

App Router y Pages Router no dan las mismas respuestas

Next.js convive hoy con dos sistemas de enrutado con comportamientos de renderizado distintos: el Pages Router, el original, basado en páginas dentro de la carpeta pages, y el App Router, basado en la carpeta app, con React Server Components. Esto importa para el SEO por una razón muy concreta: gran parte de lo que encuentras buscando cómo mejorar el SEO en Next.js se escribió para uno de los dos sistemas y ya no aplica igual al otro, y muy pocos artículos lo aclaran.

Si estás leyendo documentación, un tutorial o una respuesta de foro sobre SEO en Next.js, lo primero que conviene verificar es a qué router se refiere. Si tu proyecto usa el App Router y la fuente no lo menciona, o usa terminología del sistema antiguo sin más contexto, es probable que estés aplicando una solución pensada para un sistema distinto al tuyo. Eso rara vez falla de forma ruidosa: simplemente el resultado no es el esperado, y cuesta encontrar por qué.

  • En el Pages Router, el modo de renderizado se decide función por página: una función para estático, otra para servidor, exportadas desde el propio archivo de la página.
  • En el App Router, el modelo por defecto es distinto: los componentes se renderizan en servidor salvo que se marquen explícitamente como componente de cliente, y el propio framework decide gran parte de la estrategia de cacheo salvo que la fuerces con opciones explícitas.
  • Una guía sobre metadata, sitemaps dinámicos o generación estática escrita antes de que existiera el App Router puede describir funciones que ya no existen en el sistema nuevo, o que existen con otro nombre y otro comportamiento.

Metadata por ruta: título, descripción, canonical y Open Graph

Next.js permite definir metadata a nivel de cada ruta en lugar de tener una sola plantilla para todo el sitio, que es justo lo que necesitas para que cada página tenga su propio título y descripción en los resultados de búsqueda.

El fallo más común aquí no es técnico sino de plantilla: reutilizar la misma lógica de metadata para todo un segmento dinámico sin variar de verdad el título y la descripción según el dato de cada página, lo que produce cientos de páginas con metadata casi idéntica. Eso no es una limitación de Next.js, es un error de implementación que el framework facilita evitar, pero no evita por sí solo.

  • En el App Router, cada segmento de ruta puede exportar un objeto de metadata estático o una función que la construye de forma dinámica: título, descripción, canonical y etiquetas Open Graph a partir de datos reales, por ejemplo el título de un producto o de un artículo concreto.
  • Esa generación dinámica es especialmente relevante para páginas con plantilla: rutas como la ficha de un producto o la de un artículo de blog necesitan que el título y la descripción cambien según el contenido real, no un texto genérico repetido en cientos de páginas distintas.
  • El canonical merece atención aparte: si no lo defines explícitamente, corres el riesgo de que variantes de la misma URL, con parámetros de consulta, con o sin barra final, se traten como páginas distintas.
  • La imagen de vista previa de Open Graph también se genera por ruta; para páginas dinámicas conviene que refleje el contenido real en lugar de una imagen fija de marca, sobre todo si esas páginas se comparten con frecuencia en redes sociales.

Los fallos que se repiten en proyectos reales

Estos son los problemas de SEO que aparecen con más frecuencia en proyectos Next.js, casi siempre por decisiones de implementación y no por límites del framework.

Ninguno de estos problemas se detecta mirando la página en el navegador, porque el navegador ejecuta JavaScript y te muestra el resultado final, que es exactamente lo que no siempre ve un rastreador en su primera pasada.

  • Soft 404: una ruta dinámica recibe un identificador que no existe en la base de datos, y en vez de responder con un estado 404 real, renderiza una página vacía o con un mensaje de "no encontrado" pero devuelve un 200. Un rastreador interpreta esa página como contenido válido e indexable, y con el tiempo terminas con cientos de URLs vacías en el índice. La solución es explícita: cuando el dato no existe, la ruta debe devolver un 404 real usando la función del framework pensada para eso, no solo mostrar un texto que dice "no encontrado".
  • Título que no cambia con la navegación en cliente: cuando la navegación entre páginas ocurre sin recarga completa, es fácil que el título del documento no se actualice si la lógica de metadata no está bien conectada a la ruta activa, o si parte de la navegación se implementó a mano por fuera del sistema de rutas del framework.
  • Problemas de hidratación: cuando el HTML que llega del servidor no coincide exactamente con lo que React genera al hidratar en el cliente, el navegador tiene que descartar y reconstruir partes de la página. No es en sí mismo un problema de indexación, pero sí de estabilidad visual y de tiempo hasta que la página es interactiva.
  • Redirecciones implementadas en el cliente: hacer una redirección dentro de un efecto de React que cambia la ruta en el navegador significa que, para cualquiera que solo lea el HTML servido, incluidos muchos rastreadores en su primera pasada, la página antigua parece devolver un 200 con contenido, no una redirección. Las redirecciones con intención de SEO deben resolverse en el servidor, con la configuración de redirecciones o con middleware.
  • Sitemaps que se quedan desactualizados en sitios estáticos: si generas el sitemap en el momento del build de un sitio con generación estática, y luego el contenido cambia sin un nuevo build, el sitemap sigue reflejando el estado del último build y no las páginas nuevas o eliminadas desde entonces. Si usas regeneración incremental, el sitemap necesita su propia estrategia de actualización.

Imágenes y Core Web Vitals: el framework ayuda, no resuelve

El componente de imagen de Next.js hace varias cosas útiles de forma automática: sirve tamaños adecuados según el dispositivo, aplica formatos modernos cuando el navegador los soporta, y evita que la imagen provoque saltos de layout al reservar el espacio correcto desde el principio. Eso ayuda directamente con dos de las métricas de Core Web Vitals.

En resumen, el framework quita trabajo, no la responsabilidad. La imagen que más pesa en la métrica que más importa sigue necesitando que alguien la mire directamente, no solo que el componente la envuelva.

  • El elemento más grande visible al cargar la página, la métrica LCP, casi siempre es una imagen, y usar el componente de imagen del framework no basta si esa imagen concreta no está marcada con prioridad de carga: sin esa marca, el navegador puede tratarla igual que cualquier otra imagen de la página y cargarla tarde.
  • El formato y la compresión de origen siguen siendo tu responsabilidad: si subes una imagen sin optimizar, el framework puede redimensionarla y servirla en un formato más eficiente, pero parte del peso original ya estaba ahí desde el archivo fuente.
  • El CLS puede aparecer aunque uses el componente de imagen, si hay otros elementos, como anuncios, fuentes web o contenido cargado de forma diferida, que se insertan después y desplazan el layout.

Rutas internacionalizadas: la parte que te afecta más a ti que a un lector en inglés

Si tu sitio en Next.js sirve contenido en varios idiomas, el enrutado internacionalizado deja de ser un detalle de experiencia de usuario y se convierte en una decisión de SEO con consecuencias directas: cómo se estructuran las URLs por idioma, cómo se declara la relación entre versiones de la misma página, y qué versión se sirve por defecto.

Para un sitio que compite en mercados de habla hispana además de en inglés, tener esto bien resuelto no es un extra: es la diferencia entre que cada versión de idioma compita en su propio mercado o que el buscador trate el contenido en distintos idiomas como algo duplicado o confuso.

  • La estructura de URL importa: subcarpetas por idioma o dominios y subdominios separados son opciones válidas, pero mezclar enfoques dentro del mismo sitio, con algunas rutas con prefijo de idioma y otras sin él, genera inconsistencias difíciles de razonar más adelante.
  • Las etiquetas que declaran el idioma de cada versión deben apuntar de forma recíproca entre todas las versiones de una misma página, incluyendo una versión por defecto. Una de estas etiquetas mal configurada, apuntando a una URL que no existe o sin la relación de vuelta, puede confundir más de lo que ayuda.
  • La detección de idioma por geolocalización o por cabecera del navegador es útil para la experiencia del usuario, pero no debe ser la única forma de llegar al contenido: un rastreador necesita poder acceder a cada versión de idioma mediante una URL directa, no solo mediante una redirección automática basada en su origen.

Cómo comprobar qué se sirve realmente, no lo que crees que se sirve

La forma más fiable de saber si una página resuelve bien estos problemas no es mirarla en el navegador, que siempre ejecuta el JavaScript y te enseña el resultado final, sino pedir directamente el HTML tal como llegaría a un rastreador, sin que nada lo procese primero.

Ninguna de estas comprobaciones depende de herramientas complejas ni de acceso especial: son peticiones HTTP básicas y una comparación entre dos vistas del mismo HTML. Es la forma más directa de confirmar que las decisiones de renderizado tomadas en el código se traducen en lo que de verdad llega a quien, o a lo que, está rastreando la página.

  • Con una herramienta que haga una petición HTTP simple, sin ejecutar JavaScript, pide la URL y revisa el HTML devuelto: ¿está ahí el título real, el contenido principal, el canonical? Si lo que ves es un esqueleto vacío con poco más que referencias a archivos de JavaScript, esa página depende del cliente para mostrar su contenido.
  • Compara ese HTML crudo con lo que ves usando la opción de ver el código fuente del navegador, que también muestra el HTML sin ejecutar JavaScript, frente al inspector de elementos, que muestra el documento ya procesado. Si hay una diferencia grande entre ambos, ahí está exactamente el contenido que un rastreador en primera pasada no ve.
  • Para rutas dinámicas, prueba explícitamente con un identificador que no exista: la respuesta debe ser un estado 404 real, no una página con apariencia de vacía pero estado 200.
  • Revisa el sitemap generado en producción, no solo en tu entorno local, y confirma que las URLs que contiene responden con estado 200 y coinciden con las páginas que realmente quieres que se indexen.

Preguntas frecuentes

¿SSR es mejor que SSG para el SEO en Next.js?

No necesariamente. Ambos entregan HTML completo en la primera respuesta, que es lo que le importa a un rastreador. La elección entre uno y otro suele depender de con qué frecuencia cambia el contenido y de qué carga puede asumir el servidor, no de una ventaja de SEO inherente a uno de los dos.

¿Necesito usar el App Router para tener buen SEO en Next.js?

No. El Pages Router puede servir HTML completo igual que el App Router si usa correctamente sus funciones de generación estática o del lado del servidor. La diferencia está en qué APIs usa cada sistema para conseguirlo, no en que uno sea intrínsecamente mejor para SEO que el otro.

¿El renderizado en cliente (CSR) es siempre malo para el SEO?

No para todo. Para páginas que no dependen de tráfico de búsqueda, como un panel de usuario autenticado, es perfectamente razonable. El problema aparece cuando una página que sí quieres posicionar depende de JavaScript en el cliente para mostrar el contenido que la hace relevante para esa búsqueda.

¿Cómo sé si mi sitio Next.js tiene contenido que solo existe después de ejecutar JavaScript?

Pide la URL con una herramienta que no ejecute JavaScript, como una petición HTTP simple desde la terminal, y compara el HTML devuelto con lo que ves en el navegador. Si el contenido principal no aparece en esa respuesta cruda, depende del cliente para mostrarse.

Todos los artículos