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.
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 — Angular domyślnie buduje stronę w przeglądarce za pomocą JavaScriptu, więc surowy HTML, który wyszukiwarka widzi najpierw, jest niemal pusty. Rozwiązaniem jest wysłanie prawdziwego, gotowego HTML-a — przez wbudowane w Angulara renderowanie po stronie serwera (
@angular/ssr) albo prerenderowanie. Potem dopilnuj podstaw: unikalnego tytułu i opisu na każdej stronie, czystych adresów URL (bez#) oraz prawdziwych linków<a href>.
Problem w jednym zdaniu
Domyślna aplikacja Angular wysyła do przeglądarki małą powłokę HTML — zasadniczo jeden pusty
<div> — oraz duży pakiet JavaScriptu. Przeglądarka uruchamia ten JavaScript i dopiero
potem wypełnia stronę treścią. Nazywa się to renderowaniem po stronie klienta (CSR).
Problem polega na tym, że gdy wyszukiwarka po raz pierwszy pobiera ten adres URL, widzi pustą powłokę. Google potrafi uruchomić JavaScript i zobaczyć rzeczywistą treść, ale robi to później, w osobnym kroku, i nie zawsze niezawodnie. Evidence for this claim Google can render JavaScript but processes rendering as a stage after crawling. Scope: Google Search rendering; other crawler behavior is not covered by this record. Confidence: high · Verified: Google: JavaScript SEO basics Inne crawlery — Bing oraz boty stojące za podglądami społecznościowymi — często nie potrafią uruchomić JavaScriptu w ogóle. Widzą więc nic.
Rozwiązanie: wysyłaj gotowy HTML
Zamiast zmuszać przeglądarkę (albo crawlera) do zbudowania strony, budujesz ją wcześniej lub na serwerze i wysyłasz kompletny HTML. Angular daje Ci dwie główne możliwości:
- Renderowanie po stronie serwera (SSR) — serwer uruchamia Angulara dla każdego żądania i odsyła pełną stronę. Dobre dla treści, które często się zmieniają. Evidence for this claim Angular supports server-side rendering and build-time prerendering through its SSR tooling. Scope: Current Angular SSR and prerender features. Confidence: high · Verified: Angular: Server-side and hybrid rendering
- Prerenderowanie — Angular buduje statyczne pliki HTML dla stron podczas budowania, więc nie potrzebujesz serwera. To najszybsza opcja, świetna dla wpisów blogowych i stron marketingowych.
Nowoczesny Angular (wersja 17 i nowsze) ma obie możliwości wbudowane w zestaw narzędzi. Dodajesz je
jednym poleceniem: ng add @angular/ssr. Być może znasz dawną nazwę Angular Universal —
był to ten sam pomysł jako osobny dodatek. W Angularze v17 włączono go do rdzenia i przemianowano.
Pozostałe podstawy
- Nadaj każdej stronie unikalny tytuł i opis. Angular nie zrobi tego za Ciebie — ustawiasz je w kodzie za pomocą wbudowanych usług Angulara
TitleiMeta. Bez tego każda strona ma ten sam tytuł. - Używaj czystych adresów URL, nie adresów z haszem. Domyślne trasowanie Angulara tworzy czytelne adresy, takie jak
/products/shoes. Unikaj starszego stylu „hash” (/#/products) — wyszukiwarki nie potrafią niezawodnie obsłużyć części po#. - Używaj prawdziwych linków. Nawigacja musi korzystać z prawdziwych linków
<a href>, a nie z przycisków lub handlerów kliknięć, inaczej Google nie może za nimi podążać.
Co większość osób rozumie błędnie
„Google nie może indeksować Angulara” to mit — potrafi renderować JavaScript. Jest jednak wolniejszy i mniej niezawodny niż zwykłe wysłanie prawdziwego HTML-a, a crawlery inne niż Google w ogóle sobie z tym nie radzą. Dlatego wszystko, co ma zostać znalezione, renderuj na serwerze albo prerenderuj.
Chcesz poznać głębszą wersję — renderowanie hybrydowe dla poszczególnych tras, hydrację, usługi SEO z kodem oraz testowanie tego, co naprawdę widzi Google? Przejdź do karty Advanced.
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 renderingTL;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ługiTitle/Meta(lub routerowyTitleStrategy), trasowania HTML5 History — nigdyHashLocationStrategy— 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ą.
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:
- 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.
- Gorsze Core Web Vitals. LCP cierpi, ponieważ przeglądarka musi pobrać i wykonać pakiet, zanim wyświetli znaczącą treść.
- 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,
localStorageanisessionStorage; 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/ssrZastę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
@deferz wyzwalaczami hydracji, które decydują, które granice pozostają odwodnione przy renderowaniu początkowym; granicahydrate neverpozostaje 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:
Title—setTitle()/getTitle().Meta—addTag(),addTags(),updateTag(),getTag(),removeTag(), z selektorami takimi jakname='description'lubproperty='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. Windex.htmlpotrzebujesz<base href="/">. - Nigdy nie używaj
HashLocationStrategy/useHash: truedla 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
- Trasowanie z haszem (adresy z
#) — cała witryna wygląda jak jeden URL. - Brak wywołań
Title/Meta— każda strona ma ten sam tytuł i opis. - Brak SSR/prerenderowania — treść istnieje dopiero po odroczonej fali renderowania.
- Blokowanie
.js/.csswrobots.txt— Google nie może renderować i indeksuje pustą powłokę. - Zwracanie
200dla widoku nieznalezionej strony — miękki 404; zwróć prawdziwy404albo dodajnoindex. document.title = ...zamiast usługiTitle.isPlatformBrowser()wewnątrz szablonowego@if— niezgodność hydracji → CLS.- Dotykanie
window/localStorage/documentw kodzie uruchamianym na serwerze — awaria SSR. - Dzielenie schematu między surowy HTML a wyrenderowany DOM.
- 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.
curlna 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.
Podsumowanie AI
Skrócona wersja wariantu Advanced:
- Domyślny Angular korzysta z renderowania po stronie klienta (CSR) — powłoka HTML jest niemal pusta. Oznacza to opóźnione i mniej niezawodne indeksowanie, gorsze LCP oraz to, że crawlery inne niż Google (Bing, boty społecznościowe) nie widzą nic.
- Googlebot jest evergreen i renderuje JS, ale renderowanie trafia do kolejki („dwie fale” — najpierw surowy HTML, później wyrenderowany DOM, bez stałego harmonogramu), jest bezstanowe (bez cookies i magazynu), nie klika ani nie przewija oraz agresywnie cachuje zasoby. SSR/prerenderowanie omija oczekiwanie — HTML jest kompletny przy pierwszym pobraniu, więc nie ma osobnego przebiegu renderowania, na który trzeba czekać.
@angular/ssrto nowoczesne rozwiązanie — SSR jest częścią Angular CLI od v17 i zastępuje utrzymywane zewnętrznie Angular Universal (@nguniversal/express-engine). Konfiguracja:ng new --ssralbong add @angular/ssr.- Prerenderowanie (statyczny HTML budowany podczas kompilacji) jest najszybsze i można je wdrożyć w CDN — najlepsze dla statycznych treści marketingowych, blogowych i dokumentacji; ogranicza je dostępność danych podczas budowania.
- Renderowanie hybrydowe ustawia tryb dla każdej trasy w
app.routes.server.ts:RenderMode.Prerender(statyczne),RenderMode.Server(dynamiczne),RenderMode.Client(wewnętrzne strony nieindeksowane). - Hydracja (
provideClientHydration()) ponownie wykorzystuje HTML serwera na trasie już wyrenderowanej przez SSR/prerenderowanie — nie tworzy HTML serwera dla CSR. Odtwarzanie zdarzeń (v18+) przechwytuje obsługiwane interakcje przed hydracją i odtwarza je po niej; inkrementalna hydracja (wersja zapoznawcza v19 / stabilna v20) zależy razem od SSR + hydracji + widoków odraczalnych + odtwarzania zdarzeń i używa wyzwalaczy hydracji@defer, aby kontrolować granice pozostające odwodnione przy początkowym ładowaniu (hydrate nevernie blokuje późniejszych ładowań po stronie klienta). Żadna z tych funkcji nie zmienia indeksowania w Search. UnikajisPlatformBrowser()w szablonowym@if— powoduje niezgodność hydracji → CLS; użyjafterNextRender(). - Używaj wbudowanych usług
TitleiMeta(nie jest potrzebna biblioteka zewnętrzna); routerowyTitleStrategy(v14+) ustawia tytuły dla poszczególnych tras automatycznie. - Używaj trasowania HTML5 History, nigdy adresów z haszem (
#); nawigacja musi opierać się na prawdziwych kotwicach<a href>. JSON-LD można wstrzyknąć przez tokenDOCUMENT. - Dynamiczne renderowanie jest rozwiązaniem tymczasowym, nie strategią — Google zaleca SSR/renderowanie statyczne/hydrację.
- Testuj przez URL Inspection (wyrenderowany HTML),
curl(surowy HTML), Rich Results Test i crawler renderujący JS. AngularJS ≠ Angular — stare wskazówki dla AngularJS nie mają zastosowania.
Dokumentacja oficjalna
Dokumentacja źródeł pierwotnych Google i Angulara.
- Podstawy SEO JavaScriptu — fazy crawlowania → renderowania → indeksowania, indeksowalne linki
<a href>, ostrzeżenia dotyczące trasowania fragmentów, miękkie 404s oraz dane strukturalne wstrzykiwane przez JS. - Dynamiczne renderowanie (obejście) — dlaczego jest rozwiązaniem tymczasowym, niuans dotyczący cloakingu oraz alternatywy w postaci SSR/renderowania statycznego/hydracji.
- Renderowanie w internecie (web.dev — Addy Osmani i Jason Miller) — kanoniczne definicje SSR, CSR, renderowania statycznego i hydracji oraz kompromisy wydajnościowe.
- Narzędzie URL Inspection — jak zobaczyć wyrenderowany HTML faktycznie indeksowany przez Google, a także komunikaty konsoli JS.
Angular
- Angular — renderowanie po stronie serwera i hybrydowe (SSR) — oficjalny przewodnik po
@angular/ssr, trasach serwera iRenderMode. - Angular — usługa
Title—setTitle()/getTitle(). - Angular — usługa
Meta—addTag(),updateTag()i selektory. - Angular — opis routera —
PathLocationStrategykontra trasowanie z haszem orazTitleStrategy. - Wprowadzenie Angulara v17 (blog zespołu Angular) — SSR staje się funkcją pierwszej klasy CLI.
- Angular Universal (tryb utrzymania) — historyczny pakiet, obecnie zastąpiony przez
@angular/ssr.
Cytaty ze źródła
Wypowiedzi zapisane przez Google oraz fragmenty mojego własnego tekstu. Każdy link wyszukiwarki prowadzi bezpośrednio do cytowanego fragmentu na stronie źródłowej.
Google — jak przetwarzane są aplikacje JavaScript
- “Google processes JavaScript web apps in three main phases: 1. Crawling 2. Rendering 3. Indexing.” (tłumaczenie) „Google przetwarza aplikacje webowe JavaScript w trzech głównych fazach: 1. crawlowanie, 2. renderowanie, 3. indeksowanie.” — dokumentacja Google Search Central. Przejdź do cytatu
- “Google can only discover your links if they are <a> HTML elements with an href attribute.” (tłumaczenie) „Google może odkrywać Twoje linki tylko wtedy, gdy są elementami HTML <a> z atrybutem href.” — dokumentacja Google Search Central. Przejdź do cytatu
Google — dynamiczne renderowanie jest rozwiązaniem tymczasowym
- “Dynamic rendering was a workaround and not a long-term solution for problems with JavaScript-generated content in search engines.” (tłumaczenie) „Dynamiczne renderowanie było rozwiązaniem tymczasowym, a nie długoterminowym rozwiązaniem problemów z treścią generowaną przez JavaScript w wyszukiwarkach.” — dokumentacja Google Search Central. Przejdź do cytatu
web.dev — wybieraj SSR / renderowanie statyczne (Addy Osmani i Jason Miller)
- “Rendering an app on the server to send HTML, rather than JavaScript, to the client.” (tłumaczenie) „Renderowanie aplikacji na serwerze w celu wysłania do klienta HTML-a, a nie JavaScriptu.” — definicja renderowania po stronie serwera. Przejdź do cytatu
Patrick Stox (moja praca — JavaScript SEO: kompletny przewodnik)
- “JavaScript is not bad for SEO, and it’s not evil. It’s just different from what many SEOs are used to.” (tłumaczenie) „JavaScript nie jest zły dla SEO ani nie jest z natury zły. Po prostu różni się od tego, do czego przyzwyczaiło się wielu specjalistów SEO.”
- “Any kind of SSR, static rendering, and prerendering setup is going to be fine for search engines.” (tłumaczenie) „Każda konfiguracja SSR, renderowania statycznego i prerenderowania będzie odpowiednia dla wyszukiwarek.”
Lista kontrolna SEO Angulara
Szybki przegląd potwierdzający, że aplikacja Angular jest indeksowalna i możliwa do crawlowania:
- Publiczna treść jest dostarczana jako prawdziwy HTML przez
@angular/ssr(SSR) albo prerenderowanie, a nie pełne CSR. - Tryb renderowania jest ustawiony dla każdej trasy w
app.routes.server.ts— prerenderuj trasy statyczne, renderuj dynamiczne na serwerze, a po stronie klienta renderuj tylko wewnętrzne strony nieindeksowane. - Hydracja jest włączona (
provideClientHydration()), a żaden szablon nie używaisPlatformBrowser()wewnątrz@if(zamiast tego użyjafterNextRender()). - Każda strona ustawia unikalny tytuł (przez usługę
Titlelub routerowyTitleStrategy) i unikalny opis (przez usługęMeta). - Tagi Open Graph / Twitter Card są ustawione przez usługę
Metana potrzeby podglądów społecznościowych. - Trasowanie używa HTML5 History API (domyślnie) z
<base href="/">— a nieHashLocationStrategy/useHash: true. - Cała nawigacja korzysta z prawdziwych kotwic
<a href>, także z linków do tras ładowanych leniwie. -
robots.txtnie blokuje zasobów.jsani.css. - Widoki nieznalezionych stron po stronie klienta zwracają prawdziwe
404albo mająnoindex(bez miękkich 404). - W kodzie uruchamianym na serwerze nie ma dostępu do
window/localStorage/document(użyj tokenuDOCUMENT/ strażników platformy). - JSON-LD znajduje się w jednym miejscu i przechodzi Rich Results Test.
- Sprawdziłeś wyrenderowany HTML w URL Inspection — nie tylko lokalny tryb deweloperski.
Modele myślowe
1. Włóż prawdziwy HTML do odpowiedzi. Niemal każdy problem SEO Angulara sprowadza się do jednego pytania: czy crawler dostaje gotowy HTML przy pierwszym pobraniu, czy powłokę, którą musi wyrenderować? SSR i prerenderowanie odpowiadają „tak”. CSR odpowiada „ostatecznie, być może”. Od tego zacznij każdy audyt.
2. Zasada decyzji o renderowaniu dla każdej trasy.
- Treść statyczna (strona główna, o nas, blog, dokumentacja) →
RenderMode.Prerender. - Treść dynamiczna, która musi być świeża (wyszukiwanie, dane na żywo) →
RenderMode.Server. - Strony wewnętrzne lub tylko dla zalogowanych, których nie chcesz indeksować →
RenderMode.Clientjest odpowiedni.
3. „Angular Universal” i „@angular/ssr” to ten sam pomysł w różnych epokach.
Universal był pakietem zewnętrznym; w v17 SSR trafił do CLI i zmienił nazwę. Jeśli używasz
nowoczesnego Angulara, potrzebujesz @angular/ssr — repozytorium Universal jest w trybie utrzymania.
4. Hydratuj, nie renderuj ponownie.
Naiwne SSR wysyła HTML, a potem go wyrzuca. provideClientHydration() ponownie go wykorzystuje —
ale tylko na trasie, która już jest prerenderowana lub renderowana przez SSR; nie tworzy HTML serwera
dla trasy CSR. Odtwarzanie zdarzeń i inkrementalna hydracja (@defer) ograniczają początkową ilość
JavaScriptu. Nigdy nie używaj isPlatformBrowser() w szablonie — tak powstaje niezgodność hydracji
i problem z CLS.
5. Tagi head to Twoje zadanie, nie Angulara.
Nie ma tu Yoasta. Ustawiaj tytuły i opisy świadomie przez usługi Title/Meta (albo
TitleStrategy), dla każdej strony. „Wszystkie strony mają jeden tytuł” to domyślna awaria,
a nie pech.
6. Projektuj pod bezstanowego bota i czyste adresy URL.
Trasowanie History API, prawdziwe linki <a href>, brak zależności od cookies/magazynu i żadnej treści
ukrytej za kliknięciem. Potem pozwól URL Inspection — a nie własnemu laptopowi — powiedzieć Ci, co
zostało wyrenderowane.
SEO Angulara — ściąga
Tryby renderowania
| Tryb | Gdzie budowany jest HTML | SEO | Najlepsze zastosowanie | Konfiguracja Angulara |
|---|---|---|---|---|
| Prerender (SSG) | Czas budowania → pliki statyczne | ✅ Najlepsze | Statyczne strony marketingowe/blog/dokumentacja | RenderMode.Prerender |
| SSR | Serwer, dla każdego żądania | ✅ Bardzo dobre | Dynamiczna treść, która musi być świeża | RenderMode.Server / @angular/ssr |
| Client (CSR) | W przeglądarce | ⚠️ Ryzykowne | Strony wewnętrzne/dla zalogowanych (nieindeksowane) | RenderMode.Client |
| Dynamiczne renderowanie | Osobny serwer botów | Tylko obejście | Starsze aplikacje, których nie można przenieść | Puppeteer / Rendertron / prerender.io |
Polecenia konfiguracji
| Cel | Polecenie |
|---|---|
| Nowy projekt z SSR | ng new my-app --ssr |
| Dodaj SSR do istniejącej aplikacji | ng add @angular/ssr |
| Włącz hydrację | provideClientHydration() w app.config.ts |
Szybkie zasady head i trasowania
- Tytuły/opisy: usługi Angulara
TitleiMeta(bez potrzeby biblioteki zewnętrznej). - Automatyczne tytuły dla tras: routerowy
TitleStrategy(v14+) przez właściwość trasytitle. - Trasowanie: HTML5 History API +
<base href="/">. NigdyuseHash: true. - Linki: prawdziwe kotwice
<a href>— także do tras ładowanych leniwie. - JSON-LD: wstrzykuj przez token
DOCUMENT; trzymaj go w jednym miejscu.
Nazewnictwo
- Angular Universal = dawny pakiet zewnętrzny (
@nguniversal/express-engine), obecnie w trybie utrzymania. @angular/ssr= to samo SSR, wbudowane w CLI od v17.- AngularJS (v1.x) ≠ Angular (v2+) — różne frameworki; stare porady dla AngularJS nie mają zastosowania.
Pułapki
isPlatformBrowser()w szablonowym@if→ niezgodność hydracji → CLS. UżyjafterNextRender().window/localStorage/documentna serwerze → awaria SSR.- Blokowanie
.js/.cssw robots.txt → Google nie może renderować.
Sprawdź, czy aplikacja Angular rzeczywiście jest renderowana na serwerze
Najszybszy sposób, aby sprawdzić, czy adres URL działa jako CSR, czy jako SSR/prerenderowanie,
to pobrać surowy HTML (zanim uruchomi się JavaScript) i poszukać w nim prawdziwej treści. Aplikacja
Angular oparta wyłącznie na CSR zwraca niemal pusty <app-root>; aplikacja SSR/prerenderowana zwraca
gotowy markup.
macOS / Linux
# Raw HTML as the server sends it — this is the "first fetch", pre-JS
curl -sL -A "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" \
https://example.com/page/ -o raw.html
# Is your real headline in the raw HTML? Empty result = CSR with no SSR
grep -o "Your headline text" raw.html
# A near-empty <app-root> is the tell-tale CSR signature
grep -o "<app-root></app-root>" raw.htmlWindows (PowerShell)
$ua = "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"
Invoke-WebRequest -Uri "https://example.com/page/" -UserAgent $ua -OutFile raw.html
Select-String -Path raw.html -Pattern "Your headline text"
Select-String -Path raw.html -Pattern "<app-root></app-root>"Jeśli brakuje nagłówka i widzisz samo <app-root>, treść zależy od renderowania — dodaj SSR lub
prerenderowanie. (Dla wyrenderowanego DOM-u użyj w URL Inspection opcji „View Crawled Page →
rendered HTML”; zwykły curl nie uruchamia JavaScriptu.)
Sprawdź, czy nie blokujesz JavaScriptu/CSS Angulara w robots.txt
macOS / Linux
curl -sL https://example.com/robots.txt | grep -iE "disallow.*\.(js|css)|Disallow:\s*/(assets|dist|main)"Dyrektywa Disallow pasująca do Twojego pakietu oznacza, że Google nie może poprawnie renderować
strony — niemal zawsze jest to błąd.
Narzędzia do debugowania SEO Angulara
- URL Inspection (Google Search Console) — źródło prawdy. Uruchom Live Test, a następnie odczytaj wyrenderowany HTML (DOM po uruchomieniu JavaScriptu Angulara przez Googlebota), zrzut ekranu, zasoby strony (co się załadowało, a co zablokowano) oraz komunikaty konsoli JavaScriptu.
- Rich Results Test — potwierdź, że JSON-LD jest obecny w wyrenderowanym wyniku po każdej zmianie renderowania.
curl— pobierz surowy HTML sprzed JavaScriptu, aby odróżnić CSR (pusty<app-root>) od SSR/prerenderowania.- Ahrefs Site Audit (z włączonym renderowaniem JS) — crawluje za pomocą headless Chrome i porównuje surowy DOM z wyrenderowanym, wskazując brakujące metadane, uszkodzone kanonikale i problemy z indeksowalnością na dużą skalę.
- Screaming Frog SEO Spider (tryb renderowania JS) — porównuj surową i wyrenderowaną treść dla każdego adresu URL.
- Lighthouse / PageSpeed Insights — mierz wpływ strategii renderowania (CSR kontra SSR/prerenderowanie) na Core Web Vitals.
- Angular CLI / DevTools — potwierdź konfigurację
outputMode, tras serwera i hydracji.
Zasoby warte Twojego czasu
Moje powiązane teksty
- JavaScript SEO: kompletny przewodnik — mój pełny przewodnik po renderowaniu, zgodności DOM-u, zasadzie najbardziej restrykcyjnej dyrektywy i konfiguracjach renderowania bezpiecznych dla wyszukiwarek. SEO Angulara jest szczególnym zastosowaniem wszystkich tych zagadnień.
- Przewodnik po technicznym SEO dla początkujących — o tym, gdzie renderowanie i crawlowanie mieszczą się w szerszym obrazie.
Moje wystąpienia
- JavaScript SEO — Ungagged 2019 (SlideShare) — bezstanowe renderowanie Googlebota, viewport i cachowanie oraz ówczesne podejścia do renderowania. (Obowiązuje stałe zastrzeżenie: rekomendacja dynamicznego renderowania w tamtej prezentacji jest dziś nieaktualna — Google od tego czasu nazwało je rozwiązaniem tymczasowym.)
Z branży
- Renderowanie w internecie (web.dev) — kanoniczny tekst Addy’ego Osmani i Jasona Millera o kompromisach SSR, CSR, renderowania statycznego i hydracji.
- Renderowanie po stronie serwera i hybrydowe (SSR) (angular.dev) — oficjalny przewodnik po
@angular/ssr: trasach serwera,RenderMode, prerenderowaniu i hydracji. - Wprowadzenie Angulara v17 (blog zespołu Angulara) — wydanie, które uczyniło SSR funkcją pierwszej klasy CLI i wprowadziło pakiet
@angular/ssr. - Angular Universal (tryb utrzymania) (GitHub) — historyczny pakiet SSR zastąpiony przez
@angular/ssr, przydatny do zrozumienia zmiany nazwy. - Angular SEO Guide (Search Engine Journal, Jamie Indigo) — klasyczne omówienie indeksowania w dwóch falach; mocne w podstawach, choć powstało przed zmianą nazwy w v17.
- Przewodnik po SSR Angulara (Angular Architects, Alexander Thalhammer, marzec 2025) — techniczny przewodnik po konfiguracji SSR; sprawdzaj przykłady kodu względem oficjalnej dokumentacji
@angular/ssr, bo API mogło się zmienić od publikacji. - r/TechSEO — społeczność zajmująca się debugowaniem renderowania i indeksowania.
Antywzorce SEO Angulara
Konkretne błędy, które regularnie widzę w aplikacjach Angular — każdy z nich to nawyk, który warto sprawdzać bezpośrednio, a nie tylko teoretyczne ryzyko.
Wysyłanie builda wyłącznie CSR i uznawanie sprawy za zakończoną
Domyślny wynik ng new nie ma SSR ani prerenderowania. To najszybszy sposób na rozpoczęcie projektu
i najłatwiejszy sposób na skończenie z pustym <app-root> przy pierwszym pobraniu. Dlaczego to
błąd: surowy HTML widziany przez crawlera nie ma treści, więc indeksowanie zależy całkowicie od
odroczonego przebiegu renderowania Google — a inne boty w ogóle nie dostają drugiej szansy.
Zrób zamiast tego: dodaj @angular/ssr na początku projektu (ng new my-app --ssr) albo
uruchom ng add @angular/ssr dla istniejącego projektu, zanim wdrożysz cokolwiek, co ma zostać znalezione.
Używanie HashLocationStrategy dla publicznych tras
Trasowanie z haszem (useHash: true, adresy takie jak /#/products/shoes) nadal jest domyślne w
niektórych starszych samouczkach Angulara i kodzie startowym. Dlaczego to błąd: wszystko po #
jest usuwane po stronie klienta, zanim żądanie dotrze do serwera, więc serwer — i Googlebot — widzi
zawsze tylko jeden adres URL dla całej aplikacji. Zrób zamiast tego: użyj domyślnego trasowania
HTML5 History API (PathLocationStrategy) z <base href="/"> w index.html.
Warunkowanie szablonu przez isPlatformBrowser()
Opakowanie treści w @if (isPlatformBrowser(platformId)) wydaje się oczywistym sposobem ochrony kodu
działającego tylko w przeglądarce. Dlaczego to błąd: serwer renderuje jedną gałąź, a klient inną
podczas hydracji, co tworzy niezgodność hydracji — Angular musi uzgodnić różnicę, a widoczny rezultat
to przesunięcie układu pojawiające się jako CLS. Zrób zamiast tego: użyj afterNextRender() dla
pracy tylko w przeglądarce, aby sam szablon renderował się identycznie po stronie serwera i klienta.
Bezpośrednie ustawianie document.title zamiast korzystania z usługi Title
Działa w lokalnym trybie deweloperskim, więc łatwo sięgnąć po ten skrót. Dlaczego to błąd:
bezpośredni dostęp do DOM-u, taki jak document.title = '...', nie współpracuje dobrze z SSR — serwer
nie ma globalnego document w takim sensie jak przeglądarka, a Ty tracisz korzyści z integracji
routera z obsługą tytułów Angulara. Zrób zamiast tego: wstrzyknij usługę Angulara Title
(setTitle()) albo konfiguruj tytuły dla tras przez routerowy TitleStrategy.
Dotykanie window, localStorage lub document w kodzie uruchamianym podczas SSR
Komponent lub usługa odczytująca localStorage albo sprawdzająca window.innerWidth w chwili
konstruowania działa poprawnie w przeglądarce, ale powoduje awarię renderowania na serwerze.
Dlaczego to błąd: żaden z tych globalnych obiektów nie istnieje w procesie serwera Node
uruchamiającym build SSR, więc renderowanie rzuca wyjątek, a żądanie kończy się odpowiedzią 500s albo
po cichu wraca z pustą odpowiedzią. Zrób zamiast tego: zabezpiecz ten kod przez
afterNextRender() albo wstrzyknij token Angulara DOCUMENT zamiast globalnego obiektu i testuj
build SSR lokalnie (ng build + serwowanie wyniku SSR), a nie tylko przez ng serve.
Traktowanie dynamicznego renderowania jak stałego rozwiązania
Uruchomienie Puppeteera lub usługi takiej jak Rendertron w celu serwowania botom prerenderowanego
snapshotu rozwiązuje natychmiastowy objaw. Dlaczego to błąd: to kolejny system do utrzymania,
może rozjechać się z tym, co widzą realni użytkownicy, a Google wprost mówi, że jest to rozwiązanie
tymczasowe, a nie długoterminowe. Zrób zamiast tego: przenieś aplikację do @angular/ssr albo
prerenderowania, aby każdy odbiorca — bot czy człowiek — dostał ten sam prawdziwy HTML z tego samego
potoku.
Jaki tryb renderowania wybrać dla tej trasy?
Angular v17+ pozwala ustawić tryb renderowania dla każdej trasy w app.routes.server.ts. Pytanie
nie brzmi „SSR czy prerenderowanie dla całej aplikacji?” — zadaje się je osobno dla każdej trasy.
Choosing a rendering mode for an Angular route
Prompty do pracy nad SEO Angulara
Gotowe do skopiowania prompty dla konkretnych zadań SEO Angulara opisanych w tym artykule. Wklej opisane dane wejściowe i sprawdź wynik według własnego osądu — prompty oszczędzają czas przy mechanicznych czynnościach, ale nie zastępują testowania przez URL Inspection.
1. Porównaj surowy i wyrenderowany HTML trasy
Wklej wynik curl -sL <url> (surowy HTML) oraz panel „rendered HTML” z testu Live Test w URL
Inspection (albo crawl z renderowaniem JS w Ahrefs/Screaming Frog) dla tego samego adresu URL.
Here is the raw HTML for [URL] (fetched with curl, before JavaScript runs):
[paste raw HTML]
Here is the rendered HTML for the same URL (from Google Search Console URL
Inspection's Live Test, or a JS-rendering crawler):
[paste rendered HTML]
Compare the two. List: (1) content present in rendered but missing from raw —
this is what depends on client-side rendering, (2) any <title>, meta description,
or JSON-LD that differs between the two versions, (3) whether the raw HTML shows
a near-empty <app-root> (a sign of CSR with no SSR/prerendering).Oczekuj krótkiej listy elementów zależnych od CSR oraz rozbieżności tytułu/metadanych/schematu między surową a wyrenderowaną wersją — to dwie rzeczy, które warto naprawić jako pierwsze.
2. Przejrzyj implementację usługi Title/Meta
Wklej swoją usługę Angulara SeoService (albo odpowiednik), która wywołuje usługi Title i Meta.
Here is an Angular service that sets page titles and meta tags:
[paste the service's TypeScript code]
Check it against these rules: (1) titles are set via the Title service's
setTitle(), never document.title directly, (2) description is set with
meta.updateTag({ name: 'description', ... }) not addTag() (which can duplicate
the tag on repeat calls), (3) Open Graph tags use the property selector, not
name, (4) nothing in this code reads window/localStorage/document directly in a
way that would break during SSR. Flag any line that violates one of these and
suggest the fix.Oczekuj przejścia pass/fail linia po linii względem tych czterech zasad oraz poprawionego fragmentu dla każdego wykrytego problemu.
3. Przeprowadź audyt app.routes.server.ts pod kątem błędów trybu renderowania
Wklej plik konfiguracji tras serwera.
Here is my Angular app.routes.server.ts, which sets RenderMode per route:
[paste the file]
For each route, tell me: is RenderMode.Prerender used on anything that depends
on per-request or per-user data (a mistake — it would bake stale/wrong data into
the static build)? Is RenderMode.Client used on anything that looks like public,
indexable content (a missed-SEO-opportunity)? Is RenderMode.Server used on fully
static content where Prerender would be faster and cheaper? List each route with
its current mode and whether it matches the decision rule: static data →
Prerender, must-be-fresh → Server, non-indexed internal → Client.Oczekuj werdyktu dla każdej trasy, wskazującego trasy, których RenderMode nie odpowiada temu,
czego rzeczywiście potrzebują.
Sprawdź się: SEO Angulara
Pięć krótkich pytań o tym, jak sprawić, aby aplikacja Angular była możliwa do crawlowania i indeksowania. Wybierz odpowiedź na każde pytanie, a następnie sprawdź wynik.
Dziennik zmian
Zaktualizowano 8 sie 2026.
Podsumowanie redakcyjne i zapisane szczegóły zmian.Szczegóły zmian
-
Szczegółowe uwagi dotyczące zmian są obecnie dostępne po angielsku.
Pełne porównanie jest niedostępne — dla tej wersji nie zarchiwizowano wcześniejszej migawki.
Zaktualizowano 17 lip 2026.
Podsumowanie redakcyjne i zapisane szczegóły zmian.Szczegóły zmian
-
Szczegółowe uwagi dotyczące zmian są obecnie dostępne po angielsku.
-
Szczegółowe uwagi dotyczące zmian są obecnie dostępne po angielsku.
-
Szczegółowe uwagi dotyczące zmian są obecnie dostępne po angielsku.
Pełne porównanie jest niedostępne — dla tej wersji nie zarchiwizowano wcześniejszej migawki.