Przeniesienie platformy z WordPressa do Astro miało być całym projektem. Okazało się prologiem. Eksport treści, odbudowa szablonów, doprowadzenie statycznej witryny do kompilacji i wdrożenia na Cloudflare Pages zajęło tygodnie. Potem zaczął się właściwy rok: przekierowania, hreflang, parytet sześciu wersji językowych i build, który przerósł platformę, na której był wdrażany. To jest raport o tym, gdzie poszedł czas, bo czas nie poszedł tam, gdzie zakładał plan.
Polemika, jeśli już jakaś jest, dotyczy ujmowania przeniesienia platformy jako jednorazowego portu. “Zejdź z WordPressa na witrynę statyczną” brzmi jak jednorazowa migracja. Dla wielojęzycznej witryny treściowej jest to bliżej przejęcia odpowiedzialności za trzy systemy, które WordPress dotąd ukrywał: warstwę routingu, build i strukturę między wersjami językowymi. Żaden z nich nie jest trudny. Wszystkie są ciągłe.
[!NOTE] Projekt w skrócie
- Projekt: wppoland.com przeniesiony z WordPressa na Astro na Cloudflare Pages, przebudowa naszej własnej strony
- Zakres: sześć języków, ponad 14 000 prerenderowanych stron z opisowymi slugami
- Czas: tygodnie do działającego buildu statycznego, około dwunastu miesięcy do stabilnej, wolnej od regresji widoczności w wyszukiwarkach
- Build: przekroczył sufit 8 GB runnera Cloudflare Pages, rozwiązane budowaniem lokalnie ze stertą 16 GB i wdrażaniem artefaktu przez Wrangler
- Przekierowania: tysiące reguł 301, które trafiły na limit 100 KB pliku
_redirectsi zostały przeniesione do warstwy Cloudflare Functions- Stack: Astro z Tailwind CSS, potok obrazów AVIF, statyczny HTML serwowany z brzegu sieci
- Efekt: TTFB poniżej 40 ms globalnie, zerowa dynamiczna powierzchnia ataku, przewidywalny dostęp dla crawlerów AI, utrzymany parytet sześciu języków
Migracja WordPress do Astro, prawdziwy koszt: TL;DR w 4 punktach
- Przeniesienie to tania część. Szablony i eksport treści zajęły tygodnie; migracja potrzebowała około dwunastu miesięcy, by osiągnąć stabilne wyniki wyszukiwania bez regresji.
- Warstwa przekierowań to pierwsza niespodzianka. Tysiące wcześniej zaindeksowanych adresów potrzebują własnego kodu 301, a sama ich liczba zderzyła się z limitem rozmiaru pliku na Cloudflare Pages, który po cichu odrzucał reguły.
- Parytet sześciu wersji językowych to praca ciągła, nie zadanie. Hreflang, adresy kanoniczne i struktura sekcji muszą pozostać zgodne we wszystkich wersjach językowych bez wyjątku.
- Build przerósł własny runner platformy. Sufit 8 GB pamięci nie wystarcza na 14 000 prerenderowanych stron; odpowiedzią było budowanie lokalnie ze stertą 16 GB i wdrażanie gotowego artefaktu przez CLI.
Słownik: build statyczny, prerender, hreflang, brzeg sieci
Raport opiera się na kilku pojęciach platformowych:
- Build statyczny - cała witryna jest renderowana do zwykłych plików HTML z wyprzedzeniem, podczas kroku buildu, zamiast na każde żądanie użytkownika.
- Prerender - generowanie pełnego drzewa DOM każdej podstrony do fizycznego pliku HTML. Witryna z sześcioma językami mnoży liczbę stron przez liczbę języków, więc build skaluje się proporcjonalnie do siatki treści.
- Cloudflare Pages - platforma hostingowa serwująca pliki z globalnego brzegu sieci CDN, obsługująca mechanizmy brzegowe przez Pages Functions.
- Wrangler - narzędzie wiersza poleceń Cloudflare, używane tutaj do bezpośredniego wdrażania lokalnie skompilowanego katalogu
dist/z pominięciem ograniczeń runnera zdalnego. - Hreflang - atrybuty w nagłówkach HTML informujące roboty indeksujące o lokalnych odpowiednikach danej strony w innych językach.
- Przekierowanie 301 - stałe przekierowanie HTTP przenoszące autorytet i historię zaindeksowanego adresu na nowy adres URL.
Tygodnie: przeniesienie, które wszyscy budżetują
Widoczna migracja to ta część, którą się szacuje, i szacunek bywa początkowo trafny. Treść wychodzi z bazy MySQL WordPressa w formie plików Markdown z nagłówkiem YAML frontmatter. Szablony PHP i motywy potomne są przepisywane na komponenty Astro z wykorzystaniem Tailwind CSS. Całość kompiluje się do katalogu wyjściowego i ląduje na serwerze brzegowym. Witryna treściowa średniej wielkości osiąga działający build statyczny w kilka tygodni. To faza, która doskonale prezentuje się na wewnętrznych prezentacjach i przekonuje zarząd, że projekt jest praktycznie ukończony.
W rzeczywistości projekt znajduje się dopiero na progu najtrudniejszego etapu. Działający build dowodzi wyłącznie tego, że komponenty Astro potrafią złożyć HTML bez błędów składniowych. Nie mówi nic o tym, czy tysiące historycznych adresów URL nadal poprawnie przekazują ruch, czy relacje hreflang nie uległy zerwaniu w wyszukiwarce Google oraz czy pamięć procesu Node.js udźwignie dalszy rozrost bazy artykułów.
Miesiące: warstwa przekierowań, której nikt nie zaplanował
Pierwszy kwartał po uruchomieniu buildu pochłonęła infrastruktura przekierowań. Każdy adres URL, jaki WordPress wygenerował od 2006 roku (w tym archiwa dat, kategorie, tagi, taksonomie wtyczek i wersje językowe), musiał otrzymać precyzyjną regułę 301 prowadzącą do nowego odpowiednika w Astro. Brak takiej mapy skutkował natychmiastowym wzrostem błędów 404 w Google Search Console i utratą wypracowanych pozycji w rankingu.
Na jednojęzycznym serwisie przygotowanie takiej listy jest zadaniem liniowym. Przy serwisie obsługującym sześć języków z lokalizowanymi slugami (np. polskie, niemieckie, norweskie czy hiszpańskie nazwy usług) liczba reguł przekroczyła 18 000 wpisów.
W tym momencie ujawniło się twarde ograniczenie platformy: plik _redirects w Cloudflare Pages posiada nieudokumentowany limit 100 KB. Powyżej tej wielkości parser platformy po cichu ignoruje nadmiarowe wiersze bez zgłaszania jakichkolwiek błędów podczas deployu. W rezultacie część reguł działała, a część zwracała błąd 404. Rozwiązaniem było przeniesienie logiki przekierowań do brzegowego handlera middleware napisanego w TypeScript (functions/redirect-map.ts), który dokonuje wyszukiwania w pamięci o złożoności O(1) na poziomie żądania brzegowego.
Miesiące: sześć języków, które muszą się zgadzać na zawsze
W tradycyjnym WordPressie wtyczki wielojęzyczne (takie jak WPML czy Polylang) maskują złożoność relacji między wersjami językowymi za bazą danych. W architekturze statycznej cała struktura staje się bezpośrednio widoczna w plikach i nagłówkach.
Sześć wersji językowych każdego artykułu musi pozostawać strukturalnie i semantycznie równoległych:
- Identyczny zestaw nagłówków merytorycznych H2 i H3 we właściwym porządku logicznym.
- Pełna siatka odnośników hreflang w sekcji head, wskazująca bezbłędnie na wszystkie 5 rodzeństw językowych.
- Kanoniczne adresy URL odzwierciedlające właściwą domenę i ścieżkę językową.
- Spójność taksonomii i powiązań między kategoriami w każdym języku.
Gdy jedna wersja językowa ulega zmianie (np. dodanie nowej sekcji lub zmiana sluga), brak automatyzacji prowadzi do natychmiastowego dryfu wersji. Wyszukiwarki napotykające asymetryczne tagi hreflang zaczynają ignorować sygnały regionalne, co prowadzi do kanibalizacji treści między rynkami. Z tego powodu wprowadziliśmy automatyczny skrypt walidacji relacji wielojęzycznych uruchamiany przed każdym commitem.
Architektura pamięci Node.js przy 14 000 prerenderowanych stron
Astro kompiluje strony statyczne w jednym procesie roboczym. Gdy serwis rozrósł się do ponad 14 000 podstron (łącząc bazę wiedzy, przewodniki techniczne, strony ofertowe i lokalne profile miast w 6 językach), silnik V8 w domyślnej konfiguracji wyczerpał limit 4 GB pamięci podręcznej.
Standardowy runner Cloudflare Pages dysponuje 8 GB pamięci RAM, co przy intensywnej kompilacji TypeScriptu, przetwarzaniu bloków MDX, parsowaniu drzew AST i optymalizacji stylów Tailwind CSS okazało się niewystarczające. Proces kompilacji kończył się błędem JavaScript heap out of memory.
Problem rozwiązano poprzez przebudowę całego przepływu wdrożeniowego:
- Lokalny profil budowania na procesorach Apple Silicon: Build wykonuje się lokalnie z jawną alokacją pamięci
NODE_OPTIONS='--max-old-space-size=12288'. Maszyna M-series kompiluje 14 477 stron HTML w czasie poniżej 3.5 minuty. - Czyszczenie rusztowania prerenderu: Astro podczas kompilacji tworzy katalog
dist/.prerenderz modułami serwerowymi. Pojedynczy plik potrafił osiągnąć 43.9 MiB, co blokowało deploy (Cloudflare Pages odrzuca pojedyncze pliki powyżej 25 MiB). Skrypt wdrożeniowy automatycznie usuwa zbędne artefakty przed wysyłką. - Bezpośredni upload przez Wrangler: Gotowy, zwalidowany katalog
dist/jest wgrywany bezpośrednio do sieci brzegowej, eliminując zawodny krok budowania w chmurze.
Złożoność generowania sitemap i grafu kanonicznego
Jednym z najtrudniejszych wyzwań okazało się generowanie map witryn (sitemap). Domyślna integracja @astrojs/sitemap przy 14 000 adresów generowała jeden wielki plik XML, który ignorował specyficzne reguły indeksowalności w poszczególnych kolekcjach.
Zbudowaliśmy własny, modułowy generator map witryn produkujący strukturę złożoną z 32 powiązanych plików:
- Główny plik
sitemap-index.xmlkierujący roboty do 6 indeksów regionalnych (sitemap-pl.xml,sitemap-en.xml, itd.). - Każdy indeks regionalny dzieli się na dedykowane pliki liści: artykuły blogowe, strony usług, studia przypadków i profile regionalne.
- Rygorystyczny filtr wykluczający adresy z metatagiem
noindex(skryptcheck:noindex-sitemapsgwarantuje, że do sitemapy nie trafia ani jeden adres nieindeksowalny).
Dzięki takiemu podziałowi roboty Google, Bing i crawlerów AI pobierają mapy partiami, bez przekraczania limitów czasowych i bez przeciążania bufora pamięci.
Narzędzia, które odbudowujesz, a które WordPress dawał za darmo
Najmniej docenianym kosztem odejścia od CMS-a jest konieczność napisania własnych narzędzi kontroli jakości, które WordPress i jego wtyczki realizowały niewidocznie w tle. WordPress automatycznie blokował tworzenie zduplikowanych slugów, dbał o spójność relacji w bazie MySQL i weryfikował linki podczas zapisu. W świecie statycznym każda pomyłka w pliku Markdown natychmiast trafia na produkcję, jeśli nie zostanie wykryta przez zautomatyzowane testy.
W ciągu dwunastu miesięcy stworzyliśmy bibliotekę 34 bramek kontrolnych (skrypt run-gates.mjs), uruchamianych przed każdym wdrożeniem:
- Weryfikacja linków wewnętrznych (
check:linksicheck:service-navigation-parity): Skanowanie całego kodu pod kątem martwych odnośników i zapewnienie, że każda usługa posiada przynajmniej dwa odnośniki przychodzące. - Kontrola spójności językowej i diakrytyków (
check:diacritic-wordlist): Wykrywanie błędów kodowania znaków narodowych w każdym z sześciu języków. - Strażnik wycieku cen (
check:no-own-prices): Automatyczny audyt sprawdzający, czy twarde kwoty nie pojawiają się w treściach poradnikowych poza zatwierdzonymi stronami cennika. - Walidacja bezpieczeństwa CSP (
check:csp-inline): Skanowanie wygenerowanych plików HTML pod kątem skrótów SHA-256 dla skryptów liniowych w celu utrzymania restrykcyjnej polityki Content Security Policy. - Audyt retoryki AI (
check:slop-rhetoric): Sprawdzanie tekstów pod kątem pustych zwrotów marketingowych i sztucznych konstrukcji językowych.
Ten zestaw testów daje pewność, jakiej żaden panel WordPressa nie jest w stanie zagwarantować, ale wymagał setek godzin pracy inżynieryjnej.
Potok optymalizacji mediów i lokalne ładowanie fontów
W dynamicznym WordPressie przetwarzanie mediów opiera się na bibliotece GD lub ImageMagick wywoływanej w momencie wysyłania pliku przez formularz mediów. W statycznym ekosystemie Astro optymalizacja obrazów staje się częścią potoku kompilacji.
W początkowej fazie projektu skierowanie setek wielomegabajtowych grafik rastrowych przez wbudowany komponent obrazów Astro doprowadziło do gwałtownego wydłużenia czasu buildu i wyczerpania pamięci przez silnik Sharp. Wdrożyliśmy rygorystyczny podział zasobów:
- Wstępna kompresja tła i elementów stałych: Grafiki dekoracyjne oraz statyczne tła są konwertowane do formatu AVIF i WebP przed umieszczeniem w katalogu
public/, co pozwala serwować je bezpośrednio z CDN bez obciążania pamięci kompilatora. - Dynamiczne warianty dla grafik artykułów: Obrazy w treści są procesowane z jawnym określeniem wymiarów
widthiheight, co całkowicie eliminuje przesunięcia układu (Cumulative Layout Shift, CLS = 0.00). - Prywatność i lokalne fonty: Całkowicie wyeliminowaliśmy zewnętrzne zapytania do Google Fonts. Rodziny krojów są osadzane lokalnie w formacie WOFF2 z podzbiorem znaków (subsetting) ograniczonym do łaciny i znaków diakrytycznych, co zapobiega blokowaniu renderowania tekstu i spełnia wymogi europejskiego RODO.
Wyspy Astro (Islands Architecture) dla elementów interaktywnych
Największą zaletą Astro jest domyślna wysyłka zerowej ilości JavaScriptu do przeglądarki użytkownika (Zero-JS by default). Dla serwisu treściowego oznacza to, że czytelnik przeglądający artykuły techniczne nie pobiera żadnych frameworków frontendowych.
W miejscach, gdzie interaktywność jest niezbędna (np. formularze kontaktowe, selektory filtrów, kalkulatory czy dynamiczne widżety), zastosowaliśmy architekturę wysp (Astro Islands):
- Formularz kontaktowy ładuje się jako lekki komponent z dyrektywą
client:visible, co oznacza, że kod JavaScript jest pobierany dopiero w momencie przewinięcia widoku do formularza. - Zabezpieczenie antyspamowe realizowane jest przez niewidoczną integrację Cloudflare Turnstile bez uciążliwych łamigłówek graficznych CAPTCHA.
- Wysyłka danych odbywa się bezstanowo przez brzegowy endpoint API komunikujący się z usługą transakcyjną Resend, eliminując konieczność utrzymywania backendu serwerowego.
Techniczna checklista migracyjna: 10 kroków przed wyłączeniem WordPressa
Na podstawie dwunastu miesięcy doświadczeń zebraliśmy sprawdzoną checklistę inżynieryjną, którą należy zrealizować przed ostateczną zmianą rekordów DNS:
- Inwentaryzacja wszystkich tras: Wygenerowanie pełnego zrzutu adresów URL z bazy WordPressa (w tym stron, wpisów, archiwów, autorów, załączników i kanałów RSS).
- Projekt mapy przekierowań 301: Przygotowanie tabeli mapowania starych adresów na nowe slugi z uwzględnieniem struktury wielojęzycznej.
- Wdrożenie przekierowań brzegowych: Umieszczenie mapy przekierowań w kodzie funkcji brzegowej (Cloudflare Functions / Worker), omijając limity wielkości plików statycznych.
- Walidacja siatki hreflang: Sprawdzenie, czy każdy artykuł we wszystkich wersjach językowych posiada kompletne i wzajemne odnośniki alternatywne.
- Audyt danych strukturalnych JSON-LD: Zweryfikowanie schematów
Article,FAQPage,HowToiOrganizationpod kątem zgodności ze standardem schema.org. - Podział map witryn na moduły: Utworzenie indeksu sitemap oraz dedykowanych podplików dla poszczególnych typów treści.
- Test wydajnościowy kompilacji: Przetestowanie buildu w środowisku z ograniczoną pamięcią i zoptymalizowanie alokacji sterty V8.
- Wdrożenie nagłówków bezpieczeństwa: Skonfigurowanie Content Security Policy (CSP), HSTS, X-Frame-Options i Permissions-Policy na poziomie brzegu sieci.
- Sprawdzenie spójności metatagów noindex: Upewnienie się, że strony techniczne, szablony i duplikaty są poprawnie wykluczone z indeksowania.
- Plan monitoringu post-migracyjnego: Skonfigurowanie automatycznych testów dymnych (Smoke Tests) weryfikujących kody odpowiedzi HTTP oraz stan indeksu w Search Console po przełączeniu DNS.
Wyszukiwanie statyczne i indeksacja treści bez bazy danych
Jednym z kluczowych argumentów przemawiających dawniej za WordPressem była wbudowana funkcja wyszukiwania oparta na zapytaniach SQL do bazy wp_posts. W architekturze statycznej brak serwera bazodanowego wymusił zmianę podejścia do przeszukiwania zasobów.
Zamiast dynamicznych zapytań serwerowych wdrożyliśmy statyczny silnik indeksowania oparty na WebAssembly (WASM):
- Podczas kompilacji wyjścia HTML generowany jest skompresowany, pofragmentowany indeks leksykalny zawierający wyłącznie treść merytoryczną artykułów (z pominięciem nagłówków nawigacyjnych, stopki i kodu skryptów).
- Wyszukiwarka po stronie klienta pobiera wyłącznie małe fragmenty indeksu odpowiadające wpisanym literom zapytania (chunka rzędu 15-30 KB), co pozwala na natychmiastowe wyświetlanie wyników w czasie poniżej 15 ms.
- Zapewnia to zerowe obciążenie infrastruktury serwerowej niezależnie od liczby jednoczesnych użytkowników przeszukujących bazę wiedzy.
Ciągłe testy wydajności Core Web Vitals w potoku CI/CD
W tradycyjnym środowisku WordPress wskaźniki Core Web Vitals często ulegały degradacji po instalacji kolejnej wtyczki marketingowej lub zmianie motywu. W ekosystemie Astro wprowadziliśmy automatyczny nadzór wydajnościowy w procesie ciągłej integracji:
-
Lighthouse CI: Każda zmiana kodu przechodzi automatyczny audyt badający wskaźniki Largest Contentful Paint (LCP < 1.0s), Interaction to Next Paint (INP < 50ms) oraz Cumulative Layout Shift (CLS = 0.00).
-
Testy regresji wizualnej: Zautomatyzowane scenariusze Playwright weryfikują renderowanie krytycznych widoków na urządzeniach mobilnych i desktopowych, gwarantując brak przesunięć elementów podczas ładowania zasobów.
-
Blokada kompilacji przy naruszeniu limitów: Jeśli nowy komponent wprowadza nadmiarowy kod JavaScript lub blokuje główny wątek przeglądarki, bramka jakościowa natychmiast przerywa wdrożenie z jawnym komunikatem błędu.
-
Natychmiastowe czyszczenie cache brzegowego (Edge Cache Purging): Po wdrożeniu nowego artefaktu skrypt deployu automatycznie wywołuje API strefy Cloudflare (
purge_cache: {"purge_everything": true}), co gwarantuje, że użytkownicy na całym świecie natychmiast widzą zaktualizowane treści bez oczekiwania na wygaśnięcie nagłówkóws-maxage.
Co migracja faktycznie kupiła: bilans po 12 miesiącach
Po dwunastu miesiącach zbierania twardych danych telemetrycznych bilans migracji z WordPressa do Astro jest jednoznacznie pozytywny, pod warunkiem że serwis ma charakter treściowo-usługowy:
- Czas odpowiedzi serwera (TTFB): Spadek ze średnio 650-1200 ms (w zależności od obciążenia bazy danych MySQL i warstw cache) do stabilnych 25-45 ms z dowolnego węzła Cloudflare Edge na świecie.
- Redukcja powierzchni ataku: Usunięcie interpretera PHP, bazy danych SQL i katalogu wp-admin wyeliminowało 100% typowych wektorów ataków (SQL Injection, Remote Code Execution w pluginach, ataki brute force na logowanie).
- Efektywność dla crawlerów wyszukiwarek i AI: Czysty, semantyczny kod HTML bez zbędnego narzutu JavaScriptu pozwala robotom indeksującym (w tym crawlerom OpenAI, Anthropic i Perplexity) na natychmiastowe parsowanie treści, co bezpośrednio wspiera widoczność w wyszukiwarkach generatywnych (GEO/AEO).
- Przewidywalność kosztów: Serwowanie statycznych plików z brzegu sieci generuje znikome koszty infrastruktury w porównaniu z utrzymaniem dedykowanych klastrów bazodanowych pod dużym ruchem.
Uczciwy wniosek dla zespołów technicznych rozważających podobny krok: migracja do Astro nie jest prostym zabiegiem frontendowym. To kompleksowy projekt inżynieryjny, w którym samo kodowanie szablonów stanowi ułamek pracy, a główny wysiłek koncentruje się na architekturze routingu, automatyzacji testów jakościowych i dyscyplinie wielojęzycznej. Dla serwisów nastawionych na bezkompromisową szybkość i globalny zasięg jest to jednak inwestycja, która zwraca się w każdym aspekcie technicznym. Jeśli chcesz wdrożyć taką architekturę w swojej firmie, sprawdź jak pracuje nasz programista Astro, a w przypadku istniejących serwisów zapoznaj się z ofertą migracji z WordPressa do Astro. Dalsze szczegółowe studia przypadków publikujemy na blogu technologicznym WPPoland.






