SEO en Angular con SSR: metadatos, canonical y rutas rastreables

Este proyecto consisiterá en crear un Server Side Rendering (SSR) y cómo implementarlo para que nuestra aplicación SPA con Angular pueda ser detectada por la web (SEO) haciendo uso de Angular Universal (@nguniversal/express-engine)

Esta guía se centra en la parte específica de Angular: servir HTML útil desde servidor, exponer enlaces rastreables, generar metadatos únicos por ruta y evitar errores de canonical. Como continuación práctica puedes ver el hub de Angular, el hub de SEO técnico, resaltado de código con SSR y Cloudflare Turnstile con Angular y FastAPI.

Nota actual sobre SSR en Angular

Los ejemplos historicos de abajo hablan de Angular Universal y @nguniversal/express-engine. En proyectos Angular actuales, el SSR se integra mediante @angular/ssr, hidratacion y prerenderizado opcional. Conserva la idea SEO, pero usa el flujo moderno del CLI para proyectos nuevos.

Google puede renderizar JavaScript, pero ese renderizado es una segunda fase y no todos los rastreadores, previsualizadores sociales o herramientas SEO ejecutan JavaScript de cliente con la misma fiabilidad. Para rutas publicas importantes, es mejor que la primera respuesta HTML ya incluya contenido principal, metadatos y enlaces.

Si necesitas ayuda esta disponible el siguiente video explicativo: https://www.youtube.com/watch?v=J8PDfVY2FfI.

Peligro de SPA con el SEO

La gran ventaja de las aplicaciones SPA(Aplicaciones del lado cliente) es que trabajan en el lado del cliente. Por ejemplo una web creada en el framework de Angular se ejecuta en el cliente, es decir, en el navegador del usuario y no en un servidor remoto. Este tipo aplicaciones web muestran todas las pantallas en una misma página, sin recargar el navegador.

El problema aparece cuando la primera respuesta HTML casi no contiene contenido y la ruta depende por completo de JavaScript en cliente. Google puede renderizar JavaScript, pero ese renderizado puede llegar despues del primer rastreo, retrasar el descubrimiento y no representa a todos los bots, previsualizadores sociales o herramientas de auditoria. Si la ruta importa para SEO, no hagas que el texto principal, los enlaces, el title o el canonical dependan solo de la ejecucion en navegador.

El ejemplo real

Creemos una aplicación angular y verifiquemos cómo la ven los robots:

Ahora, en la otra ventana de terminal, ejecute el comando curl y echamos un vistazo a la salida:

Como podemos observar, no renderiza correctamente la web y nuestra aplicación web con Angular no ha sido detectada correctamente, es decir, los motores de búsqueda consideran que no hay contenido.

La solución

En versiones antiguas de Angular, la solucion a este problema era usar @nguniversal/express-engine . En Angular actual conviene usar el paquete oficial de SSR y mantener el mismo objetivo: renderizar HTML significativo en el servidor para usuarios y rastreadores. El renderizado dinamico solo para bots queda como apaño puntual; por defecto es mejor servir el mismo contenido con SSR, prerenderizado e hidratacion.

¿Que necesitamos? Para ello necesitamos tener instalado:

  • Tener instalado Node y NPM.
  • Tener instalado Angular CLI

¿Qué nos ofrece Angular Universal?

Es una tecnología que nos permitirá ejecutar nuestra aplicación Angular desde el servidor.

Genera páginas de aplicaciones estáticas en el servidor mediante el proceso denominado SSR (server side rendering o renderizado de lado del servidor) que nos permitirá cubrir las áreas oscuras que se dan en el desarrollo moderno:

  • Facilitar la indexacion del SEO
  • Mejorar el rendimiento de la aplicacion en dispositivos de baja potencia
  • Mostrar la primera pagina rápidamente

La importancia de implementar SEO en un proyecto

Es una parte importantísima para cualquier negocio sea pequeño, mediano o grande. El objetivo del SEo es conseguir que visiten nuestra página web o la de nuestra empresa la mayor cantidad de gente posible y para ello se definen una serie de procedimiento y técnias que permitirán tener una mayor repercusión a nuestra web

Hay dos tipos:

  • SEO on page: se trata de todo lo que puedes hacer respecto al código de nuestra web para que genere más tráfico y es lo que vamos a ver en esta sección
  • TSEO off page: como su nombre indica es todo lo relacionado con tu negocio fuera de tu página, es decir, lo que corresponde a redes sociales, publicidad, etc.

Para obtener estos objetivos, en nuestra aplicación de angular vamos a implementar la siguiente solución:

Esquema SEO en Angular

En ella se observa:
  • El usuario solicita la página.
  • La solicitud llega al servidor
  • NodeJS genera HTML y lo envía al navegador
  • El navegador muestra la vista desde el HTML e inmediatamente la muestra al usuario, simultáneamente se está ejecutando JavaScript
  • Cuando JavaScript finaliza el arranque de la aplicación, cambia la vista representada en HTML con la aplicación angular
  • El usuario ve una aplicación angular totalmente interactiva

Bueno. Conocemos la teoria. Vamos a practicar ahora.

Aplicando SSR con Angular Universal

Este comando se mantiene como contexto historico para proyectos Angular antiguos:


  ng add @nguniversal/express-engine

