SEO Angulara

Jak sprawić, by aplikacje Angular były crawlowalne i indeksowalne — @angular/ssr kontra prerenderowanie i renderowanie hybrydowe, hydracja, usługi Title/Meta, trasowanie History API oraz testowanie tego, co faktycznie renderuje Googlebot.

Opublikowano po raz pierwszy: 26 cze 2026 · Ostatnia aktualizacja: 8 sie 2026 · Advanced
Języki

SEO Angulara sprowadza się do jednej decyzji: wysyłaj prawdziwy HTML zamiast powłoki renderowanej po stronie klienta. Nowoczesny Angular (v17+) ma SSR w CLI jako @angular/ssr — prerenderuj statyczne trasy, dynamiczne renderuj na serwerze, a następnie hydratuj, aby nie wyrzucać HTML-a. Potem dopilnuj podstaw: unikalnych tytułów i opisów przez usługi Title i Meta, trasowania HTML5 History (nigdy adresów z haszem), prawdziwych linków <a href> oraz weryfikacji przez URL Inspection. Dynamiczne renderowanie jest rozwiązaniem tymczasowym, nie strategią, a AngularJS to inny framework.

TL;DR — SEO Angulara to jedna decyzja architektoniczna przybierająca wiele postaci: włóż prawdziwy HTML do odpowiedzi zamiast powłoki renderowanej po stronie klienta. Nowoczesny Angular (v17+) wbudowuje SSR w CLI jako @angular/ssr (przemianowanego, zintegrowanego następcę Angular Universal). Prerenderuj statyczne trasy, renderuj dynamiczne na serwerze, połącz je renderowaniem hybrydowym i hydratuj, aby ponownie wykorzystać HTML serwera, a nie budować go od nowa. Potem dopilnuj podstaw: unikalnych tytułów/opisów przez usługi Title/Meta (lub routerowy TitleStrategy), trasowania HTML5 History — nigdy HashLocationStrategy — prawdziwych linków <a href>, bezpiecznie wstrzykiwanego JSON-LD oraz weryfikacji przez URL Inspection. Dynamiczne renderowanie jest uznanym przez Google rozwiązaniem tymczasowym, a nie strategią.

Evidence for this claim Modern Angular documents @angular/ssr as its server-side and hybrid rendering package. Scope: Current Angular documentation; historical Angular Universal details are not required for this claim. Confidence: high · Verified: Angular: Server-side and hybrid rendering

Domyślna konfiguracja jest problemem: renderowanie po stronie klienta

Standardowy build Angulara dostarcza index.html, którego body składa się zasadniczo z <app-root></app-root> oraz tagów skryptów. Bez uruchomienia JavaScriptu crawler nie widzi żadnych nagłówków, tekstu ani linków — treść jest składana w przeglądarce po załadowaniu pakietu. To ten sam podstawowy problem, który opisuję w JavaScript SEO: web odszedł od zwykłego HTML-a, a CSR jest najbardziej ryzykownym końcem tego spektrum.

(Szczegóły implementacyjne tego przewodnika — API RenderMode, wyzwalacze hydracji i odtwarzanie zdarzeń — odzwierciedlają Angulara v22. Funkcje zależne od wersji poniżej są opatrzone datami: zmiana nazwy SSR w v17, odtwarzanie zdarzeń w v18 oraz inkrementalna hydracja w wersjach v19–v20.)

CSR tworzy trzy odrębne problemy SEO:

  1. Opóźnione i mniej niezawodne indeksowanie. Google przetwarza aplikacje JS w trzech fazach — „Crawling, Rendering, and Indexing” (tłumaczenie) „crawlowanie, renderowanie i indeksowanie” — a renderowanie trafia do kolejki. Twoja treść nie istnieje dla indeksowania, dopóki ten render się nie wykona.
  2. Gorsze Core Web Vitals. LCP cierpi, ponieważ przeglądarka musi pobrać i wykonać pakiet, zanim wyświetli znaczącą treść.
  3. Crawlery inne niż Google widzą powłokę. Bingbot jest znacznie mniej spójny w renderowaniu JS, a boty podglądów społecznościowych i linków zwykle w ogóle nie renderują — dlatego tagi Open Graph i treść wstrzykiwane po stronie klienta do nich nie docierają.

Jak Googlebot faktycznie obsługuje aplikację Angular

