Szybka odpowiedź: jak wygenerować sitemap.xml w Astro z CMS?
Warto od razu określić stawkę, by było jasne o co toczy się gra. Jeśli nowa treść nie jest szybko odkrywana, tracisz ruch w „prime time", co przy newsach, dynamicznych landingach czy ofertach czasowych może oznaczać utracony przychód. Sitemap nie gwarantuje indeksacji, ale mówi crawlerowi: „te adresy istnieją, te właśnie się zmieniły, warto je sprawdzić". Automatyzacja tego procesu to nie luksus, tylko wymóg każdego projektu, który skaluje content powyżej 10–20 stron.
Architektura: build-time sitemap zamiast SSR
Przepływ danych wygląda tak:
Cała decyzja sprowadza się do jednego: budujemy sitemapę w czasie build, a nie w czasie żądania (SSR). To rozróżnienie ma realne konsekwencje dla SEO i wydajności:
- Generowanie statyczne (, domyślne w Astro) — sitemapa powstaje raz, podczas builda. To zwykły plik XML serwowany z . wchodzi na niego tysiące razy i nigdy nie dotyka Twojej bazy ani API CMS-a. Zero obciążenia, natychmiastowa odpowiedź. To Astro w pełnej krasie: szybkość i lekkość.
- Generowanie dynamiczne () — sitemapa odpytywałaby CMS przy każdym żądaniu Googlebota. Większe obciążenie, wolniejsza odpowiedź, ryzyko timeoutu na dużych zbiorach i niepotrzebny ruch do API.
Skoro sitemapa zmienia się tylko przy publikacji treści (czyli przy kolejnym deployu/buildzie), nie ma powodu generować jej dynamicznie. Build-time to właściwy wybór — dlatego całą logikę opieramy o getStaticPaths i statyczne endpointy.
Jakie URL-e powinny trafić do sitemap.xml w Astro
Sitemapa nie jest listą wszystkich adresów, które technicznie istnieją w aplikacji ale to lista URL-i, które chcesz widzieć w wynikach wyszukiwania. W związku z tym, do sitemapy dodawaj tylko adresy, które:
- Zwracają HTTP 200.
- Nie używają .
- Są kanoniczną wersją danej treści.
- Nie są przekierowaniem, fallbackiem ani stroną błędu.
- Nie są duplikatem parametrów typu
?sort=,?page=albo?utm=, mają realną treść i nie są tylko pustym szablonem z CMS-a.
W praktyce filtruj dane już na etapie pobierania z CMS-a: status === 'published', slug istnieje, canonicalUrl nie wskazuje gdzie indziej, a rekord nie jest wersją roboczą. Jeżeli strona ma robots: noindex, nie powinna pojawić się w sitemapie. Jeżeli URL przekierowuje na inny adres, w sitemapie powinien być adres docelowy.
Implementacja sitemap.xml w Astro krok po kroku
Krok 1: Instalacja @astrojs/sitemap
Oficjalna integracja Astro. Instalacja jednym poleceniem:
Wymaga ustawionego site w konfiguracji — bez tego integracja nie zbuduje absolutnych URL-i:
Najważniejszym mechanizmem jest @astrojs/sitemap, który wykrywa strony zbudowane podczas builda, włącznie z tymi wygenerowanymi przez getStaticPaths. Nie musisz ręcznie wpisywać ścieżek do sitemapy, ponieważ wystarczy tylko, że Twoje dynamiczne trasy faktycznie powstają na buildzie.
Krok 2: Generowanie podstron z CMS przez getStaticPaths
To jest „ważny szczegół" obsługi tras typu /blog/[slug]. W pliku trasy dynamicznej pobierasz dane z API i zwracasz listę ścieżek. Każda z nich stanie się statyczną stroną — i automatycznie wpadnie do sitemapy.
Sanity zamiast
fetchna REST użyje zapytania GROQ przez klienta@sanity/client, ale zasada jest identyczna: pobierasz listę dokumentów zeslugiupdatedAt, mapujesz na ścieżki.
Po tym kroku @astrojs/sitemap zna już wszystkie adresy /blog/<slug>/. Brakuje mu tylko jednej rzeczy: daty ostatniej modyfikacji.
Krok 3: Mapowanie updatedAt → <lastmod> przez serialize
Integracja nie analizuje kodu źródłowego strony, więc sama nie wie, kiedy dany artykuł był aktualizowany. Tę informację wstrzykujemy w hooku serialize, który jest wołany dla każdego wpisu tuż przed zapisem na dysk. Najczystsze podejście: raz pobrać dane z CMS, przerwać build przy błędzie i zbudować mapę ścieżka → updatedAt.
To wszystko, czego potrzebuje większość projektów. Jeden build i masz sitemap-index.xml z poprawnymi datami <lastmod> dla każdego opublikowanego artykułu, aktualizowany automatycznie przy każdej publikacji, która wyzwala redeploy.
Wariant „gotowiec": własny endpoint z pełną kontrolą
Czasami możesz chcieć pełnej kontroli nad XML-em. Możesz mieć własny podział na pliki, własne reguły, dane prosto z API bez polegania na auto-wykrywaniu stron. W takiej sytuacji musisz stworzyć statyczny endpoint. Poniższy plik możesz wkleić do projektu i podmienić tylko stałe w jego górnej części:
W domyślnym trybie statycznym Astro ten endpoint jest renderowany raz, na buildzie. Efekt jest taki sam jak przy @astrojs/sitemap, czyli zwykły plik na CDN. (export const prerender = true ma znaczenie tylko wtedy, gdy projekt działa w trybie server/SSR ponieważ wymusza statyczność tego konkretnego pliku.)
Sitemap.xml a : co naprawdę czyta Google?
<lastmod> musi oznaczać istotną zmianę
<lastmod> to opcjonalny tag, który Google może wziąć pod uwagę, ale tylko wtedy, gdy jest spójny i możliwy do zweryfikowania. Poprawna data aktualizacji mówi: „tu zmieniła się istotna treść, warto wrócić". Nie powinna oznaczać każdego builda, deployu, zmiany stopki albo aktualizacji copyrightu.
Jest jednak haczyk, o którym mało kto pisze: Google ufa lastmod tylko, jeśli jest wiarygodny. Jeśli na każdym buildzie ustawisz lastmod na „teraz" dla wszystkich stron (częsty błąd — branie new Date() zamiast daty z CMS), Google szybko zauważy, że daty kłamią, i zacznie je ignorować. Dlatego w kodzie wyżej lastmod pochodzi wprost z updatedAt w CMS, a nie z czasu builda. To różnica między sygnałem, który działa, a szumem, który Google odfiltrowuje.
priority i changefreq nie są strategią indeksacji
Możesz ustawić priority i changefreq dla porządku albo dla narzędzi, które je analizują, ale Google oficjalnie ignoruje oba tagi. Nie buduj strategii indeksacji wokół wartości 0.8 czy weekly. Większe znaczenie mają: kanoniczny URL, poprawny status HTTP, brak noindex, linkowanie wewnętrzne i wiarygodny lastmod.
Sitemap Index — co zrobić przy tysiącach stron?
Pojedynczy plik sitemapy ma limity: maksymalnie 50 000 URL-i i 50 MB (Astro domyślnie tnie pliki przy entryLimit: 45000). Przy dużych katalogach produktów czy rozbudowanym blogu i tak dziel sitemapę na mniejsze, tematyczne pliki. Nie z konieczności technicznej, ale dlatego, że dużo łatwiej diagnozuje się indeksację w , gdy każdy typ treści ma osobny plik (blog-sitemap.xml, products-sitemap.xml).
W @astrojs/sitemap służy do tego opcja chunks:
Powstaną osobne pliki (sitemap-blog-0.xml, sitemap-produkty-0.xml, plus domyślny koszyk dla reszty), wszystkie spięte w sitemap-index.xml. Gdy ruch z bloga spadnie, od razu widzisz w GSC, czy problem dotyczy bloga, czy produktów.
Monitoring i utrzymanie sitemap.xml w Astro
Weryfikacja w Google Search Console
Po wdrożeniu zgłoś sitemapę w GSC. Wejdź w raport Mapy witryn → dodaj adres https://example.com/sitemap-index.xml. Po przetworzeniu zobaczysz status (powinien być „Sukces"), datę ostatniego odczytu i liczbę wykrytych adresów. Jeśli liczba „wykrytych" mocno odbiega od realnej liczby treści, masz wtedy wyraźny sygnał, że coś jest nie tak. Rozbicie na pliki tematyczne (sekcja wyżej) sprawia, że od razu wiadomo gdzie.
Dodaj też sitemapę do robots.txt, żeby crawler znalazł ją bez ręcznego zgłoszenia w panelu:
Ostrzeżenie: co, jeśli API CMS-a padnie podczas builda?
To realne ryzyko, które trzeba obsłużyć w sposób przemyślany, bo jeśli fetch do CMS-a zwróci błąd albo pustą listę w trakcie builda, zaczynają się kłopoty. Masz dwa scenariusze, a każde z nich jest groźne:
- build się wywala i nie wdrożysz nic (frustrujące, ale bezpieczne),
- build przechodzi z pustą listą → wdrażasz serwis z pustą sitemapą i bez podstron bloga, co przy regularnym crawlowaniu może doprowadzić do deindeksacji treści.
Dlatego rekomenduję podejście fail-fast dla danych krytycznych, czyli lepiej, żeby build padł głośno, niż żeby po cichu wdrożył uszkodzony serwis.
Wariant pośredni dla bardzo dużych serwisów to zamiast twardego throw możesz wczytać ostatnią dobrą wersję danych z cache (np. plik JSON commitowany na deployu), żeby chwilowa awaria CMS-a nie blokowała wydania. Wybór zależy od tego, co jest gorsze w Twoim projekcie: zablokowany deploy czy ryzyko nieświeżych danych.
Jeśli budujesz rozwiązanie dla biznesu i zależy Ci na solidnej architekturze — nie tylko stronie, ale całym ekosystemie treści na Astro i headless CMS — sprawdź moją matrycę decyzyjną AI albo napisz do mnie w sprawie wdrożenia. A jeśli serwis już działa, ale indeksacja kuleje, od tego jest audyt techniczny SEO.