Hay que recordar que el CLI de Angular utilizar la directiva “ng add” del principio del uso de los schematicas para modificar nuestro código y adaptarlo a la nueva funcionalidad que queremos implementar.

Si hicieramos manualmente el proceso tendríamos que seguir los siguientes pasos, que como podéis ver son muchos:

  • Instalar nuevas dependencias
  • Editar el fichero main.ts
  • Editar el fichero app.module.ts
  • Editar el fichero angular.json
  • Crear el fichero src/app/app.server.module.ts
  • Crear el fichero src/main.server.ts
  • Crear el fichero server.ts
  • Crear el fichero tsconfig.server.json
  • Crear el fichero webpack.server.config.js
  • Editar el fichero angular.json
  • Editar el fichero package.json

Ahora que ya tenemos nuestra aplicación adaptada al renderizado por el lado del servidor (SSR), con lo visto hasta ahora, nuestra aplicación estará más que cubierta en cuanto a ofrecer la mejor experiencia para usuarios al tiempo que conseguiremos enviar contenido indexable para robots… mejorando este punto negativo en las aplicaciones de tipo SPA.

Lanzar la aplicación

Para lanzar la aplicación se ejecutan los siguientes comandos:


  npm run build:ssr
  npm run serve:ssr

Y si ejecutamos la misma sentencia que antes nos encontramos con el suguiente código html:

  curl localhost:4000

      

¿Qué pasa con window y localStorage?

NodeJS y los navegadores pueden ejecutarse en 99% de los casos con el mismo código. Pero hay algunas cosas que se pueden ejecutar solo en NodeJS o solo en el navegador. Ahora nos centraremos en resolver estas incidencias

Window: es el navegador que utiliza el cliente. Un rastreador no utiliza una ventana entonces, si por ejemplo queremos utilizar el siguiente código nos daria un error:

  public ngOnInit (): void {
  console.log (window.URL);
  }
    

En mi caso, para ese problema he aplicado la siguiente solución:

  

    import {Component, Inject, OnInit, PLATFORM_ID} from '@angular/core';
import {isPlatformBrowser} from '@angular/common';
@Component ( {
...

export class xxxxComponent implements OnInit {
    isBrowser;

  constructor(@Inject(PLATFORM_ID) private platformId: object) {
    this.isBrowser = isPlatformBrowser(platformId);
  }
    ngOnInit() {
    if (this.isBrowser) {

      if (document.cookie.indexOf(this.cookie_name + '=1') >= 0) {
    ....
      }

    }
  }
  

Es decir, con PLATFORM_ID puedo comprobar si hay o no una ventana solicitando la información. En caso afirmativo, genero el código necesario para que se muestre la lógica necesaria, en caso negativo, muestro la lógica deseada en ese caso.

Puesta en producción

En mi caso, he decidido utilizar pm2 para ejecutar el servidor, próximamente creare un proyecto explicando como llevar a cabo una puesta en marcha de un proyecto en angular de estas caracteristicas, pero mientras tanto dejo el siguiente comando:

  sudo npm install -g pm2
  

Una vez instalado, tengo que copiar el directorio raiz (por ejemplo, utilizando el comando scp) creado por el comando:

   npm run build:ssr
  

EL directorio raiz es por defector /dist pero depende de la configuración del proyecto. Una vez copiado el directorio, simplemente se genera un fichero con el siguiente código:

  
  // generated by @nguniversal/express-engine
  const port = process.env.PORT || 8590;

  const server = require('./dist/server');

  server.app.listen(port, () => {
  console.log(`Listening on: http://localhost:${port}`);
  });
  
  

Con este fichero creado y el entorno pm2 instalado se ejecuta el siguiente comando:

  pm2 start node filename.js (en mi caso server.js)
  

Y ya tendremos en ejecutación en la url http://xxxx:8590 nuestro Server Side Rendering. Si por ejemplo estamos en un entorno apacher, si añadimos a nuestro archivo .conf del dominio las siguientes tres linea redireccionariamos las peticiones 80 al Server Side Rendering de la siguiente forma:

  

  <VirtualHost www.example.com:80>
    ProxyPreserveHost On
    ProxyRequests Off
    ServerName www.example.com
    ServerAlias example.com
    ProxyPass / http://localhost:8080/
    ProxyPassReverse / http://localhost:8080/
  </VirtualHost>
  
      

Es necesario tener instalado tanto el paquete proxy y proxy_http de apache y configurado.

    sudo a2enmod proxy
    sudo a2enmod proxy_http
  

Diagnosticar URLs rastreadas pero no indexadas

Cuando Google Search Console marca una URL como rastreada pero actualmente sin indexar, el servidor era accesible. Las siguientes comprobaciones son de calidad y coherencia: el HTML renderizado debe contener el artículo principal, el canonical debe apuntar a la URL correcta, title y description deben ser únicos y los enlaces internos deben explicar por qué esa página importa dentro del sitio.

En proyectos Angular, la prueba más útil es comparar tres versiones de la misma ruta: el HTML SSR que devuelve curl, la página hidratada en navegador y la entrada del sitemap. Si esas tres piezas no coinciden, Google puede rastrear la URL pero decidir que está incompleta, duplicada o que no tiene señales suficientes para indexarse.

Lecturas relacionadas de SEO tecnico