Googlebot jest evergreen — renderuje przy użyciu aktualnej wersji silnika V8 Chrome i aktualizuje się razem z wydaniami Chrome. Evidence for this claim Googlebot uses an evergreen version of Chromium for rendering. Scope: Google Search rendering engine; this does not remove application-level rendering risks. Confidence: high · Verified: Google: Evergreen Googlebot Potrafi więc uruchomić Angulara. Jego zachowanie ma jednak twarde ograniczenia, które warto uwzględnić w projekcie:

  • Renderowanie trafia do kolejki i nie jest natychmiastowe. Google opisuje odrębne fazy crawlowania, renderowania i indeksowania i nie publikuje stałego harmonogramu, po jakim renderowanie nadąża za crawlowaniem. Branżowy skrót to „dwie fale” — najpierw indeksowany jest surowy HTML (w przypadku Angulara CSR pusta powłoka), a później wyrenderowany DOM, gdy renderowanie się wykona. SSR/prerenderowanie omija oczekiwanie: HTML jest kompletny przy pierwszym pobraniu, więc treść nie czeka na osobny przebieg renderowania.
  • Renderer jest bezstanowy. Nie przenosi między ładowaniami cookies, localStorage ani sessionStorage; odrzuca prośby o uprawnienia. Nie uzależniaj treści od stanu klienta.
  • Nie klika ani nie przewija i renderuje przy bardzo wysokim viewportcie. Treść ukryta za interakcją nie zostanie zobaczona.
  • Zasoby są agresywnie cachowane — używaj nazw plików z odciskiem treści (domyślne w Angularze main.<hash>.js), aby zaktualizowane pakiety nie były serwowane jako nieaktualne.

@angular/ssr — czym jest i skąd wzięła się nazwa

Renderowanie po stronie serwera uruchamia Angulara na serwerze Node dla każdego żądania i zwraca w pełni wyrenderowany HTML, który przeglądarka następnie hydratuje (podłącza listenery zdarzeń do istniejącego DOM-u zamiast renderować go ponownie). Każdy crawler — Googlebot, Bingbot i boty społecznościowe — dostaje kompletny HTML przy pierwszym żądaniu, bez potrzeby drugiej fali.

Nazwy bywają mylące, więc zachowaj precyzję: „Angular Universal” był historycznym rozwiązaniem SSR dostarczanym jako zewnętrzny pakiet (@nguniversal/express-engine). W Angularze v17 (listopad 2023) SSR zintegrowano bezpośrednio z Angular CLI i Application Builderem oraz przemianowano na @angular/ssr; repozytorium Angular Universal jest obecnie w trybie utrzymania. Evidence for this claim Modern Angular documents @angular/ssr as its server-side and hybrid rendering package. Scope: Current Angular documentation; historical Angular Universal details are not required for this claim. Confidence: high · Verified: Angular: Server-side and hybrid rendering Konfiguracja sprowadza się teraz do jednego polecenia:

# New project with SSR enabled
ng new my-app --ssr

# Add SSR to an existing project
ng add @angular/ssr

Zastępuje ono stare ng add @nguniversal/express-engine. Pomysł jest ten sam, ale narzędzie jest teraz obsługiwane jako funkcja pierwszej klasy.

Prerenderowanie (generowanie statyczne / SSG)

Prerenderowanie generuje statyczny HTML dla tras podczas budowania, więc w chwili żądania nie uruchamia się żaden serwer — możesz wdrożyć aplikację w CDN. Daje najszybsze TTFB/FCP/LCP i najniższy koszt operacyjny. W wersji v17+ konfigurujesz je dla każdej trasy w pliku tras serwera (app.routes.server.ts) za pomocą RenderMode.Prerender, a outputMode: 'static' tworzy w pełni statyczną aplikację.

Ograniczenie jest takie, że dane muszą być dostępne podczas budowania, nie ma treści per użytkownik, a bardzo duże witryny oznaczają długie buildy. To idealne rozwiązanie dla stron marketingowych, dokumentacji i wpisów blogowych — dokładnie tych treści, które najbardziej potrzebują rankingu i cytowania.

Renderowanie hybrydowe — wybierz tryb dla każdej trasy

Największy postęp w wersji v17+ polega na tym, że tryb renderowania jest decyzją dla każdej trasy, konfigurowaną w app.routes.server.ts:

  • RenderMode.Prerender — trasy statyczne (strona główna, o nas, wpisy blogowe).
  • RenderMode.Server — trasy dynamiczne, zależne od żądania (wyniki wyszukiwania, dashboardy ze świeżymi danymi).
  • RenderMode.Client — trasy wyłącznie wewnętrzne, których i tak nie chcesz indeksować (panele administracyjne, ekrany tylko dla zalogowanych). CSR jest tu naprawdę w porządku.

