Sitemap.xml i RSS feed w Next.js App RouterSEO techniczne 2026

sitemap6 min czytania20 lipca 2026

Autor: DevStudio.it

TL;DR

Google indeksuje szybciej, gdy ma sitemap.xml z pełną listą URL i RSS dla sekcji bloga — crawler nie musi zgadywać nowych wpisów. W Next.js App Router generujesz oba pliki jako sitemap.ts i feed.xml/route.ts (lub dedykowany rss.xml/route.ts) bez pluginów WordPress. Dla strony wielojęzycznej (pl/en/de) każdy URL w sitemap dostaje hreflang alternates, a RSS publikuje tylko wpisy z danego locale. Deploy na DevStudioIT Cloud serwuje pliki z cache CDN — aktualizacja po każdym next build. Treści dynamiczne (case studies z PostgreSQL w Branchly) wchodzą do sitemap przez async fetch w sitemap.ts.

Dla kogo to jest

  • Stron firmowych Next.js z blogiem i wieloma wersjami językowymi
  • Zespołów SEO, które chcą technicznej kontroli bez Yoast / Rank Math
  • Projektów migrujących z WordPress — RSS subskrybenci muszą dostać nowy feed URL
  • Developerów wdrażających ISR lub static generation — sitemap musi odzwierciedlać revalidate
  • Każdego, kto ma /sitemap.xml z trzema URL-ami „bo ktoś kiedyś dodał"

Fraza (SEO)

sitemap nextjs app router, rss feed next.js, feed.xml seo, sitemap.ts wielojęzyczność, robots.txt nextjs 2026, hreflang sitemap

sitemap.xml — natywny MetadataRoute w Next.js

Plik app/sitemap.ts eksportuje funkcję zwracającą tablicę URL:

import type { MetadataRoute } from 'next';
import { getBlogPosts } from '@/lib/blog';

const BASE = 'https://devstudioit.com';

export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
  const staticRoutes = ['', '/strony-www', '/blog', '/kontakt'].flatMap((path) =>
    ['pl', 'en', 'de'].map((locale) => ({
      url: `${BASE}/${locale}${path}`,
      lastModified: new Date(),
      changeFrequency: 'weekly' as const,
      priority: path === '' ? 1 : 0.8,
    }))
  );

  const posts = await getBlogPosts();
  const blogEntries = posts.map((post) => ({
    url: `${BASE}/${post.locale}/blog/${post.slug}`,
    lastModified: new Date(post.updated ?? post.date),
    changeFrequency: 'monthly' as const,
    priority: 0.6,
  }));

  return [...staticRoutes, ...blogEntries];
}

Next.js serwuje wynik pod /sitemap.xml. Nie twórz ręcznie pliku XML w public/ — tracisz typowanie i dynamiczne wpisy.

Pole Zalecenie Uwaga
lastModified Data realnej zmiany treści Nie new Date() dla statycznych stron co build
changeFrequency weekly / monthly Hint dla crawlera, nie gwarancja
priority 0.5–1.0 względne Nie wpływa bezpośrednio na ranking

Wielojęzyczność — hreflang w sitemap vs w &lt;head&gt;

Google akceptuje hreflang w sitemap (extension xhtml:link) lub w HTML. Next.js Metadata API w layoutcie:

export async function generateMetadata({ params }): Promise<Metadata> {
  const { locale, slug } = await params;
  return {
    alternates: {
      canonical: `https://devstudioit.com/${locale}/blog/${slug}`,
      languages: {
        pl: `https://devstudioit.com/pl/blog/${slug}`,
        en: `https://devstudioit.com/en/blog/${slug}`,
        de: `https://devstudioit.com/de/blog/${slug}`,
      },
    },
  };
}

Dla sitemap z hreflang użyj rozszerzonego formatu (Next.js 15 wspiera alternates w obiekcie sitemap entry, gdy dodasz pole zgodne z dokumentacją). Spójność między &lt;head&gt; a sitemap jest ważniejsza niż wybór jednej metody.

Podział sitemap — gdy URL > 50 000

Duże serwisy dzielą sitemap na indeks:

Plik Zawartość
/sitemap.xml Indeks wskazujący pod-sitemapy
/sitemap-pages.xml Strony statyczne
/sitemap-blog.xml Wpisy bloga
/sitemap-cases.xml Case studies z Branchly

W Next.js: app/sitemap/[id]/route.ts lub wiele plików sitemap.ts w segmentach — sprawdź limit 50 MB / 50k URL na plik według Google.

RSS / feed.xml — route handler

Subskrybenci RSS, agregatory branżowe i niektóre narzędzia monitoringu oczekują application/rss+xml. Route handler:

// app/[locale]/feed.xml/route.ts
import { getBlogPosts } from '@/lib/blog';

export async function GET(
  _req: Request,
  { params }: { params: Promise<{ locale: string }> }
) {
  const { locale } = await params;
  const posts = await getBlogPosts(locale);
  const site = `https://devstudioit.com/${locale}`;

  const items = posts.slice(0, 20).map((post) => `
    <item>
      <title><![CDATA[${post.title}]]></title>
      <link>${site}/blog/${post.slug}</link>
      <guid isPermaLink="true">${site}/blog/${post.slug}</guid>
      <pubDate>${new Date(post.date).toUTCString()}</pubDate>
      <description><![CDATA[${post.description}]]></description>
    </item>
  `).join('');

  const xml = `<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>DevStudio.it Blog (${locale})</title>
    <link>${site}/blog</link>
    <description>Aktualności i przewodniki web development</description>
    <language>${locale}</language>
    ${items}
  </channel>
</rss>`;

  return new Response(xml, {
    headers: {
      'Content-Type': 'application/rss+xml; charset=utf-8',
      'Cache-Control': 'public, s-maxage=3600, stale-while-revalidate=86400',
    },
  });
}

