Cómo generar un sitemap dinámico en Angular: CSR vs SSR paso a paso

Una vez que tengamos publicado nuestro website en angular, es importante que este llegue correctamente indexado en los motores de búsqueda. Una de las claves para conseguirlo -entre otras cosas - es un sitemap XML. Un sitemap es fundamental para que los bots lean las rutas y así indexen correctamente las páginas de nuestro sitio web.

En aplicaciones Angular, que pueden funcionar tanto en modo cliente (CSR) como servidor (SSR), la configuración cambia ligeramente.

En este artículo te muestro cómo implementar un sitemap dinámico en ambos casos con ejemplos prácticos y explicaciones claras.

🔍 ¿Por qué un sitemap dinámico?

Lo bueno de un sitemap dinámico es que podemos configurarlo para que genere nuestras rutas de forma automática en función de los cambios que vamos publicando en el sitio web. Esto evita tener que mantenerlo manualmente.

Es especialmente útil cuando:

  • Se crean nuevas páginas con frecuencia.
  • Las páginas suelen ser dinámicas.
  • Usas Angular en modo SSR (Server-Side Rendering) para SEO.

🧭 1. Sitemap dinámico en Angular SSR

✔️ Requisitos

  • Angular 17+ con SSR configurado (@angular/ssr)
  • Node.js
  • Express (ya incluido en el server.ts generado por Angular SSR)

🛠️ Paso a paso

Paso 1: Instala xmlbuilder

npm install xmlbuilder

Paso 2: Agrega la ruta del sitemap en tu server.ts

Edita server.ts y añade antes de la ruta server.get('**') lo siguiente:

const xmlbuilder = require('xmlbuilder');
    server.get('/sitemap.xml', (_req, res) => {
      const routes = ['/', '/about', '/contact']; // Añade tus rutas reales aquí

      const xml = xmlbuilder.create('urlset', { version: '1.0', encoding: 'UTF-8' })
        .att('xmlns', 'http://www.sitemaps.org/schemas/sitemap/0.9');

      routes.forEach(route => {
        const url = xml.ele('url');
        url.ele('loc', 'https://tudominio.com${route}');
      });

      res.set('Content-Type', 'application/xml');
      res.send(xml.end({ pretty: true }));
    })

Paso 3: Compila y ejecuta


npm run build:ssr
npm run serve:ssr

Ahora puedes visitar:
📍 https://tudominio.com/sitemap.xml

🌐 2. Sitemap dinámico en Angular CSR (cliente)

En modo CSR, Angular es una SPA (Single Page Application), así que necesitas un backend aparte que sirva el sitemap. Puedes usar un microservidor en Express o alojarlo en tu backend actual.

🛠️ Paso a paso con Express

Paso 1: Crea un servidor básico


npm init -y
npm install express xmlbuilder

Paso 2: server.js

const express = require('express');
    const xmlbuilder = require('xmlbuilder');
    const app = express();

    const routes = ['/', '/about', '/contact']; // Rutas públicas de tu app

    app.get('/sitemap.xml', (_req, res) => {
      const xml = xmlbuilder.create('urlset', { version: '1.0', encoding: 'UTF-8' })
        .att('xmlns', 'http://www.sitemaps.org/schemas/sitemap/0.9');

      routes.forEach(route => {
        const url = xml.ele('url');
        url.ele('loc', 'https://tudominio.com${route}');
      });

      res.set('Content-Type', 'application/xml');
      res.send(xml.end({ pretty: true }));
    });

    app.listen(3000, () => console.log('Servidor en http://localhost:3000'));
  

Paso 3: Despliega junto a tu frontend

Puedes servir este servidor en un subdominio o ruta del backend.
Asegúrate de que esté disponible en producción.

✅ Bonus: Agrega el sitemap a robots.txt

Asegúrate de declarar tu sitemap en el archivo robots.txt para que Google lo encuentre:

Sitemap: https://tudominio.com/sitemap.xml

📦 Bonus 2: ¿Y si las rutas son dinámicas?

Puedes extraerlas desde:

  • Una base de datos
  • Archivos Markdown
  • Un CMS Headless (ej. Sanity, Contentful)
  • Peticiones HTTP internas

Solo reemplaza el array routes por una función que genere las rutas dinámicamente.

🧠 Conclusión

Tanto si usas Angular en SSR como en CSR, tener un sitemap XML dinámico mejora muchísimo tu posicionamiento en Google. La clave está en interceptar la ruta /sitemap.xmlfuera de Angular y generarla dinámicamente según tu estructura de rutas o contenido.


¿Quieres ver un ejemplo real de esto en producción?
Visita 👉 https://giuliacapozzi.com/sitemap.xml 😉