W praktyce zasada decyzyjna wygląda tak: prerenderuj to, co statyczne, renderuj na serwerze to, co musi być świeże, a po renderowanie po stronie klienta sięgaj tylko dla rzeczy, które nie powinny trafić do indeksu.

Hydracja — i pułapka, która niszczy CLS

Naiwne SSR ma wadę: serwer wysyła HTML, a następnie przeglądarka wyrzuca go i renderuje wszystko od nowa, powodując migotanie i marnując pracę. Hydracja to naprawia — przeglądarka odtwarza aplikację wyrenderowaną na serwerze i ponownie wykorzystuje pasujący DOM zamiast go niszczyć i tworzyć, podłączając tylko interaktywność. Włącz ją w app.config.ts:

provideClientHydration()

Warunek wstępny: hydracja jest klienckim uzupełnieniem SSR, a nie samodzielnym przełącznikiem. provideClientHydration() coś robi tylko na trasie, która już jest renderowana po stronie serwera (albo prerenderowana) — nie potrafi zamienić powłoki trasy RenderMode.Client w HTML serwera. Najpierw włącz SSR/prerenderowanie dla tej trasy.

Dwie nowsze możliwości mają znaczenie dla UX sąsiadującego z SEO — żadna nie zmienia indeksowania przez Search:

  • Odtwarzanie zdarzeń (v18+): przechwytuje obsługiwane interakcje użytkownika, które nastąpią przed zakończeniem hydracji, i odtwarza je po jej zakończeniu. Ogranicza utratę kliknięć dla obsługiwanych typów interakcji; jest funkcją UX/interaktywności, a nie SEO, i nie wpływa na to, co indeksuje Google.
  • Inkrementalna hydracja (wersja deweloperska v19, stabilna w v20): zależy łącznie od SSR, hydracji, widoków odraczalnych i odtwarzania zdarzeń — nie jest funkcją samodzielną. Działa przez bloki @defer z wyzwalaczami hydracji, które decydują, które granice pozostają odwodnione przy renderowaniu początkowym; granica hydrate never pozostaje odwodniona przy pierwszym ładowaniu, ale nie musi być zablokowana przed załadowaniem zależności podczas późniejszego renderowania po stronie klienta (np. po zmianie trasy). Wpływ istotny dla SEO jest pośredni: mniejsza ilość JavaScriptu hydratującego się z góry może pomóc LCP, ale konfiguracja wyzwalacza dotyczy renderowania, a nie czasu działania crawlera.

Krytyczna pułapka: umieszczenie bezpośrednio w szablonie @if (isPlatformBrowser(...)) powoduje, że serwer i klient renderują inny markup — niezgodność hydracji — co wywołuje przesunięcie układu i pogarsza CLS. Zamiast sprawdzać platformę w gałęzi szablonu, użyj afterNextRender() do pracy tylko w przeglądarce.

Tytuły i tagi meta — wbudowane usługi Angulara

Angular nie może bezpośrednio powiązać tekstu elementu <title>, więc tagami w head zarządzasz przez dwie usługi z @angular/platform-browser:

  • TitlesetTitle() / getTitle().
  • MetaaddTag(), addTags(), updateTag(), getTag(), removeTag(), z selektorami takimi jak name='description' lub property='og:title'.

Do podstaw nie potrzebujesz biblioteki zewnętrznej. Czystym wzorcem jest współdzielona usługa SEO:

@Injectable({ providedIn: 'root' })
export class SeoService {
  private title = inject(Title);
  private meta = inject(Meta);

  updatePage(title: string, description: string) {
    this.title.setTitle(title);
    this.meta.updateTag({ name: 'description', content: description });
    this.meta.updateTag({ property: 'og:title', content: title });
  }
}

W przypadku tytułów routerowy TitleStrategy (Angular v14+) pozwala ustawić title bezpośrednio w konfiguracji trasy, dzięki czemu tytuł strony aktualizuje się automatycznie przy nawigacji — bez kodu w każdym komponencie. Nie ustawiaj też ręcznie document.title = ...; używaj usługi Title, aby działało to poprawnie pod SSR.

Struktura adresów URL

  • Używaj domyślnego trasowania HTML5 History API (PathLocationStrategy) — czystych adresów, takich jak /products/shoes. W index.html potrzebujesz <base href="/">.
  • Nigdy nie używaj HashLocationStrategy / useHash: true dla publicznych treści. Identyfikatory fragmentów po # są usuwane przed żądaniem HTTP, więc serwer ich nie widzi, a Googlebot nie potrafi niezawodnie rozwiązać #/products — cała witryna może zapaść się do jednego adresu URL.
  • Do nawigacji używaj prawdziwych linków <a href>, także do tras ładowanych leniwie. Lazy loading jest dobry dla wydajności, ale linki do tych tras nadal muszą być indeksowalnymi kotwicami, a nie handlerami kliknięć.