URL końcowy: /pl/feed.xml, /en/feed.xml. Dodaj &lt;link rel=&quot;alternate&quot; type=&quot;application/rss+xml&quot;&gt; w &lt;head&gt; bloga.

robots.txt — spójność z sitemap

Plik app/robots.ts:

import type { MetadataRoute } from 'next';

export default function robots(): MetadataRoute.Robots {
  return {
    rules: {
      userAgent: '*',
      allow: '/',
      disallow: ['/api/', '/admin/'],
    },
    sitemap: 'https://devstudioit.com/sitemap.xml',
  };
}
Błąd Skutek
Sitemap wskazuje na staging URL Indeksacja środowiska testowego
disallow: /blog + RSS aktywny Sprzeczne sygnały dla Google
Brak sitemap w robots Wolniejsze odkrywanie nowych wpisów

Staging na DevStudioIT Cloud powinien mieć robots: noindex w metadata layoutu — osobna instancja, osobny robots.

Treści dynamiczne z Branchly w sitemap

Case studies lub landingi CMS trzymane w PostgreSQL (Branchly):

const cases = await db.caseStudy.findMany({
  where: { published: true },
  select: { slug: true, updatedAt: true, locale: true },
});

const caseEntries = cases.map((c) => ({
  url: `${BASE}/${c.locale}/case-studies/${c.slug}`,
  lastModified: c.updatedAt,
}));

Cache: export const revalidate = 3600 w sitemap.ts lub fetch z { next: { revalidate: 3600 } }. Po publikacji w CMS sitemap odświeży się w ciągu godziny bez pełnego redeploy — tradeoff akceptowalny dla bloga B2B.

Walidacja przed produkcją

Test Narzędzie
Poprawność XML sitemap Google Search Console → Sitemaps
RSS valid validator.w3.org/feed
hreflang Ahrefs / Screaming Frog
Czy feed zwraca 200 curl -I https://devstudioit.com/pl/feed.xml

Po deploy na produkcję zgłoś sitemap w Search Console raz — kolejne aktualizacje XML Google pobiera sam.

Workflow po publikacji nowego wpisu bloga

Typowy cykl w projekcie DevStudio z markdown w repo:

  1. Merge PR z nowym plikiem content/blog/{locale}/{slug}.md
  2. CI buduje Next.js i deploy na DevStudioIT Cloud
  3. sitemap.ts przy buildzie dodaje URL z lastModified z frontmatter
  4. RSS route z revalidate: 3600 pokazuje wpis w feedzie w ciągu godziny (lub od razu po buildzie jeśli SSG)
  5. Opcjonalnie ping do Google Indexing API dla krytycznych landingów — blog zwykle wystarczy sitemap
Krok Kto Czas
Publikacja treści Content / dev PR review
Build + deploy CI 5–12 min
Odkrycie przez Google Crawler 1–7 dni (normalne)
RSS u subskrybenta Feed reader Przy następnym poll

Nie pinguj Indexing API masowo dla każdego wpisu — limit dzienny i ryzyko flagi spam. Sitemap + internal linking z listy bloga wystarczy dla B2B.

Checklist SEO technicznego przy launchu

  • /sitemap.xml zwraca 200 i zawiera wszystkie locale
  • /pl/feed.xml, /en/feed.xml, /de/feed.xml walidują się w W3C validator
  • robots.ts wskazuje produkcyjną domenę, nie staging
  • Każdy wpis ma alternates.languages w metadata
  • Staging ma noindex, produkcja nie
  • Search Console: osobna property lub folder per locale (zależnie od struktury)

FAQ

Jeden RSS dla wszystkich języków czy osobny per locale?

Osobny per locale — czytelniejszy dla subskrybenta i zgodny z &lt;language&gt; w kanale. Opcjonalnie główny feed z wpisami tylko po polsku, jeśli 90% audience to PL.

Czy sitemap musi zawierać noindex strony?

Nie dodawaj URL z robots: noindex — marnujesz crawl budget. Filtruj drafty bloga i strony /preview.

RSS vs Atom — co wybrać?

RSS 2.0 ma najszerszą kompatybilność. Atom (application/atom+xml) opcjonalnie obok — większość czytników obsługuje RSS.

Jak często rebuild sitemap przy 100+ wpisach?

Przy markdown w repo sitemap generuje się przy każdym buildzie — zero problemu. Przy CMS async fetch + revalidate wystarczy; pełny rebuild tylko przy zmianie struktury URL.

Chcesz sitemap i RSS w swoim Next.js?

Powiązane wpisy

Google Search Console i indeksowanie strony Next.js — SSR, SSG i sitemap w 2026
5 min czytania
JSON-LD i Schema.org — SEO strony firmowej w Next.js (przewodnik 2026)
9 min czytania
Stripe Customer Portal — subskrypcje, webhooks i Next.js w 2026
5 min czytania

O autorze

Budujemy szybkie strony WWW, aplikacje web/mobile, chatboty AI i hosting — z naciskiem na SEO i konwersję.

Przydatne linki

Od teorii do produkcji — Branchly, hosting i realizacje.

Podoba Ci się nasze podejście? Zbudujmy coś razem.

Rozpocznij konfigurację projektu