Dane strukturalne (JSON-LD)

Google obsługuje wstrzykiwanie JSON-LD za pomocą JavaScriptu. Solidny wzorzec w Angularze to usługa, która tworzy <script type="application/ld+json"> i dodaje go do document.head, korzystając z tokenu wstrzykiwania Angulara DOCUMENT, zamiast dotykać globalnego document (co psuje działanie na serwerze). Trzymaj cały schemat w jednym miejscu — nie dziel go między statyczny HTML a wyrenderowany DOM — i waliduj przez Rich Results Test oraz URL Inspection.

Dynamiczne renderowanie — starsze obejście, nie plan

Dynamiczne renderowanie dostarcza botom prerenderowaną wersję (przez Puppeteer, Rendertron lub prerender.io), a użytkownikom pełną aplikację SPA. Google mówi wprost, że “dynamic rendering is a workaround and not a long-term solution,” (tłumaczenie) „dynamiczne renderowanie jest rozwiązaniem tymczasowym, a nie długoterminowym” oraz że istnieją “better solutions than dynamic rendering” (tłumaczenie) „lepsze rozwiązania niż dynamiczne renderowanie” — mianowicie renderowanie po stronie serwera, renderowanie statyczne lub hydracja. Nie jest to automatycznie cloaking, jeśli dostarczasz zasadniczo podobną treść, ale dodaje kolejny serwer renderujący, grozi rozjechaniem treści i nic nie daje realnym użytkownikom w zakresie Core Web Vitals. W nowym buildzie Angulara wybierz @angular/ssr albo prerenderowanie.

Typowe błędy SEO Angulara

  1. Trasowanie z haszem (adresy z #) — cała witryna wygląda jak jeden URL.
  2. Brak wywołań Title/Meta — każda strona ma ten sam tytuł i opis.
  3. Brak SSR/prerenderowania — treść istnieje dopiero po odroczonej fali renderowania.
  4. Blokowanie .js/.css w robots.txt — Google nie może renderować i indeksuje pustą powłokę.
  5. Zwracanie 200 dla widoku nieznalezionej strony — miękki 404; zwróć prawdziwy 404 albo dodaj noindex.
  6. document.title = ... zamiast usługi Title.
  7. isPlatformBrowser() wewnątrz szablonowego @if — niezgodność hydracji → CLS.
  8. Dotykanie window/localStorage/document w kodzie uruchamianym na serwerze — awaria SSR.
  9. Dzielenie schematu między surowy HTML a wyrenderowany DOM.
  10. Testowanie w lokalnym trybie deweloperskim zamiast przez URL Inspection — poleganie na założeniach, nie na Googlebocie.

Testowanie Angulara pod kątem SEO

  • URL Inspection (Search Console) jest źródłem prawdy: widok pobranej strony pokazuje wyrenderowany HTML — DOM po uruchomieniu JavaScriptu przez Googlebota, czyli to, co jest indeksowane — a także komunikaty konsoli JS i zablokowane zasoby. Uruchom Live Test, aby wymusić renderowanie na żądanie.
  • curl na adresie URL pokazuje surowy HTML sprzed JavaScriptu — pusta powłoka oznacza CSR bez SSR.
  • Rich Results Test waliduje dane strukturalne.
  • Ahrefs Site Audit (z włączonym renderowaniem JS) i Screaming Frog (tryb JS) porównują surowy i wyrenderowany DOM na dużą skalę.
  • Lighthouse / PageSpeed Insights mierzą wpływ wybranego sposobu renderowania na Core Web Vitals.

Angular nie jest zły dla SEO — jest po prostu inny. Włóż prawdziwy HTML do odpowiedzi, zarządzaj tagami head, utrzymuj indeksowalne adresy i linki, a o tym, co zostało wyrenderowane, pozwól rozstrzygać własnym narzędziom Google. Ten temat sąsiaduje z JavaScript SEO oraz pytaniami o renderowanie bezgłowego CMS-a w tym klastrze — podstawowa lekcja jest taka sama: tryb renderowania decyduje niemal o wszystkim.

Add an expert note

Pin an expert quote

New person? Create their unclaimed profile at /admin/experts/ → Pin a quote first.