PRZYKŁAD dane ilustracyjne · format jak w realnym audycie · do wglądu na stronie, bez pobierania ← Wszystkie raporty
Szybkość

Analiza szybkości i Core Web Vitals - raport przykładowy

Przykładowy, anonimizowany audyt z danymi ilustracyjnymi. To pełny dokument w formacie i objętości, w jakiej dostajesz realny, ręczny audyt - liczby i nazwy są reprezentatywne (przykładowe), a struktura i poziom szczegółowości jak w prawdziwym raporcie.


Metryka raportu i punkt wyjścia

Zakres audytu i metodologia badania

Audyt szybkości i Core Web Vitals zrobiłem 22 czerwca 2026 roku na portalu e-commerce. Przejrzałem 265 stron - główne kategorie produktów, strony informacyjne i całą ścieżkę zakupową. Sprawdzam dwie rzeczy naraz: dane od prawdziwych użytkowników z Google Chrome User Experience Report (CrUX) oraz pomiary laboratoryjne (Lab) z Google Lighthouse i WebPageTest. Jedno bez drugiego daje fałszywy obraz.

Testy laboratoryjne odpalałem na urządzeniach, które symulują realne warunki. Mobile: sieć 4G (pobór 1.6 Mb/s, wysyłanie 750 kb/s, opóźnienie 50ms) i czterokrotne spowolnienie procesora (CPU 4x throttle) - tak działa typowy smartfon ze średniej półki. Desktop testowałem na nieobciążonym procesorze, ale tej samej sieci 4G, żeby wyniki dało się porównać. Dane CrUX obejmują 28 dni przed audytem, zebrane od 2 847 realnych użytkowników, przy aktywnym mobile-first indexingu Google.

Baseline metryki - obecny stan techniczny

Stan wyjściowy pokazuje dużą przepaść między mobile a desktopem i sporo problemów strukturalnych, które ciągną Core Web Vitals w dół:

MetrykaWartość Lab (Mobile)Wartość CrUX (Mobile)DesktopCelStatus
LCP (s)3,84,22,1<2,5🔴 Krytyczne
INP (ms)248-45<100🔴 Krytyczne
CLS0,180,220,08<0,1🟡 Słabe
TTFB (ms)520-380<600🟡 Słabe
FCP (s)1,5-0,9<1,8✅ Akceptowalne

Largest Contentful Paint (LCP) to 4,2 sekundy na mobile w danych polowych - 68% ponad cel 2,5 sekundy. Desktop robi 2,1 sekundy, czyli dwa razy lepiej. W teście Lab głównym elementem LCP jest grafika hero na stronie głównej (2,1 MB w JPG), serwowana bez optymalizacji. Waterfall pokazuje LCP dopiero o 3,8 sekundy, a DOMContentLoaded o 2,1 sekundy - to znaczy, że rendering blokuje JavaScript (5 plików blokujących, opóźnienie 1,2 sekundy).

Interaction to Next Paint (INP) wynosi 248 milisekund na mobile, 2,5x powyżej celu <100ms. Interfejs reaguje z opóźnieniem. 60% audytowanych stron przekracza próg „Poor” dla INP. Winny jest JavaScript: 840 milisekund parse/compile time, z czego 520 ms idzie na samo wykonanie kodu. Profiling w DevTools pokazał, że main thread jest zajęty 85% czasu, zwłaszcza podczas interakcji. Skrypty third-party (8 plików) odpowiadają za sporą część tego: Google Analytics (+240ms), Facebook Pixel (+180ms) i reCAPTCHA (+320ms). Do tego architektura SPA na Reakcie daje hydration time 1,8 sekundy z Main Thread Blocking 156 milisekund - kolejny czynnik psujący responsywność.

Cumulative Layout Shift (CLS) na poziomie 0,22 w CrUX (ponad dwa razy powyżej celu 0,1) oznacza skaczący layout. Powody: brak preloadu webfontów (4 rodziny czcionek, FOUT 200-400ms), brak ustalonej wysokości dla elementów dynamicznych i renderowanie treści poniżej folda bez rezerwacji miejsca (szczególnie produkty doładowywane asynchronicznie).

Time To First Byte (TTFB) to 520 milisekund: server rendering 180ms (w porządku), network latency 340ms (sieć mobilna) i cache miss Redis 42%. Tylko 22% stron ma poprawny cache-control; 78% ma headery byle jakie. Cache hit na CDN to 38% - strategia cachowania nie działa.

Zużycie zasobów i optymalizacyjne potencjały

Po zasobach widać, gdzie leżą największe rezerwy:

JavaScript: Bundle łączny 862 KB, w 23 osobnych plikach (powinno być 3-5). Pięć plików jest render-blocking (1,2 sekundy opóźnienia LCP). CSS-in-JS dokłada 140ms do FCP i 180ms do LCP. Osiem skryptów third-party generuje 740ms łącznego opóźnienia (Google Analytics 240ms, Facebook Pixel 180ms, reCAPTCHA 320ms). Code-splitting może oszczędzić 240 KB.

CSS: Rozmiar 284 KB, z czego 98 KB to render-blocking CSS. Średnia głębokość specificity to 6 poziomów, 23 reguły powodują slow-down. Optymalizacja CSS-in-JS i usunięcie martwego CSS zwolni 65 KB.

HTML: Średni rozmiar 285 KB - mocno powyżej benchmarku branżowego (80-120 KB). Bez minifikacji oszczędność: 85 KB.

Obrazy: Średnio 3,2 MB na stronę, z czego 68% (2,1 MB) to nieoptymalizowany plik hero (JPG 2,1 MB). WebP obsługiwany na 12% stron (de facto brak wdrożenia), AVIF: 0%. Konwersja hero do AVIF zmniejszy go o 68%. Łączna oszczędność: 1,9 MB na stronę, czyli średnio 420 KB zamiast obecnego (optymalizacja 68%).

Kompresja: Gzip na 78% stron (średnia oszczędność 35%). Brotli: ledwie 2% stron (potencjał +220 KB średnio). Po wdrożeniu Brotli łączna redukcja: 185 KB.

Liczba HTTP requestów: 87 żądań (64 dla assetów statycznych), cel: 52 (redukcja o 40%). Zmniejszenie bundli zejdzie do 58 requestów w ciągu 30 dni.

Wpływ na biznes i korzyści konwersji

Korelacja między prędkością ładowania a konwersją wynosi 0.74 - związek jest silny. Obecny conversion rate na mobile (1,8%) jest o 58% niższy niż na desktopie (4,2%). Bounce rate na mobile przy LCP >3s to 62%, a przy LCP <2s spada do 28% - różnica 34 punktów procentowych. Dla strony z 2,4 milionami wizyt miesięcznie ta przepaść kosztuje szacunkowo 42,000 PLN miesięcznie.

Z badań branżowych i wewnętrznej analityki widać, co dają poszczególne ruchy: zmniejszenie LCP o 1 sekundę to lift konwersji +2,8%, czyli dla bieżącego ruchu mobile (1,2M wiz/mies.) = 20,160 dodatkowych konwersji rocznie. Zejście INP poniżej 100ms podnosi sessyjność o +1,4%, stabilizuje engagement i o 12% zmniejsza powroty do strony głównej.

Cel biznesowy to wzrost konwersji +18% i redukcja bounce rate -28% w 90 dni. Da się go osiągnąć przez:

  • Osiągnięcie LCP <2,5s = +8% konwersji mobilnej
  • Redukcja INP <100ms = +4% sessyjności
  • Stabilizacja CLS <0,1 = +2% retention
  • Razem target +14% (rozpiętość 90 dni pozwala na dodatkowe +4% z organic reach improvement)

Prioryty optymalizacyjne i terminy realizacji

P1 (Krytyczne, wpływ 30 dni):

  1. Optymalizacja hero image: konwersja do AVIF, lazy-loading niewidocznych grafik (-1.2s LCP) - Efekt: LCP 3.8s → 2.6s
  2. Usunięcie render-blocking JavaScript (5 plików) - Efekt: +0.8s FCP, +1.2s LCP
  3. Redukcja third-party skryptów: asynchronizacja Google Analytics i Facebook Pixel - Efekt: -320ms LCP
  4. Implementacja Service Workera dla static assets - Efekt: +62% cache hit, -340ms TTFB

P2 (Wysokie, wpływ 60 dni):

  1. Code-splitting React bundle: zmniejszenie initial JS z 862KB do 520KB - Efekt: -140ms FCP, -180ms parse time
  2. Preload webfontów, wdrożenie font-display: swap - Efekt: -200ms FOUT, +0.05 CLS improvement
  3. Optymalizacja CSS (usunięcie dead code, redukcja specificity) - Efekt: -98ms rendering

P3 (Średnie, wpływ 90 dni):

  1. Migracja na Brotli compression - Efekt: -185KB TTFB
  2. Database query optimization: cache Redis miss -42% → 15% - Efekt: -300ms server response

Szacunkowe efekty końcowe: LCP 3.8s → 2.0s (zmniejszenie 47%), INP 248ms → 85ms (zmniejszenie 66%), CLS 0.22 → 0.09 (zmniejszenie 59%), konwersja mobilna +18% = dodatkowo 216,000 PLN przychodów rocznych.


Streszczenie zarządcze

Core Web Vitals - dane terenowe (mobile)
Poglądowy panel PageSpeed Insights z wynikiem ogólnym i trzema metrykami Core Web Vitals na mobile - wszystkie liczby zaczerpnięte z raportu, ilustracyjne.

Audyt pokazał krytyczne problemy z Core Web Vitals na mobile, które bezpośrednio biją w wyniki biznesowe. Sprawdzałem dane realnych użytkowników (CrUX) z 2 847 urządzeń mobilnych, pomiary laboratoryjne w Chrome DevTools oraz diagnostykę zasobów serwera. Najgorsze odkrycie: 48% stron dostaje ocenę „Poor” w Core Web Vitals na mobile, podczas gdy na desktopie problem dotyczy tylko 12% witryn. Ta różnica nie jest przypadkowa - pokrywa się z przepaścią w dochodach: konwersje mobilne to 1,8% wobec 4,2% na desktopie, a korelacja prędkości z konwersją wynosi 0,74.

Strata przychodu z obecnych problemów to około 42 000 PLN miesięcznie przy 2,4 milionach wizyt. Główny hamulec to Largest Contentful Paint (LCP), który na mobile wynosi 4,2 sekundy - daleko powyżej celu <2,5 sekundy. Ten parametr odpowiada za 62% bounce rate, gdy strona ładuje się dłużej niż 3 sekundy, wobec 28% przy czasach poniżej 2 sekund. Drugi krytyczny problem to Interaction to Next Paint (INP) na poziomie 248 milisekund - 60% stron przekracza próg akceptowalności (<100ms), więc interfejs sprawia wrażenie zamulonego. Trzeci czynnik to Cumulative Layout Shift (0,22 w danych rzeczywistych) - treść skacze i utrudnia konwersję.

MetrikaBieżący stanCelLukaMobilność vs Desktop
LCP (Lab)3,8s2,5s-1,3s (-35%)4,2s vs 2,1s (2x)
INP248ms<100ms-148ms (-60%)312ms vs 45ms
CLS0,22 (Field)0,1-0,12 (-120%)0,18 (Lab) vs 0,22 (Field)
TTFB520ms<300ms-220msBackend 180ms, Network 340ms
Bounce rate (LCP >3s)62%28%-34ppPrzy <2s

Źródła problemu: Diagnostyka wskazuje trzy główne przyczyny. Obrazy nie są zoptymalizowane - średnio 3,2 MB na stronę, z czego 68% da się zmniejszyć, a WebP jest na zaledwie 12% przypadków. Największy element strony (hero image) to 2,1 MB w JPG, choć konwersja do AVIF ścięłaby go o 68%. Druga sprawa to JavaScript: 862 KB nieskompresowanego kodu w 23 plikach, z czego 5 blokuje renderowanie (opóźnienie 1,2s), a 8 skryptów third-party (Google Analytics +240ms, Facebook Pixel +180ms, reCAPTCHA +320ms) zjada 740 milisekund. Sam parse i execute tego kodu trwa 840 milisekund. Trzecia rzecz to Time To First Byte 520 milisekund - backend odpowiada w 180 ms, a 340 ms pochłania sieć. Cache hit CDN to zaledwie 38%, tylko 22% stron ma poprawne nagłówki cache-control. Do tego baza odpowiada średnio w 145 ms, ale najwolniejszy endpoint (/api/products) potrzebuje 480 ms, a Redis ma miss rate 42%.

Plan naprawy - trzy fazy: Działania podzieliłem na trzy fazy z priorytetami. P1 - optymalizacja obrazów (30 dni, szacunkowy zysk: -1,2s LCP): konwersja hero image i reszty assetów do AVIF z fallbackami WebP, lazy loading, zejście ze średniego rozmiaru z 420 KB do 140 KB na obraz. Oszczędność: 1,9 MB na stronę. P2 - refaktor JavaScript (45 dni, szacunkowy zysk: -0,8s TTFB + FCP): code splitting React bundla z 862 KB na moduły ~300 KB, async/defer dla 5 render-blocking skryptów, preload krytycznych zasobów, sandbox dla third-party, Brotli na 98% stron (zamiast 2%). Oszczędność: ~220 KB na stronie. P3 - poprawa TTFB i cachingu (60 dni, szacunkowy zysk: -0,3s TTFB): optymalizacja zapytań do bazy, zmiana Redis cache strategy z 42% miss na <15%, edge caching CDN z 38% na >75% hit rate, Service Worker dla statycznych zasobów, preload czcionek.

Czego się spodziewam po 90 dniach: LCP spadnie do 2,1s (mobile), INP do 120ms (to wciąż wymaga dalszej pracy), CLS ustabilizuje się na <0,1, bounce rate spadnie z 62% do ~32%, a mobile conversion rate wzrośnie z 1,8% do 2,7% (+2.8% lift). Przy 2,4 milionach wizyt miesięcznie i średnim AOV przychód wzrośnie o ~50 000 PLN. Koszt wdrożenia to 35-50 tys. PLN (zespół wewnętrzny 3-4 tygodnie + ewentualnie audyt Senior Frontend), co daje ROI ~8-12 w ciągu 6 miesięcy. Bez działań problem będzie się pogłębiać - konkurencja przyspiesza, a algorytm Google już premiuje Core Web Vitals.


Top problemy z priorytetami

Audyt objął 265 stron sklepu - Chrome DevTools, Google PageSpeed Insights, Google Lighthouse Lab oraz dane CrUX z ostatnich 28 dni (2,847 realnych użytkowników mobilnych i desktopowych). Problemy krytyczne wybierałem po korelacji metryk wydajności z biznesem: bounce rate (+34pp różnicy między LCP >3s a <2s), konwersja (mobile 1.8% vs desktop 4.2%, korelacja 0.74) i strata przychodu rzędu 42,000 PLN/miesiąc.

PriorytetProblemWpływ technicznyKoszt biznesowyRozwiązanieEfekt oczekiwany
P0Obrazy nieoptymalizowane1.9MB potencjału zmniejszenia/stronę; 68% bez kompresji; 0% w formacie WebP/AVIF-2.8% konwersji na mobile; utrata ~29,600 PLN/miesiąc1) Konwersja hero image (2.1MB JPG) do AVIF (-68%); 2) Implementacja adaptatywnych srcset; 3) Lazy-loading dla below-fold; 4) Mozjpeg/oxipng na CI/CDLCP -1.2s; obrazy 340KB zamiast 2.1MB; +2.8% konwersji
P0TTFB za wysoki520ms całkowity; serwer 180ms (akceptowalny); sieć 340ms (CDN issue); cache hit rate 38% zamiast >80%Bounce rate +18pp; przesunięcie pozostałych metryk (LCP 3.8s zamiast <2.5s)1) Audyt cache-control headers (78% stron sub-optimalnie); 2) Implementacja Brotli (obecnie 2%; potencjał -220KB); 3) Redis cache dla endpoint’ów API (np. /products: 480ms → <150ms); 4) CDN cache rules hardeningTTFB ≤300ms; cache hit >80%; LCP -0.8s dzięki szybszemu TTFB
P1Render-blocking CSS98KB CSS blokuje FCP; +180ms do LCPDelayed First Paint → brak wizualnej odpowiedzi przez 1.5s+1) Critical CSS inlining (<14KB above fold); 2) Defer non-critical CSS via media queries; 3) Inline SVG sprite zamiast CSS background; 4) PurgeCSS: 284KB CSS → ~120KB (usunięcie unused rules)FCP 1.0s zamiast 1.5s; LCP 2.8s zamiast 3.8s
P1Third-party JavaScript (8 plików)740ms sumarnie; Google Analytics +240ms, Facebook Pixel +180ms, reCAPTCHA +320ms; parse/compile 840msINP degradacja do 248ms (target <100ms); 60% stron powyżej progu; bounce rate +12pp1) Web Worker dla Google Analytics; 2) Defer reCAPTCHA do POSTu formularza (lazy-trigger); 3) Facebook Pixel wrap w setTimeout(…, 5000); 4) Monitorowanie Core Web Vitals Trade Samą alternatywą: Plausible Analytics (~40ms zamiast 240ms)INP <150ms; eliminacja 520ms junk; +1.4% sessions/user
P1JavaScript main thread blokada312ms INP na mobile; 85% czasu main thread zajęty parsing/execution; hydration 1.8s (React SPA bez SSR)Brak interaktywności; delay do-click-to-response; 62% bounce rate przy LCP >3s1) Implementacja SSR (Next.js w trybie server rendering); 2) Hydration 1.8s → <400ms przez komponentyzację; 3) Code splitting: 862KB JS bundla → chunks po 120-180KB; 4) Web Worker dla computations (sortowanie, filtrowanie w Product Listing)INP 45-80ms (desktop-like), głównie input delay <50ms
P2Font loading FOUT4 webfonty bez preload; FOUT delay 200-400ms; FCP degradacja o brak optymalizacjiInvisible text flash; CLS +0.08 (total 0.18); UX degradacja1) font-display: swap (zamiast block); 2) <link rel="preload"> dla 2 krytycznych fontów; 3) Local font fallback (system stack); 4) Wczytanie w preconnect do Google FontsFOUT <100ms; CLS -0.08; spostrzegana FCP szybsza
P2Backend API opóźnieniaAvg 145ms, ale /products: 480ms, P95: 620ms; Redis miss 42%; 8% requests trafia limit rateDelayed page hydration; timeout dla slow users; load spike powoduje failure1) Redis cache warmer dla /api/products query (cacheability: 60min); 2) Database index audit (N+1 query pattern likely); 3) Connection pooling (PostgreSQL max_connections); 4) GraphQL batching zamiast REST REST/products <150ms; Redis hit >90%; P95 <250ms
P2CLS (Cumulative Layout Shift)Lab: 0.18; Field: 0.22 (>0.1 poor); głównie hero image lazy-load trigger i image dimensions missingBounce rate +8pp (UX jarring); mobile users abandon; paid search Quality Score penalty1) Explicit width i height na <img> tags; 2) aspect-ratio CSS property dla containers; 3) loading="lazy" z margin-bottom placeholder; 4) Intersection Observer dla soft-load (preload 2s przed scroll)CLS <0.1; field-stable; +3% mobile engagement
P3Liczba HTTP requests (87 total)68 requests to assets; target redukcja do 52; waterfal serializacja koszt ~300msRedundant round-trips; slow networks (3G) cierpi; resource contention1) HTTP/2 push dla critical assets (CSS, main.js); 2) SVG sprite sheet zamiast 12 PNGs; 3) Font subsetting (wciąż 4 webfonty → 2); 4) CSS/JS concatenation (nie bundle, ale przynajmniej group)52 requests; waterfall -250ms; LCP -0.4s
P3React hydration brak SSR1.8s hydration; 156ms TBT (Total Blocking Time); zero pre-rendered HTMLBlank page do 1.5s; interaktywność delayed; mobile users experience “frozen” app1) Next.js App Router (SSR by default); 2) Selective hydration (interactive-only); 3) getStaticProps dla catalog stron; 4) Streaming SSR (React 18 Suspense)Hydration <400ms; FCP 0.8s; interactive <1.2s
P3CSS specificity i unused rulesAvg 6 levels specificity; 23 high-cost rules; 284KB CSS z 45% wasteSelector re-evaluation overhead; +140ms FCP via CSS-in-JS; bundle bloat1) PurgeCSS/Tailwind CSS (atomic, ~40KB); 2) CSS refactor: max 3 levels specificity; 3) Remove unused @media + legacy class rules; 4) Critical CSS tooling (inline <14KB)CSS 120KB zamiast 284KB; FCP +0.3s
P3Brotli nie wdrożonyAktualnie 2% stron; tylko gzip (78%); Brotli savings ~220KB average220KB/stronę = 6% dodatkowego zmniejszenia vs gzip; 42M+ monthly visitors = 9.2TB wasted bandwidth/miesiąc1) Enable Brotli w nginx/Apache (poziom 11); 2) Accept-Encoding: br w CDN config; 3) Verify client browser support (>95% coverage); 4) Monitor via Accept-Encoding header logsPayload -220KB/stronę; transfer time -18%; +0.2s LCP relief

Podsumowanie wpływu: P0 (obrazy + TTFB + CSS) ścina LCP z 3.8s do ~2.2s w 30 dni (quick-win 1.2s). P1 (third-party JS + hydration) obniża INP z 248ms do <100ms, co podbija mobile conversion rate o 2.8% (szacunkowo +117,600 PLN/miesiąc). Core Web Vitals “Poor” spadają z 48% (mobile) do <15% w 90 dni, „Good” na 85%+ stron - to plasuje sklep w top 15% konkurencji na mobile i poprawia ranking organiczny w Search Console średnio o +12 pozycji dla słów średnio-konkurencyjnych.


Metodyka badania

Audyt prowadziłem warstwowo: testy laboratoryjne, dane realnych użytkowników i pomiary funkcjonalne. Dopiero razem dają pełny obraz Core Web Vitals i tego, co psuje doświadczenie.

Testy laboratoryjne - Lighthouse 12

Podstawą były kontrolowane testy w Google Lighthouse 12 w dwóch konfiguracjach sprzętowych. Na mobile symulowałem Pixel 6 w pięciu powtórzeniach z throttlingiem sieci (profil 4G, opóźnienia 150ms, zmienna prędkość pobierania) i CPU x4 slowdown - tak wygląda praca na średnich i słabszych procesorach. Dla desktopu zrobiłem 5 testów w Chrome ze standardowymi profilami sieci (Fast 3G equiv.) i limitem CPU x2. Testy odpalałem dwukrotnie - zaraz po wyczyszczeniu cache’a i po 3 dniach pracy - żeby pokazać, jak cache wpływa na TTFB i FCP.

Wyniki laboratoryjne: średnia LCP (Largest Contentful Paint) 3,8 sekundy, czyli mocno powyżej celu (<2,5s), do ścięcia o 35% w 90 dni. Desktop wypadł znacznie lepiej - LCP 2,1s - więc mobile jest 2x wolniejszy. INP (Interaction to Next Paint) osiągnął 248 ms przy docelowych <100ms; 60% stron przekroczyło próg. CLS (Cumulative Layout Shift) w labie wyniósł 0,18, ale u realnych użytkowników rośnie do 0,22 - to efekt asynchronicznego doładowywania komponentów.

Dane rzeczywistych użytkowników - Chrome UX Report

Dane CrUX z 28-dniowego okna i 2 847 realnych użytkowników zweryfikowały wyniki labowe w naturze. LCP na mobile siada na 4,2 sekundy (górny kwartyl), na desktopie trzyma 2,1s - różnica 2x potwierdza, że problem leży po stronie mobile. INP w terenie to średnio 312ms na mobile wobec 45ms na desktopie, co wskazuje na blokowanie głównego wątku JavaScript. Górny percentyl (P95) CrUX pokazuje LCP do 6,8s dla 5% sesji mobilnych - stąd te skrajne przypadki frustracji.

Analiza zasobów i ścieżki krytycznej

Waterfall z WebPageTest pokazał kolejność ładowania: DOMContentLoaded w 2,1s (głównie czekanie na JavaScript), pełny Load Event w 4,8s. First Contentful Paint (FCP) wypada w 1,5s, ale Largest Contentful Paint (LCP) to 3,8s - różnica 2,3 sekundy to opóźnione ładowanie zdjęcia głównego (2,1MB w JPG). Analiza Resource Timing pokazała:

  • TTFB (Time To First Byte): 520ms - rozkład: serwer 180ms (backend processing) + sieć 340ms (latencja CDN/routing). Cache hit na CDN osiąga jedynie 38%, pozostałe requesty trafiają do origin.
  • HTML: 285KB średnio na stronę (unminified, z wbudowanym stylem)
  • JavaScript bundel: 862KB (głównie React + vendor dependencies); 5 plików blokujących render przesyłane synchronicznie powoduje dodatkowe opóźnienie 1,2s
  • CSS: 284KB łącznie; 98KB render-blocking CSS zanim będzie możliwy FCP
  • Obrazy bez optymalizacji: średnio 3,2MB na stronę; WebP dostępny dla 12% zasobów, AVIF w ogóle nie wdrożony
  • 23 pliki JavaScript, z czego 8 to third-party (Google Analytics +240ms, Facebook Pixel +180ms, reCAPTCHA +320ms razem +740ms overhead)
MetrykaWartośćBenchmark (P75)Status
LCP (Lab)3,8s<2,5s🔴 POOR
LCP (Field) mobile4,2s<2,5s🔴 POOR
LCP (Field) desktop2,1s<2,5s🟢 GOOD
INP248ms<100ms🔴 POOR
CLS0,18 (lab) / 0,22 (field)<0,1🟡 NEEDS WORK
TTFB520ms<600ms🟡 BORDERLINE
FCP1,5s<1,8s🟢 GOOD

Profiling JavaScript i main thread

Chrome DevTools Performance tab (10 sesji rejestracji) pokazał, że parse, compile i execution JavaScriptu zajmują łącznie 840ms - z czego 520ms to czysty execution, reszta to bottleneck kompilacji V8. Main thread jest zablokowany JavaScriptem przez 85% czasu ładowania, więc przeglądarka nie nadąża reagować na interakcje. Framework CSS-in-JS (Emotion) dokłada +140ms do FCP i +180ms do LCP względem zwykłego inline/optimized CSS. Hydration (SSR: 0%, czysty React SPA) trwa 1,8s, a TBT (Total Blocking Time) podczas hydracji sięga 156ms.

Coverage tab pokazał, że 62% kodu CSS nie jest używane na pierwszej stronie, a 44% JavaScriptu ładuje się dla stron, których kod nie jest potrzebny (kod źle podzielony). Font loading ściąga 4 webfonty bez preload (0% wdrożenia), co daje FOUT (Flash of Unstyled Text) 200-400ms opóźnienia.

Monitoring ciągły - SpeedCurve i RUM

SpeedCurve continuous monitoring (codzienna aktualizacja przez 30 dni) pokazał destabilizację: LCP wahał się w przedziale 3,4-4,1s z lekkim trendem wzrostowym. Real User Monitoring z BoomerangJS potwierdził korelację 0,91 między deploymentami a pogorszeniem LCP. RUM wyłapał też, że na tej samej sieci (4G throttling) wariancja wynosi ±0,8s zależnie od pory dnia - w godzinach szczytu (19:00-21:00) wartości rosną nawet do 5,2s przez obciążenie CDN.

Badanie percepcji użytkownika

Ankieta na 304 użytkownikach (próba reprezentatywna dla 2,4M monthly visits) pokazała wyraźną zależność prędkości i zadowolenia. 62% użytkowników mobilnych zgłaszało “slow experience” przy LCP > 3s; tylko 28% deklarowało komfort przy LCP < 2s. Bounce rate na mobile przy LCP > 3s sięga 62%, a przy LCP < 1,5s spada do 28% - różnica +34 pp. Korelacja LCP z mobile conversion rate to 0,74 (mocna zależność): przy obecnych 1,8% na mobile vs 4,2% na desktopie przyspieszenie o 1s mogłoby teoretycznie podnieść mobilną konwersję o +2,8pp (rozkład 28,6% do 31,4%).

Analiza strony serwerowej i cache’owania

Database query avg to 145ms, najwolniejszy endpoint /api/products zwraca w 480ms (P95). Redis ma miss rate 42% - czyli prawie połowa żądań idzie do bazy zamiast do cache’a. SSR: 0% (czysty SPA React), hydration time 1,8s. TTFB rozkłada się na 180ms backend + 340ms sieć. CDN cache headers są poprawne tylko na 22% stron; 78% ma sub-optimal cache-control (brak max-age albo private zamiast public), stąd niski 38% cache hit.

Zakres wyłączeń i margines błędu

Audyt nie obejmował testów ad network’ów (Google Ads, DoubleClick) ani wariantów AMP (nie są używane na stronie). Margin błędu dla lab testing to ±2%, dla danych polowych (CrUX) ±5% przy sample size 2847. Margin błędu dla szacunku przychodu (~42,000 PLN/miesiąc straty przy 2,4M monthly visits) to ±8% przez sezonowe wahania konwersji.

Performance budget i optymalizacyjne quick-wins

Performance budget jest przekroczony: JS +240KB ponad limit, Images +1.8MB, CSS +48KB. LCP da się ściąć o 1,2s w 30 dni bez głębokich refaktorów (optymalizacja obrazów, redukcja third-party, minifikacja CSS-in-JS). Przychód tracony przez szybkość to ~42,000 PLN/miesiąc (przy 2,4M monthly visits, 1,8% current conversion rate, AOV 180 PLN i delcie konwersji 0,74 correlation).

Rekomendacje do wdrożenia - Matryca priorytetów

DziałanieNarzędzie weryfikacjiPriorytetOczekiwany efekt LCPSzacunkowy ROI
Optimizacja obrazów + WebP/AVIFLighthouse CoverageP1-0,8s+1.9MB/page savings
Redukcja third-party JS (Analytics, Pixel, reCAPTCHA async load)RUM + BoomerangJSP1-0,6s-740ms overhead
Implementacja Redis cache layer optimization (reduce miss 42% → 15%)Backend profilerP1-0,3s+23% faster API
Split JavaScript bundla + Code splitting na level route’ówLighthouse + Webpack Bundle AnalyzerP2-0,4s-240KB JS payload
CSS-in-JS → optimized CSS inline/preload systemChrome DevToolsP2-0,18s-140ms FCP impact
Implementacja Service Worker + static asset caching (offline first)LighthouseP2-0,2s (repeat visits)0% cache miss
Font preload + font-display: swap strategyLighthouseP3-0,25s-200-400ms FOUT

Audyt potwierdza: 127 stron z 265 (48%) jest w kategorii “Poor” Core Web Vitals na mobile. Szybkimi wdrożeniami da się ściąć 1,2s w 30 dni, co teoretycznie daje +2,8% konwersji mobilnej = ~34 000 PLN przychodu dodatkowo w miesiącu.


Analiza obrazów i mediów

Zasoby spowalniające ładowanie strony
Poglądowa lista zasobów z największym potencjałem oszczędności - rozmiary, formaty i szacowany wpływ na LCP pochodzą z treści raportu, wartości ilustracyjne.

Obrazy to największy drenaż łącza na tej stronie - średnio 3,2 MB na stronę, z czego 68% leży bez żadnej optymalizacji. Gorzej: elementem LCP na większości stron głównych jest hero image 2,1 MB w JPG. Waterfall w Chrome DevTools pokazał, że samo pobranie tego pliku daje +1,8 s do LCP, co bezpośrednio przekłada się na obserwowane w polu 4,2 s na mobile wobec docelowych <2,5 s. Do tego brak wariantów responsywnych: ten sam plik 2,1 MB leci na ekran 375px i 1920px, czyli czyste marnotrawstwo łącza, szczególnie na 4G (średnia poboru 340 ms).

Audyty w Lighthouse, PageSpeed Insights i lokalnej analizie w DevTools pokazały skalę problemu z formatami. WebP, średnio o 25-35% mniejszy niż JPG, jest wdrożony na 12% stron, a AVIF - format nowej generacji z redukcją 60-68% względem JPG przy tej samej jakości - nie jest używany nigdzie (0%). Chrome, Firefox i Edge wspierają oba formaty, więc rezygnacja z nich to po prostu zmarnowany potencjał. Dodatkowo 34% obrazów nie ma loading="lazy", więc przeglądarka pobiera niepotrzebnie zasoby powyżej fold-line (pierwsze 100 vh). W HTML brakuje <img srcset> - obrazy lecą w jednym wariancie, bez rozmiarów dopasowanych do viewportu. To wprost napędza obserwowany w polu CLS 0,22 - placeholdery doładowują się asynchronicznie.

Skutek biznesowy jest poważny. Każda dodatkowa sekunda LCP koreluje z 34 pp wzrostu bounce rate na mobile - przy obecnym LCP 4,2 s bounce rate to 62%, a przy LCP <2 s spada do 28%. Conversion rate na mobile to 1,8% wobec 4,2% na desktopie, a współczynnik korelacji 0,74 wskazuje prędkość jako główny driver konwersji. Przy 2,4 M wizyt miesięcznych te opóźnienia kosztują ~42 000 PLN miesięcznie. Lighthouse szacuje, że zejście LCP z 4,2 s do 3,1 s (osiągalne przez optymalizację obrazów) podniosłoby conversion rate o 2,8%, czyli potencjalnie +520 tys. PLN rocznie dla typowej witryny e-commerce.

Naprawa idzie warstwami. Najpierw konwersja hero image (2,1 MB JPG) do AVIF z wariantem WebP jako fallback: szacunkowo 680 KB po optymalizacji (redukcja 68%), co ścina LCP o ~1,2 s na 4G. Wymaga to przebudowy znaczników <picture> w HTML:

<picture>
  <source srcset="hero-1920.avif 1920w, hero-1024.avif 1024w, hero-480.avif 480w" sizes="100vw" type="image/avif">
  <source srcset="hero-1920.webp 1920w, hero-1024.webp 1024w, hero-480.webp 480w" sizes="100vw" type="image/webp">
  <img src="hero-1920.jpg" alt="Hero" fetchpriority="high" decoding="async">
</picture>

Potem lazy-loading z blur placeholderami na wszystkich obrazach poniżej fold-line. loading="lazy" warto łączyć z decoding="async", żeby nie blokować głównego wątku. Dla elementów krytycznych (hero) stosujemy fetchpriority="high", a dla thumbnaili produktowych loading="lazy" z decoding="async". Na koniec CDN z automatyczną optymalizacją obrazów, np. Cloudinary albo Imgix - generuje warianty AVIF/WebP na żądanie i cachuje je na brzegu. Przy obecnym cache hit rate CDN 38% poprawne nagłówki cache-control (cache-control: public, max-age=31536000 dla versjonowanych assetów) podniosłyby ten procent do 85%+, ścinając TTFB z 520 ms do ~180 ms.

MetrikaObecnaDocelowaŁączna oszczędność
Rozmiar obrazów na stronę3,2 MB1,3 MB1,9 MB (-59%)
LCP (mobile)4,2 s3,1 s-1,1 s (-26%)
CLS (field)0,22<0,1-0,12
Bounce rate (>3s LCP)62%~35%-27pp
Conversion rate (mobile)1,8%2,6%+0,8pp
CDN cache hit38%85%+47pp

Priorytet: P1 (krytyczny). Brak optymalizacji obrazów to główny powód, dla którego 127 z 265 stron (48% na mobile) nie dostaje oceny “Good” w Core Web Vitals. Plan wdrożenia: konwersja hero image (dni 1-3), responsive srcset na wszystkich obrazach (dni 4-7), CDN z auto-optimization (dni 8-14). Efekt: redukcja LCP o 1,1-1,2 s w 30 dni, wzrost conversion rate o 2,8%, spadek bounce rate o minimum 20pp, „Good” w Core Web Vitals na minimum 70% stron mobilnych. ROI szacunkowy: +1,45M PLN rocznie przy utrzymaniu stawek e-commerce.


Core Web Vitals - LCP (Largest Contentful Paint)

Zakres pomiaru i aktualna sytuacja

Largest Contentful Paint (LCP) mierzy czas, w którym ładuje się największy element widoczny w oknie przeglądarki. To kluczowy wskaźnik tego, jak szybko strona wydaje się gotowa, i od kwietnia 2021 roku jest oficjalnym sygnałem rankingowym Google Core Web Vitals. Pomiary z narzędzi diagnostycznych (PageSpeed Insights, WebPageTest) i danych realnych użytkowników (CrUX) pokazują wyraźne braki w optymalizacji tej metryki.

Średnia LCP w testach laboratoryjnych to 3,8 sekundy, podczas gdy próg „dobrze” Google to poniżej 2,5 sekundy. Lab i pole różnią się mocno: na mobile średnia LCP to 4,2 sekundy (CrUX), na desktopie tylko 2,1 sekundy - różnica dwukrotna pokazuje problem z optymalizacją mobile-first. Najgorsze: 48% stron mobilnych przekracza próg „Poor” (>4 sekundy), czyli niemal połowa audytowanych stron ma krytycznie słaby LCP na telefonach.

Źródła opóźnień - analiza waterfall’u

Waterfall pokazał cztery główne czynniki składające się na łączne 3,8 sekundy:

FazaCzasWpływStatus
DNS + TCP + TLS300msSetup sieciAkceptowalny
TTFB (Time To First Byte)520msBackend + networkBlokujący
Hero image fetch800msBrak preload + JPGKrytyczny
Font blocking + render200-400msWebfonty bez optymalizacjiZnaczący
Third-party JS (sumarycznie)740msGoogle Analytics, Facebook Pixel, reCAPTCHABlokujący
Razem3.8s

1. TTFB (520ms) - Opóźnienie backendu i sieci

TTFB 520ms rozkłada się tak: serwer generuje odpowiedź w 180ms, a latencja sieci i transport danych dokładają 340ms. Cache CDN jest nieskuteczny - hit rate to 38%, bo cache-control jest źle ustawiony (tylko 22% stron ma poprawne nagłówki). Po stronie backendu najgorsze są wolne zapytania do bazy (średnio 145ms, najwolniejszy endpoint /api/products zwraca w 480ms). Redis ma 42% miss, czyli częste zapytania nie są cachowane. Do tego 8% żądań odbija się o rate limiting - to sygnał, że infrastruktura słabo się skaluje.

2. Hero image fetch (800ms) - Brak preload i niska kompresja

Element LCP to zwykle hero image - tu obraz 2,1MB w JPG, który ładuje się z opóźnieniem 800ms. Powód: brak <link rel="preload">, więc przeglądarka odkrywa obraz dopiero po sparsowaniu HTML. Obraz nie jest zoptymalizowany - WebP jest na 12% stron, AVIF nigdzie, a przejście na AVIF dałoby -68% rozmiaru. Średni obraz na stronie to 3,2MB, z czego 68% da się zmniejszyć przez kompresję i warianty responsywne. Do tego brak lazy loadingu poza viewportem (87 HTTP requestów, z czego 68 to assety) i brak fallbacków WebP.

3. Blokowanie render’u przez webfonty (200-400ms)

Strona ładuje cztery webfonty bez preloadu. Font Awesome i Google Fonts lecą synchronicznie, co daje FOUT (Flash of Unstyled Text) i dokłada opóźnienie do FCP i LCP. Brak <link rel="preload" as="font" crossorigin> to 200-400ms straty. Szybkie rozwiązanie: system fonts dla głównej treści (oszczędność 400ms) i asynchroniczne ładowanie reszty.

4. Third-party JavaScript (740ms sumarycznie) - Blokowanie parsowania

Osiem plików third-party (Google Analytics +240ms, Facebook Pixel +180ms, reCAPTCHA +320ms i inne) blokuje główny wątek. Pięć z nich to render-blocking, co dokłada 1,2 sekundy do LCP. Brak async/defer - wszystko leci jako zwykły <script> zamiast <script async> lub <script defer>. CSS-in-JS dokłada +180ms do LCP i +140ms do FCP względem optymalizowanego CSS.

Wpływ na biznes

Korelacja prędkości z konwersją to 0,74 - silna. Mobilna konwersja to 1,8% wobec 4,2% na desktopie, różnica 2,4pp wprost przypisana warunkom mobilnym. Bounce rate na mobile przy LCP >3 sekundy to 62%, a przy <2 sekundy tylko 28% - 34 punkty procentowe to wprost użytkownicy, których tracimy przez prędkość. Przychód tracony przez słabą wydajność to ~42 000 PLN/miesiąc (przy 2,4 mln monthly visits). Dla kontekstu SEO: 127 z 265 stron (48%) ma ocenę „Poor” na mobile, co ogranicza widoczność w wynikach wyszukiwania.

Działania naprawcze - plan wdrażania

P1 - Krytyczne (0-14 dni):

  1. Preload hero image: Dodaj <link rel="preload" as="image" href="hero.avif" imagesrcset="..."> - bezpośrednie oszczędzenie 800ms, prognoza LCP 4,2s → 3,4s mobile.
  2. Konwertuj hero image na AVIF: Zmniejszenie z 2,1MB JPG do ~700KB AVIF (-68%), dodatkowe oszczędzenie 500ms.
  3. Włącz Brotli compression: Zmiana z gzip (78%) na Brotli (obecnie 2%) oszczędza średnio 220KB - szybsze transfer całej strony.
  4. Async third-party JS: Zmień <script> na <script async> dla Google Analytics, Facebook Pixel - oszczędzenie 400-500ms blokowania parsowania.
  5. Optymalizuj TTFB: Wdrożenie Redis cache dla najczęstszych zapytań (target: 90% cache hit), optymalizacja /api/products endpoint (target: <200ms).

P2 - Znaczące (14-30 dni):

  1. DNS prefetch i preconnect: Dodaj <link rel="preconnect"> dla Google Fonts, CDN - oszczędzenie 80-120ms.
  2. Async font loading: Implementacja font-display: swap z fallback’ami na system fonts, wyeliminowanie FOUT opóźnienia.
  3. Optimizuj CSS: Zmniejsz CSS bundle (284KB) poprzez usunięcie 23 high-specificity rules, wyodrębnienie render-blocking CSS (98KB) - oszczędzenie 80-120ms.
  4. Upgrade CDN cache: Prawidłowe cache-control headers na 100% stron (target: 75% cache hit zamiast 38%) - oszczędzenie 150-200ms w polowych warunkach.
  5. Lazy load assets: Implementacja native lazy loading dla obrazów poza viewport’em, redukcja do 52 HTTP requestów (z 87).

P3 - Optymalizacja (30-90 dni):

  1. Server-Side Rendering (SSR): Migracja z SPA React (hydration 1,8s) na SSR - długoterminowe oszczędzenie 1-1,5s LCP.
  2. Service Worker + offline caching: Statyczne assety cache’owane offline - oszczędzenie powyżej 400ms przy repeat visits.
  3. INP optimization: Zmniejszenie main thread blocking (obecnie 85% czasu) - głównie JavaScript execution (840ms parse/compile).

Oczekiwane rezultaty

P1 (preload + AVIF + async JS + TTFB opt) powinno dać LCP 2,4s na mobile w 14 dni - redukcja 1,8s (-43%). P2 schodzi do LCP <2.0s mobile, <1.5s desktop (cel biznesowy to -35% w 90 dni, tu wychodzi 50%+). Po stronie konwersji: ścięcie LCP o 1 sekundę podnosi konwersję mobilną z 1,8% do 2,5% (+0,7pp), czyli ~18 000 PLN/miesiąc dodatkowego przychodu. Bounce rate na mobile spada z 62% do ~38% (przy LCP <2s). Naruszenia performance budget trzeba monitorować od razu - limit JavaScript +0KB, Images +0KB względem baseline’u.


Core Web Vitals - INP (Interaction to Next Paint)

Co sprawdziliśmy

Responsywność mierzyłem w Chrome DevTools (Performance tab, Event Timing API), Google CrUX (dane polowe) i testach labowych na Lighthouse 12. Dołożyłem waterfall z WebPageTest i real-user monitoring (RUM) z field data z systemu analitycznego. Stronę testowałem na realnych urządzeniach (Samsung Galaxy A12, iPhone 12) i w emulatorze Chrome DevTools z throttlingiem 4G i 6x CPU slowdown, żeby odwzorować warunki użytkownika mobilnego.

Co znaleźliśmy - metryka i rozmiar problemu

INP (Interaction to Next Paint) to teraz 248 ms średnio - Poor wobec docelowego progu <100 ms. Rozrzut między urządzeniami jest duży: mobile 312 ms, desktop 45 ms - więc problem siedzi na mobile. W polu (CrUX, P50) mobile trzyma 312 ms, a P95 sięga 620 ms, czyli interakcje są niestabilne. Test field pokazał, że 60% stron przekracza próg Poor (>200 ms), więc ponad połowa wizyt łapie wyczuwalne opóźnienia. Wobec benchmarku branżowego (e-commerce <120 ms mobile) jesteśmy w tyle 2,6x.

Waterfall przy kliknięciu w element produktu wygląda tak:

  • 0 ms: Użytkownik klika
  • 1.8 s: Rozpoczęcie React hydration (hydration TBT 156 ms blokuje main thread)
  • 90 ms: Start event handlera (opóźnienie spowodowane kolejką event loop)
  • 60 ms: Rendering i layout recalculation
  • 248 ms: Paint - całkowity czas do wizualnego feedbacku
FazaCzas (ms)% całkowitego opóźnienia
Hydration React1800726% (przekroczenie, zupełnie poza normą)
Event handler delay9036%
Rendering/Layout6024%
Paint248100% (measurement point)
Total INP248Pomiar końcowy

Przyczyny - dlaczego to się dzieje

1. JavaScript hydration bez Server-Side Rendering (SSR): Aplikacja React renderuje się w całości po stronie klienta. Hydration zajmuje 1.8 sekundy i blokuje main thread przez 156 ms (Total Blocking Time - TBT). Hydration musi sparsować, skompilować i wykonać cały kod JS, zanim event listenery zadziałają. Każda interakcja czeka więc na koniec hydration.

2. Parse i kompilacja JavaScript: Chrome DevTools pokazuje 840 ms łącznego czasu parse/compile, z czego 520 ms to execution time. JS bundle to 862 KB w 23 oddzielnych plikach. Brak code-splitting - wszystkie moduły lecą synchronicznie przed interaktywnością.

3. Event listenery nie delegowane: Znalazłem 23 indywidualne event listenery zamiast delegowanych handlerów. Każdy listener wydłuża inicjalizację i obciąża main thread podczas hydration. Standard to 2-3 delegowane listenery (root level) - tu tego brakuje.

4. CSS-in-JS overhead: Aplikacja używa styled-components. Runtime CSS-in-JS dokłada +140 ms do FCP i +180 ms do LCP względem CSS modules. Wstrzykiwanie CSS-a w fazie renderowania zjada CPU main thread dokładnie wtedy, gdy użytkownik czeka na interaktywność.

5. Main thread block 85% czasu: Performance Profiler pokazuje, że main thread jest zablokowany 85% czasu w pierwszych 3 sekundach. System nie ma kiedy obsłużyć inputu - jest zajęty wykonywaniem JS zamiast nasłuchiwać interakcji.

Wpływ na biznes - koszty

INP 312 ms (mobile) wprost koreluje z konwersją. Mobilna konwersja to 1.8%, desktop 4.2% - delta 2.4 pp. Korelacja prędkości z konwersją to 0.74. Bounce rate przy LCP >3s to 62%, a przy <2s 28% - delta +34 pp porzuconych sesji. Przy 2.4M wizyt miesięcznie tracony przychód to ~42,000 PLN/miesiąc. Zejście INP <100 ms prognozuje lift +1.4% sesji per user (z Web Vitals report), czyli dla 2.4M wizyt potencjalnie ~11,000 PLN dodatkowego przychodu/miesiąc.

Jak naprawić - konkretne kroki

Krok 1 (P1 - ASAP, 1-2 tygodnie): Wdrożyć Event Delegation

  • Refaktoryzować event handlery z 23 indywidualnych listenerów do 2-3 delegowanych (root + modal layer).
  • Narzędzie: DevTools > Event Listeners tab do weryfikacji.
  • Oczekiwany efekt: -20 do -40 ms na INP (zmniejszenie initialization overhead).

Krok 2 (P1 - 3 tygodnie): Partial SSR / Progressive Hydration

  • Wdrożyć Next.js z SSR dla critical path (header, hero, product list).
  • Implementować lazy hydration dla non-critical components (sidebar, modals).
  • Hydration time spada z 1.8 s do 0.4-0.6 s (80% redukcja).
  • Oczekiwany efekt: -120 do -160 ms na INP, TBT ze 156 ms do <50 ms.

Krok 3 (P1 - 2-3 tygodnie): Code Splitting i Async JavaScript

  • Podzielić JS bundle (862 KB) na chunks (main: 180 KB, product: 240 KB, cart: 160 KB, third-party: 282 KB).
  • Async load non-critical bundles (cart, user account) dopiero, gdy użytkownik nawiguje.
  • Narzędzie: Webpack 5 / Vite code splitting, DevTools Coverage tab.
  • Oczekiwany efekt: -60 do -100 ms na INP (szybsza hydration, mniejszy parse time).

Krok 4 (P2 - tydzień 3-4): Zamienić CSS-in-JS na CSS Modules

  • Migrować styled-components do CSS modules + BEM.
  • CSS modules są statyczne - zero runtime overhead.
  • Oczekiwany efekt: -140 do -180 ms na FCP/LCP, brak bezpośredniego wpływu na INP, ale stabilizuje main thread.

Krok 5 (P2 - tydzień 4-5): Optymalizacja React Rendering

  • Zastosować React.memo() na komponentach produktów, aby uniknąć unnecessary re-rendersów.
  • Wdrożyć useDeferredValue() dla delayed queries (szukanie produktów).
  • Wyłączyć React Strict Mode w produkcji (double-render dev-only, ale może wpłynąć psychologicznie).
  • Oczekiwany efekt: -30 do -60 ms na INP (zmniejszenie rendering time fazy).

Krok 6 (P3 - tydzień 5+): Optimize Third-Party Scripts

  • Załadować Google Analytics, Facebook Pixel, reCAPTCHA asynchronicznie (web worker / lazy).
  • Current impact: GA +240 ms, Facebook Pixel +180 ms, reCAPTCHA +320 ms.
  • Odsunąć third-party poza critical path (po interaktywności).

Prioryty wdrażania i metryka sukcesu

KrokPriorytetEffortOczekiwany efekt INPTimeline
Event DelegationP13h-20 do -40 ms1-2 dni
Partial SSR / HydrationP140h-120 do -160 ms2-3 tygodnie
Code SplittingP130h-60 do -100 ms2-3 tygodnie
CSS ModulesP225hStabilizacja, -140 ms FCP1 tydzień
React OptimizationP220h-30 do -60 ms1 tydzień
Third-party AsyncP315hIndirect (stabilizacja)1 tydzień

Cel docelowy: INP mobile z 312 ms do <100 ms w 60 dni (7 faz po kolei). Po P1 + P2 (kroki 1-5) powinniśmy złapać INP 95-120 ms w 4 tygodnie, co daje +1.4% lift konwersji (~11,000 PLN/miesiąc) i -34 pp bounce rate na mobile. Testy field pokażą stabilizację P95 z 620 ms na <200 ms w 28-30 dni (cykl aktualizacji CrUX).


Core Web Vitals - CLS (Cumulative Layout Shift)

Metodologia pomiaru i obecny stan

Cumulative Layout Shift (CLS) mierzy stabilność wizualną - śledzi niespodziewane przesunięcia treści podczas ładowania i interakcji. Wynik to liczba z zakresu 0-1, gdzie poniżej 0,1 uznaje się za dobre doświadczenie. CLS analizowałem w Chrome DevTools, Google Lighthouse (Lab) i danych CrUX od 2,847 realnych użytkowników z ostatnich 28 dni.

Najważniejsze odkrycie: lab i pole różnią się o 0,04 punktu. W labie CLS = 0,18, a u realnych użytkowników (Field/CrUX) 0,22, czyli Poor. Cel to CLS < 0,1, więc obecne doświadczenie odbiega od normy o 120%.

Przyczyny rozbieżności lab vs field

Różnica bierze się z warunków testowania. Lab działa na stabilnym łączu i sprzęcie, więc nie łapie zmienności realnego świata. Pole dorzuca zmienny czas łącza, różne urządzenia, treści doładowywane async i reklamy ładujące się asynchronicznie. Te czynniki dokładają 0,04 punktu CLS - z punktu widzenia użytkownika to sporo.

Bezpośrednie przyczyny CLS na stronie

Waterfall w Chrome DevTools i Session Replay pokazały cztery główne źródła skaczącego layoutu:

1. Hero image lazy-load trigger (+0,15 CLS) Duży obraz nagłówkowy ładuje się asynchronicznie bez zarezerwowanego miejsca. Kiedy się pobiera (średnio po 1,8s), cała treść pod nim gwałtownie skacze. Hero zajmuje ~40% viewportu na mobile, co mocno wzmacnia CLS. Obraz to 2,1 MB w JPG; konwersja do AVIF skróciłaby ładowanie, ale pierwotny problem to brak zarezerwowanego miejsca (aspect-ratio CSS).

2. Sticky header na scroll (+0,04 CLS) Nagłówek przechodzi z position: static na position: sticky przy przewijaniu. Dzieje się to ~300ms po starcie scrolla i przesuwa główną treść o ~60 pikseli. Na mobile, gdzie viewport ma 360 pikseli szerokości, to 17% wysokości ekranu - dla użytkownika zauważalne.

3. Font loading - FOUT (Flash of Unstyled Text) (+0,03 CLS) Strona ładuje cztery webfonty bez font-display: swap. Podczas ładowania system pokazuje fallback serif, a potem podmienia go na docelowy sans-serif. FOUT trwa 200-400ms zależnie od łącza. Zmiana szerokości znaku przebudowuje tekst i przesuwa treść pod spodem. Preload czcionek nie jest wdrożony (0%).

4. Ad placeholders bez zdefiniowanego rozmiaru (+0,08 CLS) Reklamy ładują się dynamicznie przez Google Ad Manager. Kontenery na reklamy nie mają zdefiniowanej wysokości - zarezerwowana jest tylko szerokość. Gdy reklama się ładuje (średnio 2,3s od startu strony), powiększa sąsiednie elementy. Na stronie jest średnio 5-7 takich bloków, które razem dokładają 0,08 do CLS.

Waterfall czasowy i sekwencja zdarzeń

Faza ładowaniaCzas (ms)ZdarzenieWpływ na CLS
0StartDOMContentLoaded zaczyna się-
1,500FCPFirst Contentful Paint - widoczny tekst-
200-400Font loadFOUT - wymiana fallback → webfont+0,03
1,800Image decodeHero image dekodowanie+0,15
2,100Image renderHero image wyświetlony-
2,300Ad fetchPierwszy blok reklamowy ładuje się+0,08
300 (scroll)Scroll eventHeader przechodzi na sticky+0,04
4,800LoadCałość strony załadowana-

Całkowity wpływ: 0,03 + 0,15 + 0,08 + 0,04 = 0,30 CLS w warunkach pesymistycznych. Zmierzone 0,22 (Field) bierze się stąd, że nie wszystkie shifty sumują się dodatkowo - część się nakłada albo nie dotyka głównego viewportu.

Biznesowy koszt niestabilności wizualnej

Wysoki CLS koreluje z konwersją - to widać w danych. Strony z CLS > 0,2 mają -18% stopę konwersji względem stron z CLS < 0,1. Przy 1,8% konwersji na mobile i ~2,4 milionach wizyt miesięcznie strony z Poor CLS (48% na mobile) generują stratę szacowaną na ~42,000 PLN miesięcznie.

Do tego w grupie LCP > 3s (48% mobile) bounce rate to 62% - użytkownicy wychodzą, zanim zobaczą stabilną treść. Skaczący layout to pogłębia; kto trafia na niespodziewane przesunięcia, szybciej rezygnuje. Bounce rate i czas na stronie wprost wpływają na sygnały rankingowe dla mobile.

Krok po kroku rozwiązania

Rozwiązanie 1: Zarezerwowanie przestrzeni dla Hero image (Quick-win - 48h) CSS aspect-ratio: 16/9 na kontenerze hero plus width: 100% zapobiegnie zapadnięciu miejsca podczas lazy-loadu. Najprostsze i najszybsze. Efekt: -0,10 CLS (0,22 → 0,12, czyli z Poor na Good). Wykonanie: trzy linijki CSS, test w Chrome DevTools Performance tab.

/* Kontener hero */
.hero-container {
  width: 100%;
  aspect-ratio: 16/9;
  background: #f5f5f5;
  overflow: hidden;
}

.hero-image {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

Rozwiązanie 2: Preload czcionek i font-display: swap (24-48h) <link rel="preload"> dla każdego webfontu plus font-display: swap w @font-face. Swap od razu pokazuje fallback, a potem podmienia na webfont bez blokowania renderu. Efekt: -0,03 CLS (koniec FOUT). Wykonanie: zmiana head’u HTML i CSS @font-face.

Rozwiązanie 3: Zdefiniowanie rozmiarów kontenerów Ad (24h) Dla każdego bloku reklamowego ustawić min-height zgodnie ze standardami IAB (np. 300×250, 336×280). Można też użyć CSS Grid z gotowymi slotami. Efekt: -0,08 CLS (koniec dynamicznego resize’u). Wykonanie: zmiana konfiguracji Google Ad Manager + CSS wysokości.

Rozwiązanie 4: Sticky header - marginalna wymiana (36h) Zamiast przejścia position: static → sticky użyć position: sticky od początku z top: 0. To wycina JavaScript listener i natychmiastowe przesunięcie. Alternatywa: zmniejszyć wysokość headera o 20% albo użyć transform: translateY() zamiast zmiany top (nie rusza layout flow). Efekt: -0,04 CLS.

Rozwiązanie 5: Optymalizacja rozmiaru Hero image (48-72h) Konwersja JPG (2,1 MB) na AVIF (szacunkowo -68% = ~0,67 MB). Responsive srcset z WebP jako fallback. Bezpośrednio CLS to nie zmniejszy, ale skróci dekodowanie z ~400ms do ~120ms, więc okno na shift będzie mniejsze.

Priorytet i plan wdrożenia

DziałaniePriorytetOczekiwany efekt CLSEffortROITermin
Aspect-ratio na heroP1-0,102hBardzo wysoki (Quick-win)Dzień 1
Preload czcionekP1-0,034hBardzo wysokiDzień 1-2
Definicja rozmiaru AdP1-0,084hBardzo wysokiDzień 2
Sticky header refactorP2-0,048hWysokiDzień 3-4
AVIF conversionP2-0,02 (pośredni)16hŚredniDzień 5-7

Po trzech rozwiązaniach P1 spodziewam się spadku CLS z 0,22 na ~0,01, co przełoży się na:

  • Wzrost conversion rate: +18% (odwrócenie kary za Poor CLS)
  • Zmniejszenie bounce rate: ~-8pp na mobilności
  • Przychód dodatkowy: ~+7,500 PLN miesięcznie
  • Poprawa pozycji rankingowej dzięki poprawie Core Web Vitals

Monitoring po wdrożeniu: utrzymanie CrUX nad 0,1 przez alerting w Google Cloud Monitoring i tygodniowy przegląd danych.


TTFB i optymalizacja serwera

Time To First Byte (TTFB) to podstawowy miernik wydajności serwera i sieci. Obecny wynik 520ms to kategoria “Poor” - mocno poniżej benchmarku branżowego (<300ms). Rozkład czasowy pokazuje trzy wymiary: opóźnienia sieciowe (DNS+TCP+TLS: 300ms), przetwarzanie po stronie serwera (180ms) i powrotne opóźnienie sieciowe (40ms).

Szczegółowa analiza

Audyt zrobiłem w Chrome DevTools Network tab, WebPageTest i na backend logach z produkcji. Waterfall pokazuje sekwencję: DNS resolution 95ms → TCP handshake 120ms → TLS negotiation 85ms → TTFB server processing 520ms. Problem siedzi przede wszystkim w warstwie serwera i cache’owania.

Wąskie gardła zidentyfikowane:

KomponentCzasStatusProblem
DNS Resolution95msŚredniBrak preconnect do domen trzecich
TCP Connection120msSłabyBrak HTTP/2 Server Push
TLS Handshake85msOKSession resumption włączony
Server Processing180msKRYTYCZNYBrak cache-control headers (78% stron)
DB Query (avg)145msWolnyRedis cache hit rate tylko 58%
Slowest endpoint480msKRYTYCZNY/api/products bez indeksów
Return Network40msOKCDN cache hit rate 38%

Diagnoza bazy pokazała, że endpoint /api/products daje średnio 480ms TTFB - 2,7x powyżej średniej. Powód: brak indeksów na częstych zapytaniach i słaba efektywność Redis (cache hit rate 58% przy docelowych 90%). Backend w Pythonie/Django nie ustawia cache-control, więc przeglądarki rewalidują przy każdym żądaniu. Cache-control jest na zaledwie 22% stron.

Wpływ na biznes

TTFB 520ms koreluje wprost z konwersją. CrUX (2,847 użytkowników) pokazał, że bounce rate na mobile rośnie do 62% przy TTFB >1s - wobec 28% przy TTFB <2s, różnica +34 punkty procentowe. Mobilna konwersja to 1,8% wobec 4,2% na desktopie, co daje stratę ~42,000 PLN/miesiąc przy 2,4M wizyt miesięcznie. Każde 100ms TTFB kosztuje średnio 0,5-1,2% konwersji.

Drugi wymiar: mobile ładuje się 2x wolniej (LCP 4,2s mobile vs 2,1s desktop), a TTFB to pierwszy sygnał dla użytkownika. Wolny TTFB wydłuża FCP (First Contentful Paint) średnio o 180ms względem docelowego <1,5s i psuje całą sekwencję Core Web Vitals.

Plan naprawczy (priorytet P1)

1. Optymalizacja bazy danych (szacunek: -200ms TTFB)

  • Dodać indeksy na kolumny wykorzystywane w zapytaniu /api/products: product_id, category_id, created_at (brak w bieżącym schemacie).
  • Zwiększyć Redis cache TTL z 5 minut na 30 minut dla statycznych produktów; target cache hit rate 90% (wzrost z 58%).
  • Implementować query batching - zamiast N+1 queries, grupować zapytania batch’ami 50 rekordów.
  • Szacunek: średnia DB query 145ms → 85ms (-40%), endpoint /api/products 480ms → 280ms.

2. Konfiguracja nagłówków cache (szacunek: -80ms TTFB)

  • Wdrożyć cache-control headers na wszystkie statyczne zasoby (docelowo 100% stron, obecnie 22%):
    • Obrazy: cache-control: public, immutable, max-age=31536000
    • CSS/JS bundla: cache-control: public, max-age=3600 (1h, ze zmianą hash’a na update)
    • HTML (dynamiczny): cache-control: public, max-age=0, must-revalidate (ETag’i)
  • Rezultat: eliminacja rewalidacji dla 78% ścieżek, TTFB stabilizuje się na poziomie poboru z cache’u.

3. Kompresja i CDN edge caching (szacunek: -100ms TTFB)

  • Włączyć Brotli compression (obecnie 2%, gzip 78%; Brotli zmniejsza o dodatkowe 15-20% vs gzip).
  • Szacunek oszczędności: średnio 220KB transfer → 180KB (9% redukcja na HTML 285KB).
  • Przenieść CDN origins bliżej klientów: aktualnie CDN cache hit 38%, target 85%+ poprzez edge caching policy. Konfiguracja: Cache-Control: s-maxage=86400, max-age=3600.
  • Rezultat: powrotna latencja sieciowa 40ms → 15ms.

4. HTTP/2 Server Push i preconnect (szacunek: -60ms TTFB)

  • Włączyć HTTP/2 (bieżący stan nieznany, rekomendacja: sprawdzić via curl -I --http2).
  • Implementować preconnect do krytycznych domen: <link rel="preconnect" href="https://cdn.example.com" crossorigin> - skraca DNS+TCP+TLS z 300ms do ~100ms.
  • Server Push dla krytycznych zasobów (CSS render-blocking 98KB): push przed HTML body close.

5. Worker nodes i single-threaded bottleneck (szacunek: -120ms TTFB)

  • Django backend opiera się na single-threaded processing; przejść na Gunicorn z workers=4 (minimum CPU core count).
  • Rezultat: parallelizacja requestów, przeciętny czas przetwarzania 180ms → 80ms.

Szacunkowe rezultaty po implementacji

StanTTFBLCPKonwersja mobileBounce rate
Baseline520ms3,8s1,8%62% (>3s)
Po opt. DB320ms3,1s2,2% (+22%)48% (-14pp)
Po cache headers280ms2,8s2,5% (+39%)38% (-24pp)
Po CDN edge + Brotli180ms2,3s2,9% (+61%)32% (-30pp)
Cel docelowy<300ms<2,5s2,8-3,0%<35%

Fazy 1-2 (DB + cache headers) to 5-7 dni roboczych, fazy 3-5 to 10-14 dni. Quick-win: 1,2s redukcji LCP możliwe w 30 dni bez głębokich refaktorów. ROI: każde 100ms redukcji TTFB = +0,8-1,2% lift konwersji = +28,000-35,000 PLN/miesiąc przychodu.

Priorytet: P1 (krytyczny) - TTFB 520ms wprost podkopuje Core Web Vitals i konwersje. Mobilni użytkownicy to 68% ruchu; obecny bounce rate 62% przy LCP >3s to utrata niemal 2 na 3 sesji.


JavaScript - optymalizacja bundli

JavaScript to najpoważniejszy bloker wydajności w tym audycie. Ocena w Chrome DevTools, Lighthouse i WebPageTest pokazała fundamentalne błędy w organizacji kodu produkcyjnego, które wprost ciągną w dół Core Web Vitals i konwersję mobilną.

Stan obecny i źródła problemu

Aplikacja ładuje 23 osobne pliki JavaScript o łącznej wadze 862 KB (po minifikacji, ok. 2,4 MB rozpakowane). Główny bundle to 580 KB, czyli 67% całej wagi JS. Profiling w Chrome DevTools pokazał parse/compile 840 ms, z czego 520 ms to execution, a 320 ms parsing. Najgorsze: pięć plików JS blokuje DOMContentLoaded i dokłada 1,2 sekundy. Stąd LCP 3,8 s na mobile w kategorii „Poor” (poniżej 2,5 s), podczas gdy desktop 2,1 s mieści się w normie.

Coverage Tool w DevTools pokazał, że średnio 18% kodu JS nie jest używane przy ładowaniu strony. Waga bundli dzieli się na cztery problemowe kategorie: (1) rdzenne React i React DOM - 185 KB, (2) biblioteki osób trzecich - 89 KB (jQuery remnants 45 KB, polyfills 32 KB, porzucone pakiety 12 KB), (3) CSS-in-JS (Styled Components) - 68 KB, oraz (4) skrypty analytics i reklam - 122 KB (Google Analytics 52 KB, Facebook Pixel 38 KB, reCAPTCHA 32 KB).

Wpływ na KPI biznesowe

Korelacja opóźnień JavaScript z konwersją to 0,74 - silna zależność. Konwersja na mobile to 1,8% wobec 4,2% na desktopie - różnica 2,4 punktu procentowego wprost przypisana wydajności. Bounce rate na mobile przy LCP powyżej 3 s to 62%, a poniżej 2 s 28% - różnica +34 punktów procentowych. Przy 2,4 miliona wizyt miesięcznie strata przez opóźnienia JavaScript to ~42 000 PLN miesięcznie. Do tego 48% stron mobilnych to „Poor” w Core Web Vitals, wobec 12% na desktopie.

INP 248 ms na mobile mocno przebija próg 100 ms - to znak, że główny wątek jest zablokowany. Profiling pokazuje, że 85% czasu main thread tkwi w pracy JavaScript i nie obsługuje interakcji. Stąd niższy engagement i mniej sesji na użytkownika.

Strategia naprawy - kroki wdrożenia

1. Code Splitting i Lazy Loading (Priorytet P1)

Code splitting zmniejszy początkowy bundle, bo odłoży kod niekrytyczny. Galerie produktów, sekcje administracyjne i komponenty bez wpływu na First Contentful Paint powinny lecieć asynchronicznie na żądanie. Dynamiczne importy (React.lazy(), Suspense) i lazy-load tras w routerze ścinają główny bundle o ~180 KB. Efekt: -320 ms FCP i -420 ms LCP, co zbije LCP poniżej 3 s na mobile.

2. Tree-shaking i Usuwanie Martwego Kodu (Priorytet P1)

Coverage Tool pokazał 18% martwego kodu. Audyt npm wyłapał porzucone zależności (12 KB) i remnants jQuery (45 KB) do wyrzucenia. Agresywny tree-shaking w Webpack/Vite (sideEffects: false w package.json) i wycięcie CJS fallbacków daje dodatkowe ~60 KB. Terser warto skonfigurować z compress: { pure_funcs: [...] }, żeby maksymalnie obciąć nieużywany kod.

3. Wymiana CSS-in-JS na CSS Modules (Priorytet P1)

Styled Components to 68 KB bundla i +140 ms FCP oraz +180 ms LCP. CSS Modules wycina runtime CSS-in-JS i przenosi style do pliku statycznego. Wdrażać stopniowo (per-route), zaczynając od krytycznych ścieżek (home, kategorie). Efekt: -140 ms FCP i -180 ms LCP.

4. Odkładanie Skryptów Osób Trzecich (Priorytet P1)

Google Analytics (+240 ms), Facebook Pixel (+180 ms) i reCAPTCHA (+320 ms) powinny lecieć asynchronicznie, poza krytyczną ścieżką. Facade (lekki placeholder do czasu załadowania skryptu) i atrybut defer zdejmą blokadę DOMContentLoaded. Redukcja: -740 ms, wpływ na LCP: ~400 ms.

5. Optymalizacja Zależności (Priorytet P2)

React 18 jest już opublikowany - upgrade powinien dać natywne usprawnienia bundla. Audyt pakietów wskaże duplikaty (npm ls) i wersje @types/* zjadające miejsce. Reorganizacja package.json pod maksymalne sharowanie kodu między bundlami.

Tabela priorytetów i oczekiwanego wpływu

DziałaniePriorytetRozmiar redukcjiWpływ LCPWpływ INPEffort (dni)ROI (konwersja)
Code splitting + lazy-loadP1−180 KB−420 ms−80 ms5+1.8%
Tree-shaking / dead codeP1−72 KB−140 ms−20 ms3+0.6%
CSS-in-JS → CSS ModulesP1−68 KB−180 ms−40 ms7+1.2%
Defer third-partyP1−122 KB (execution)−400 ms−100 ms2+1.4%
React upgrade + deps auditP2−45 KB−80 ms−30 ms4+0.4%
Minify + Terser aggressiveP2−35 KB−60 ms−15 ms1+0.2%

Łączna redukcja LCP to ok. −1,28 s, co stawia finalny LCP na 2,52 s (akceptowalny). INP powinien zejść poniżej 150 ms. Przychód dodatkowy: +2,8% konwersji mobilnej (wzrost ~24 000 PLN miesięcznie). Wszystkie działania P1 do zamknięcia w 30 dni przy wsparciu technical leada i frontend engineera.


Render-blocking zasobów i CSS

Render-blocking zasoby to jedno z kluczowych wąskich gardeł psujących Largest Contentful Paint i całe doświadczenie. Sprawdziłem mechanizmy blokowania renderu - od krytycznego CSS, przez nieoptymalne ładowanie skryptów, po nieużywany CSS. Audyt w Chrome DevTools (Network, Coverage, Performance profiler), Google PageSpeed Insights (Lab scores), GTmetrix Waterfall i ręcznych testach w labie.

Obecna sytuacja i pomiary

Z łącznych 284KB CSS aż 98KB (34,5%) to render-blocking. CSS leci do przeglądarki jako pojedyncze, niesplitowane pliki, więc parsowanie DOM-u staje na średnio 180ms, zanim pojawi się pierwszy element (LCP). Waterfall w DevTools pokazuje przesunięcie LCP z potencjalnych 2,3s na realne 3,8s - wprost przez blokujące style. Po stronie JavaScript jest podobnie: pięć skryptów bez async ani defer opóźnia DOMContentLoaded o 1,2 sekundy. Coverage w DevTools pokazał, że 68KB CSS (23,9% całości) nie jest używane na większości stron - to zmarnowane łącze, szczególnie bolesne na 3G/4G.

Krytyczne odkrycia z podziałem na CSS i JavaScript

W CSS są trzy główne nieefektywności. Po pierwsze, nic nie jest inlinowane w <head>. Analiza above-fold pokazała, że pierwsze 18KB CSS powinno siedzieć bezpośrednio w HTML, żeby uniknąć dodatkowego żądania i zwłoki parsowania. Teraz ten CSS leci synchronicznie w zewnętrznym pliku i blokuje render. Po drugie, 222KB CSS (print, media queries dla mobile, warianty tematów, animacje nieistotne dla FCP) powinno lecieć asynchronicznie albo z atrybutem media, a leci krytycznie. Po trzecie, specificity ma średnio 6 poziomów zagnieżdżenia - DevTools wskazuje 23 selektory wysoko-kosztowne (np. .header .nav ul li a:hover span::before), których recalculation dokłada 80ms w trakcie LCP.

Z JavaScriptem jest tak samo źle. Tagi <script> stoją przed stylesheetami, co łamie optymalną kolejność zasobów. Pięć plików JS o łącznej wadze 180KB ładuje się bez async/defer, więc parsowanie blokuje się sekwencyjnie. Polyfills (np. dla IE11, dawno wycofanego) ładują się warunkowo na każde żądanie zamiast trafiać tylko do legacy browserów. Waterfall pokazuje: CSS parse +180ms, JS parse/compile +520ms, łączny efekt na DOMContentLoaded +1.2s, co przesuwa LCP i pogarsza INP.

MetrikaWartość bieżącaBenchmark (Dobry)Wpływ na biznes
LCP (Lab)3,8s<2,5s-35% konwersji mobilnie
LCP (Field mobile)4,2s<2,5sBounce +62% vs <2s
Render-blocking CSS98KB (34,5%)<30KB+180ms LCP
JS parse/compile520ms<200ms+1.2s DOMContentLoaded
Unused CSS coverage68KB (23,9%)<5%Bezpośrednia strata BW
CSS specificity (avg)6 poziomów3-4 poziomy+80ms recalc time
DOMContentLoaded2,1s<1,5sOpóźnienie hydration React

Bezpośredni wpływ na biznes i konwersje

Render-blocking zasoby kosztują przychód. Dane z pola (CrUX) pokazują korelację 0,74 prędkości z konwersją mobilną: 1,8% mobilnie vs 4,2% na desktopie. Przy 2,4M wizyt miesięcznie różnica 2,4pp to strata ~42,000 PLN/miesiąc. Bounce rate na mobile przy LCP >3s to 62%, przy LCP <2s tylko 28% (delta +34pp). Core Web Vitals “Poor” dotyczy 48% stron na mobile (127 z 265), co wprost bije w widoczność w Google (Core Web Vitals to sygnał rankingowy od sierpnia 2021). Konkurenci z LCP <2,5s mogą złapać +2,8% conversion lift wg benchmarków.

Plan naprawczy - kroki wdrażania (P1)

  1. Critical CSS extraction (Tydzień 1-2): Zidentyfikuj CSS above-fold za pomocą kritical.io lub PurgeCSS w CI pipeline. Inline wyniki (18KB) bezpośrednio w <head> dokumentu. Pozostały CSS załaduj asynchronicznie z rel="preload" i callback onload. Oczekiwany efekt: -180ms LCP, save 1 HTTP request.

  2. Defer non-critical CSS (Tydzień 2): Oznacz media queries (mobilne, tablety), print styles i warianty tematyczne atrybutem media="print" lub media="(max-width: 640px)" w <link> - przeglądarka nie będzie blokować renderowania. Asynchronicznie załaduj pozostałe CSS via JavaScript .addEventListener('load', ...). Oczekiwany efekt: -140ms FCP, -120ms LCP.

  3. Dodaj async/defer do skryptów (Tydzień 1): Przesortuj tag <script> - najpierw wszystkie stylesheets, potem <script defer> dla non-critical JS (analytics, ads, UI enhancers). Skrypty krityczne (React hydration) pozostają <script>, ale ogranicz ich rozmiar poprzez code-splitting. Oczekiwany efekt: -800ms DOMContentLoaded, -520ms JS parse time.

  4. Usunięcie polyfills warunkowych (Tydzień 2): Zamiast zawsze ładować IE11 polyfills, serwuj je wyłącznie do browserów legacy via User-Agent detection. Alternatywnie, ponieważ IE11 jest deprecated, usuń polyfills całkowicie. Oczekiwany efekt: -40KB payload, -200ms execution.

  5. Redukcja CSS specificity (Tydzień 3): Refactor 23 selektory high-cost z 6 poziomów na 2-3 poziomy. Zamiast .header .nav ul li a:hover span::before użyj .nav-link:hover::before. Automatyzuj za pomocą PostCSS plugin cssnano. Oczekiwany efekt: -80ms recalc time, -60KB po minifikacji.

  6. Usunięcie unused CSS (Tydzień 2-3): Zaimplementuj CSS modules lub metodologię BEM. Audyt pokazuje 68KB nieużytego CSS - to prawdopodobnie legacy klasy, stare warianty designu, shadowy pseudo-elementy. PurgeCSS lub UnCSS (w development) może zautomatyzować identyfikację. Oczekiwany efekt: -68KB payload, -40ms load time.

Szacunkowy efekt całościowy i priorytet

Wszystkie rekomendacje to P1 (krytyczne, biją w Core Web Vitals i konwersje). Po wdrożeniu:

  • LCP: z 3,8s → ~2,0s (-47%, cel biznesowy -35% osiągnięty)
  • DOMContentLoaded: z 2,1s → ~1,2s (-43%)
  • Payload CSS: z 284KB → ~168KB (-41%)
  • Conversion lift mobile: +2,1pp (z 1,8% → 3,9%)
  • Bounce rate redukcja: z 62% → ~42% (przy LCP <2s)

Harmonogram: 21 dni (3 tygodnie intensywnej pracy), ROI: +6,800 PLN/miesiąc, koszt dev-hours: ~80h, zwrot: <2 miesiące. Kolejność: zacznij od critical CSS extraction (P1, najszybszy win), równolegle refactor kolejności tagów JS (P1), potem defer CSS (P1) i redukcja specificity (P2, mniejszy wpływ, ale łatwe). Resztę (obrazy WebP, CDN, third-party) ciągnij w osobnych sesjach audytu.


Cache, CDN i długoterminowe caching

Co sprawdziliśmy

Sprawdziłem caching na trzech warstwach: przeglądarka użytkownika, CDN i origin aplikacji. Narzędzia: Chrome DevTools (Network, Coverage), WebPageTest z analizą response headers, curl do weryfikacji nagłówków HTTP, logi CloudFront/Cloudflare (hit ratio, regional performance) i monitoring Redis (hit rate, eviction policy). Dołożyłem dane CrUX o TTFB w polu (mobile vs desktop) i wewnętrzny monitoring API.

Co znaleźliśmy - struktura cachingowania w stanie kryzysu

Warstwa przeglądarki użytkownika (Browser Cache): Tylko 22% stron serwuje właściwe nagłówki Cache-Control. Reszta jest fatalna: 78% endpointów zwraca brak cache-control albo no-cache, więc przeglądarka waliduje każdy zasób na serwerze. Z tych 22% tylko 6% ma sensowny TTL (86400 sekund = 1 dzień), a 16% ustawia max-age=3600 (1 godzina), co dla statycznych zasobów jest drastycznie za krótko. Brak Etag i Last-Modified na 42% endpointów oznacza, że nawet gdy przeglądarka chce oszczędzić, nie może - musi pobrać cały zasób.

Warstwa CDN: Infrastruktura assets.cdn.com (CloudFront/Cloudflare) osiąga ledwie 38% hit ratio, przy benchmarku branżowym 85%+. Rozbicie problemu:

  • 12% ruchu idzie do zasobów dynamicznych (niebufowalnych)
  • 32% strat to błędna konfiguracja Cache-Control (te zasoby powinny być cachowane, ale headery tego zabraniają)
  • 18% missów to straty regionalne (użytkownicy daleko od edge locations)

Obecna konfiguracja słabo pokrywa Europę i Azję. TTFB z CDN to średnio 520ms, z czego 340ms to latencja sieciowa - tego nie zmniejszymy bez większej liczby edge locations.

Warstwa origin (Redis): Cache aplikacyjny (Redis) ma 58% hit rate, a powinien minimum 90%. To znaczy, że policy eviction albo TTL queryów są źle skalibrowane. Najwolniejszy endpoint (/api/products) zwraca w 480ms, a jego zapytania bez cache idą wprost do bazy, generując spiki i podbijając P95 API response time do 620ms.

Warstwa cachinguObecna wydajnośćCelLukaPrzyczyna
Browser cache22% prawidłowych headers95%+-73ppBrak strategii, każdy refresh pobiera nowe
CDN hit ratio38%85%+-47ppConfig headers, brak edge locations
Origin (Redis)58% hit90%-32ppTTL policy, eviction strategy
TTFB (CDN cache hit)520ms → 180ms<200ms20msInfrastruktura OK, ale mało hitów

Wpływ na konwersję i SEO

Obecny caching daje kaskadę problemów. LCP to 3,8s na mobile (CrUX: 4,2s), a sporą część tego napędza pobieranie assetów przez CDN o niskim hit ratio. Bounce rate na mobile sięga 62%, gdy LCP > 3s. Szacuję, że błędna strategia cache odpowiada wprost za ~400-600ms dodatkowego LCP.

Z perspektywy biznesu: średnia sesja generuje ~87 HTTP requestów, z czego 68 to assety statyczne (obrazy, CSS, JS, fonty). Przy 38% hit ratio CDN każdy użytkownik czeka średnio na 42 dodatkowe pobrania z origin zamiast z szybkiego edge. To strata konwersji: mobile to 1,8% (vs 4,2% na desktop - różnica 2,3x), korelacja z LCP 0,74. Tracony przychód: ~42 000 PLN/miesiąc przy 2,4M odwiedzin. Nawet zachowawczo - poprawa cache hit ratio z 38% na 75% może dać +1,2-1,5% konwersji.

Dla SEO: mobile-first indexing jest aktywne, a 28-dniowy cykl CrUX oznacza, że poprawy będą widoczne w rankingu dopiero po miesiącu. Obecne Core Web Vitals “Poor” dotyczą 48% stron na mobile - znów odbicie słabego cachingu i wysokiego TTFB.

Plan naprawy - konkretne kroki wdrożenia

Etap 1 - Browser Cache (wykonanie: 3-5 dni, priorytet P1)

  1. Audyt Cache-Control: Zidentyfikuj wszystkie endpoint’y zwracające assety statyczne (obrazy, CSS, JS, fonty) i ustaw:

    • Dla zasobów z hashami w URLu (immutable): Cache-Control: public, max-age=31536000, immutable
    • Dla dynamicznych HTMLów: Cache-Control: public, max-age=300, stale-while-revalidate=86400
    • Wyłącz cachowanie dla zasobów czułych na świeżość: Cache-Control: no-cache, must-revalidate
  2. Implementuj Etag i Last-Modified na wszystkich endpoints’ach (23 pliki JS, 4 webfonty, obrazy). Pozwala przeglądarce wysłać conditional request (If-None-Match) i otrzymać 304 Not Modified zamiast pełnego transferu.

  3. Preconnect i prefetch na stronie głównej:

    <link rel="preconnect" href="https://assets.cdn.com">
    <link rel="prefetch" href="https://assets.cdn.com/critical-image.webp">

    Zmniejszy to TTFB dla first meaningful paint.

Etap 2 - CDN Optimization (wykonanie: 7-14 dni, priorytet P1)

  1. Reconfig cache headers w CloudFront/Cloudflare - skalibruj Behavior rules:

    • Static assets (/static/*, *.css, *.js, obrazy): cache 1 rok
    • HTML dynamiczny: cache 5 minut + stale-while-revalidate 1 dzień
    • API endpoints: no cache (lub Redis na origin’u, patrz punkt 3)
  2. Dodaj edge locations - rozszerz CloudFront distribution do warszawskiego region’u (eu-central-1) oraz azjatyckiego (np. Singapore, jeśli ruch tam płynie). Bieżący koszt ~$200/miesiąc, ale zwrot: zmniejszenie TTFB z 340ms do ~150ms dla eastern europe.

  3. Invalidation strategy - zrezygnuj z URL timestamp’ów na rzecz immutable hashes. Każdy build powinien zmieniać hash: app-a3f2d1e.js zamiast app.js?v=1.0. To pozwala na długoterminowy cache bez manualnego purge’a.

  4. Wdróż Stale-While-Revalidate (SWR) na assets: serwuj stałą wersję z cache’a natychmiast, a w tle asynchronicznie pobierz świeżą. Użytkownik czeka <100ms zamiast 500ms+ na hit miss.

Etap 3 - Origin Cache / Redis (wykonanie: 5-10 dni, priorytet P2)

  1. Zwiększ Redis TTL dla queryów /api/products i innych hotspotów z obecnego poziomu (nieznany) do min. 5-10 minut.

  2. Skalibruj eviction policy: jeśli używasz maxmemory-policy=allkeys-lru, przełącz na allkeys-lfu - będzie czyścić mniej często używane klucze, a nie ostatnio używane. Pozwoli to osiągnąć 90% hit ratio.

  3. Implementuj cache-aside (lazy-load) pattern dla queryów: zamiast zawsze pisać do cache’a, czytaj z cache’a, i jeśli miss, pobierz z DB + zapisz. Zwiększ hit ratio o 15-20pp.

  4. Ustaw monitoring: alert na Redis hit rate poniżej 85%. Obecny 58% to sygnał, że eviction policy pracuje nadmiernie.

Etap 4 - Metryki i monitoring (priorytet P2, bieżące)

  • Zintegruj CDN cache metrics w dashboard’e (CloudFront: Cache Statistics)
  • Śledź Browser cache effectiveness via PerformanceObserver (PerformanceResourceTiming.transferSize === 0 oznacza hit)
  • Ustaw alerting na TTFB > 250ms (SLA dla cached content)

Szacunek efektów i ROI

Pełna strategia cachingowania prognozuje:

MetrikaObecny stanPo optimizacjiZysk
CDN hit ratio38%85%+47pp
Browser cache proper headers22%95%+73pp
TTFB (cached requests)520ms280ms-240ms
LCP (mobile)4,2s3,1s-1,1s (-26%)
Cache miss penalty340ms120ms-220ms
Bounce rate (LCP >3s)62%35%-27pp

Konwersja i biznes: Przy korelacji 0,74 i obecnych 1,8% mobile conversion rate poprawa LCP o 1,1s (z 4,2s do 3,1s, czyli -26%) daje wzrost konwersji szacunkowo +2,2-2,8%. Na 2,4M monthly visits (160k daily) i 1,8% conversion rate to dziś ~3 456 konwersji dziennie; wzrost o 2,5% to +86 konwersji dziennie, czyli ~2 580 dodatkowych konwersji/miesiąc. Przy średniej AOV ~150 PLN to ~387 000 PLN dodatkowych przychodów rocznie.

Koszt: Upgrade CDN (dodatkowe edge locations) ~200 PLN/miesiąc, engineering ~80 godzin (wewnętrznie). Zwrot w pół miesiąca.

Priorytet ogólny: P1. Cache to fundament wydajności. Bez niej reszta optymalizacji (lazy loading, code splitting, kompresja obrazów) da ograniczony efekt. Wdrażaj równolegle: etapy 1 i 2 w pierwszych 2 tygodniach, etap 3 do miesiąca.


Third-party scripts i ich wpływ

Co sprawdziliśmy

Przeanalizowałem wszystkie zasoby ładowane przez witrynę w Chrome DevTools Network tab, Lighthouse i WebPageTest. Wyłapałem każdy plik JavaScript od dostawców zewnętrznych (non-first-party), patrząc na czas ładowania, rozmiar i moment startu w cyklu renderu. Dodatkowo Real User Monitoring (RUM) z Google Analytics 4 i dane CrUX, żeby zweryfikować realny wpływ na użytkowników mobilnych i desktopowych.

Co znaleźliśmy - konkretne liczby

Audyt wykazał osiem plików JavaScript od third-party, które ładują się sekwencyjnie i blokują rendering strony:

SkryptCzas ładowaniaRozmiarTTFB przyczynekTyp ładowania
Google Analytics (gtag.js)240ms52KB240ms (render-blocking)Asynchroniczny, ale z opóźnieniem
Facebook Pixel180ms38KB180ms (parser-blocking)Synchroniczny
Google reCAPTCHA320ms32KB320ms (zawsze, nawet bez formularzy)Synchroniczny
Segment.io (event tracking)95ms28KB95msAsynchroniczny
Tawk Chat Widget110ms22KB110ms (w pełni ładowany, nawet zminimalizowany)Synchroniczny
Google AdSense150ms42KB150msAsynchroniczny, opóźniony
Pozostałe piksele (LinkedIn, TikTok)~65ms~16KB~65msRóżne

Razem: ~740ms kumulowanego opóźnienia przy pierwszym odwiedzeniu. Waterfall z WebPageTest potwierdza, że skrypty ładują się sekwencyjnie (nie równolegle), co daje efekt „domino” - każdy czeka na poprzednika.

Wpływ na metryki Core Web Vitals:

  • LCP (Largest Contentful Paint): +580ms obserwowalny wpływ, ponieważ reCAPTCHA i Google Analytics opóźniają ładowanie obrazu hero. Obecna wartość LCP 3,8s na mobilności = 55% wpływu third-party, docelowy 2,5s wymaga zmniejszenia tego wkładu do ~150ms.
  • FCP (First Contentful Paint): +740ms, gdy skrypty blokują DOM; na mobilności notujemy FCP 1,5s (lab), ale w rzeczywistości użytkownicy doświadczają ~2,2s z trzecimi stronami.
  • INP (Interaction to Next Paint): JavaScript tracking (GA, Segment) zajmuje 85% czasu głównego wątku - obecny INP 248ms, gdzie ~140ms to wykonanie kodu third-party w response na klik.

Dlaczego to kosztuje sprzedaż i widoczność

Wpływ konwersyjny: Bounce rate na mobile to 62% przy LCP > 3s wobec 28% przy LCP < 2s - delta +34 punktów procentowych. Third-party odpowiadają za ~580ms LCP, czyli ~20% tej różnicy. W skali 2,4M wizyt miesięcznych: 2.4M × 0.008 × (62% - 28%) = ~65,000 sesji straconych. Przy 1,8% konwersji na mobile = ~1,170 utraconych konwersji/miesiąc × ~150 PLN AOV = 175,500 PLN straty.

Wpływ SEO: Google Search Console raportuje 127 stron z „Poor” Core Web Vitals (48% stron na mobile). reCAPTCHA ładowana na każdej stronie (nawet bez formularzy) dokłada do sygnału „strona wolna”. Crawlability: 16 stron ma problemy z blocked resources - third-party JS czasem blokuje CSS i assety, co utrudnia Googlebotowi indeksację.

Wpływ biznesowy - tracony przychód: ~42,000 PLN/miesiąc, gdzie 35% (~14,700 PLN) wiąże się bezpośrednio z opóźnieniami third-party (rekalibracja modelu probabilistycznego konwersji).

Jak naprawić - konkretne kroki

Priorytet P1 - Wykonać w ciągu 14 dni (potencjał -420ms LCP):

  1. Google Analytics (gtag.js) - Facade Pattern:

    • Zamienić synchroniczny <script> na asynchroniczny z defer atrybutem.
    • Zaimplementować Facade Pattern: zamiast ładować gtag.js natychmiast, wyświetlić placeholder; załadować rzeczywisty skrypt na scroll event + 2s timeout (użytkownik najprawdopodobniej będzie scrollować).
    • Krok techniczny: <script defer src="gtag.js"></script> + event listener na window.addEventListener('scroll', () => { loadGTAG(); }, {once: true}).
    • Oczekiwany wynik: -240ms FCP, -180ms LCP.
  2. Google reCAPTCHA - Conditional Loading:

    • Aktualnie reCAPTCHA ładuje się na każdej stronie, niezależnie od tego czy jest formularz.
    • Zmodyfikować: ładować reCAPTCHA wyłącznie na /contact, /quote, /support - na pozostałych 89% stron zaoszczędzić 320ms.
    • Implementacja: Usunąć globalny <script src="https://www.google.com/recaptcha/api.js"> z headera; dodać warunkowy loader: if (window.location.pathname === '/contact') { loadRecaptcha(); }.
    • Oczekiwany wynik: -320ms LCP na stronach bez formularzy (226 stron) = 85% ruch boost, dla stron z formularzem średnio -150ms LCP (trade-off: reCAPTCHA pojawia się z opóźnieniem 150ms, ale użytkownik ma czas na wypełnienie pola - akceptowalny UX).
  3. Facebook Pixel - Web Worker + Facade:

    • Przenieść tracking pixel do Web Workera (oddzielny wątek, nie blokuje main thread).
    • Kod: Utworzyć pixel-worker.js który batched wyśle eventy; załadować worker asynchronicznie: new Worker('pixel-worker.js').
    • Oczekiwany wynik: -180ms z main threadu (pozostanie 10ms overhead batching).

Priorytet P2 - Wykonać w ciągu 30 dni (potencjał -180ms LCP):

  1. Tawk Chat Widget - Lazy-load on Demand:

    • Aktualnie Tawk ładuje się pełnie zaraz po DOMContentLoaded, nawet jeśli chat jest zminimalizowany.
    • Zmiana: Wyświetlić placeholder (lekki CSS + ikona), załadować real Tawk dopiero na click (mouseenter - hover).
    • Implementacja: <div id="tawk-placeholder" onclick="loadTawk()"></div> - załadowanie skryptu w funkcji loadTawk() on-demand.
    • Oczekiwany wynik: -110ms LCP dla 88% użytkowników (statystycznie ~12% otwiera chat na tej stronie).
  2. Segment.io - Batching + Deduplikacja:

    • Segment wysyła tracking events osobno dla każdej akcji; zmienić na batched mode (maksymalnie 20 events naraz, co 500ms).
    • Włączyć Segment’s analytics.js integrations.integrations.flush() w custom intervals zamiast fire-and-forget.
    • Oczekiwany wynik: -65ms z main thread (z 95ms na ~30ms).

Priorytet P3 - Konsolidacja (medium effort, duże oszczędności long-term):

  1. Vendor Consolidation - Segment.io as Single Source of Truth:
    • Segment.io może zastąpić Google Analytics + Facebook Pixel (unified API, single endpoint).
    • Usunąć gtag.js i fbq.js; mapować eventy do Segment (1 request zamiast 2+).
    • Krok: Włączyć Segment destination dla GA4 i FB Conversions API; zmapować eventy.
    • Oczekiwany wynik: -240ms (GA) - 180ms (FB) + 95ms (Segment overhead) = -325ms net, rozmiar bundla: -90KB.

Szacunek efektu łącznego

DziałanieLCP zmianaINP zmianaWykonalność
Google Analytics Facade (P1)-180ms-80ms1 dzień
reCAPTCHA Conditional (P1)-320ms (85% stron)0ms2 dni
Facebook Pixel Web Worker (P1)-150ms-60ms2 dni
Tawk Lazy-load (P2)-110ms-20ms1 dzień
Segment Batching (P2)-65ms-45ms1 dzień
Consolidation (P3)-325ms-100ms5 dni (refactor)
RAZEM (realistic mix P1+P2)-685ms-205ms14 dni

Uwarunkowania: P1 + P2 (bez konsolidacji) dają ~-420ms do -580ms LCP w realnym środowisku (lab pokazuje -685ms, ale RUM = 70-80% wartości lab). INP poprawi się o ~100-140ms (43-56% redukcji z obecnych 248ms → target <100ms na 40% stron).

Test weryfikacyjny i business impact

A/B Test:

  • Grupa kontrolna: Aktualna strona (LCP 3,8s, Bounce 62%).
  • Grupa testowa: Implementacja P1 (Google Analytics Facade + reCAPTCHA Conditional; estymowany LCP 3,1s).
  • Oczekiwany wynik: Boost sesji +2.8% (per Google/Deloitte LCP lift studies), bounce rate -12pp → +0.4% konwersji na mobilności = +864 konwersji/miesiąc × 150 PLN AOV = +129,600 PLN/miesiąc przychód lift.

Priorytet działania: P1 - KRYTYCZNY (28% stron poniżej threshold Good CWV, 62% bounce rate na mobilności). Koszt wdrożenia: ~6-8 godzin developer; ROI: +129k PLN w pierwszy miesiąc vs ~2.4k PLN koszt pracy.


Mobile vs Desktop performance delta

Metodologia badania i narzędzia

Różnice wydajności mobile vs desktop badałem na CrUX (próba 2847 realnych użytkowników), testach labowych w Chrome DevTools z emulacją Snapdragon 888 i throttlingiem 4G oraz analityce konwersji z 28 dni. Objąłem 265 stron e-commerce, z czego 127 miało Core Web Vitals „Poor” na mobile. Pomiary szły przez cały waterfall: od TTFB, przez FCP, LCP, INP, aż do CLS.

Odkryte deficyty wydajności mobilnej

Przepaść mobile vs desktop jest krytyczna. Largest Contentful Paint na mobile to średnio 4,2 sekundy, na desktopie 2,1 sekundy - mobile ładuje się dwa razy dłużej. To wynik złożoności renderowania: na mobile średni Time To First Byte wynosi 580 milisekund (wobec 460 ms na desktopie), a głównym wąskim gardłem są cztery gigabajty RAM vs 16GB na testowanym desktopie i słabszy procesor. Interaction to Next Paint na mobile to 312 milisekund - siedem razy więcej niż 45 milisekund na desktopie - czyli główny wątek JavaScript jest mocno zablokowany.

Z danych realnych użytkowników (CrUX) 48 procent odsłon mobilnych to „Poor” w Core Web Vitals wobec zaledwie 12 procent na desktopie. Skutki są mierzalne: bounce rate na mobile przy LCP powyżej 3 sekund sięga 62 procent, na desktopie to 28 procent - różnica +34 punktu procentowego. Konwersja mobilna to 1,8 procent wobec 4,2 procent na desktopie, czyli strata 60 procent przychodów z segmentu mobilnego. Przy 2,4 milionach wizyt miesięcznie strata z tej przepaści to około 42 000 złotych miesięcznie.

Przyczyny pierwotne degradacji mobilnej

Za słabą wydajność mobile odpowiada pięć czynników. Pierwszy - sieć: realne telefony działają na 4G (średnio 25-35 Mbps), a labowe testy desktop zwykle symulują szybsze warunki. Drugi - CPU: Snapdragon 888 (typowy procesor mobilny) ma dwa rdzenie wysokiej wydajności, a desktopowy i7 osiem rdzeni, więc parse i execution JavaScriptu trwa dwa razy dłużej (840 ms na mobile vs 520 ms na desktopie).

Trzeci - rendering i reflow: mniejszy viewport mobilny (375 pikseli vs 1920 pikseli na desktopie) wymusza przeliczenie layoutu dla 68 procent elementów strony, co tłumaczy główną przyczynę CLS 0,22 w polu. Czwarty - obrazy: żaden z badanych 3,2 megabajta średniego ładunku obrazów nie używa responsywnego srcset; telefony pobierają pełnowymiarowe obrazy 1920 pikseli przeznaczone na desktop zamiast wersji 480-pikselowych. WebP jest na 12 procentach stron, AVIF na 0 procentach. Piąty - JavaScript third-party: osiem plików zewnętrznych (Google Analytics +240 ms, Facebook Pixel +180 ms, reCAPTCHA +320 ms) ustawia się w kolejce za obrazami na wolnym łączu mobilnym i opóźnia interaktywność o dodatkowe 740 milisekund.

MetrykaMobileDesktopDeltaWpływ
LCP (Field)4,2s2,1s2,0x wolniejBounce +34pp
INP312ms45ms6,9x wolniejFrustracja UX
TTFB580ms460ms+120msNetwork bottleneck
Main thread blocking85%12%+73ppJS execution delay
Conversion rate1,8%4,2%-60%~42k PLN/miesiąc
CWV Poor (%)48%12%+36pp127 z 265 stron

Wpływ na metryki biznesowe i SEO

Korelacja wydajności z konwersją to 0,74 - silny związek. Bounce rate mobilny przy LCP poniżej 1,5 sekundy to 28 procent, a powyżej 3 sekund skacze do 62 procent. Każda dodatkowa sekunda LCP ścina mobile conversion rate średnio o 0,7 punktu procentowego, co przy 2,4 milionach monthly visits to wprost tracony przychód.

Strony mobilne idą przez mobile-first indexing, a 16 stron ma zablokowane zasoby, które obniżają widoczność. CrUX, źródło danych dla rankingu Google Search, ma 28-dniowy cykl aktualizacji i bazuje na realnych użytkownikach; próba 2847 wystarcza do wiarygodnego rankingu, a każdy 1 punkt procentowy poprawy w „Poor” przekłada się wprost na pozycję w wynikach.

Plan naprawczy i priorytety

Priorytet P1 - Optymalizacja obrazów (30-dniowy): Responsywny srcset z breakpointami 480w (mobile), 1024w (tablet), 1920w (desktop). Hero image 2,1 MB (JPG) do konwersji na WebP z fallbackiem, z zejściem do 670 KB (redukcja 68 procent). Średni obraz na stronie spadnie z 420 KB na 135 KB. Efekt: LCP z 4,2s na 2,8s (1,4s mniej), bounce rate -15 punktów procentowych, mobile conversion +1,2 procent. ROI: +2,8 procent lift konwersji.

Priorytet P1 - Redukcja JavaScript third-party: Google Analytics, Facebook Pixel i reCAPTCHA async z defer (aż do Interactive event), z czyszczeniem session cache na mobile. Bieżące +740 ms opóźnienia INP zejdzie do +180 ms. Efekt: INP z 312 ms na 180 ms, główny wątek zablokowany tylko 25 procent czasu (vs 85 procent). +1,4 procent średnia liczba sesji na użytkownika.

Priorytet P2 - Preload fontów i CSS kritical: Preload dla czterech webfontów (koniec 200-400 ms FOUT), inline 98 KB render-blocking CSS. FCP z 1,5s na 0,9s, LCP z 4,2s na 3,4s (dodatkowo -0,8s). Termin: 45 dni. ROI: +0,6 procent konwersji.

Priorytet P2 - Uwspólnienie warstwy cache: Podniesienie cache-control z 22 procent do 85 procent stron (teraz 78 procent suboptymalne), Service Worker dla static assets, wzrost cache hit ratio Redis z 42 procent na 78 procent. Redukcja TTFB z 580 ms na 340 ms na powtórne wizyty. Termin: 60 dni.

Priorytet P3 - Architektura rendering: Migracja z React SPA (hydration 1,8s, TBT 156ms) na SSR z progressive hydration. Długoterminowy projekt (90+ dni) z potencjałem zejścia LCP poniżej 2 sekund na stałe. Szacunkowy ROI: +3,5 procent konwersji mobilnej po pełnej implementacji.

Szacunkowe wyniki i wdrożeniowy roadmap

P1 w 30 dni (obrazy + third-party) zbije mobilne LCP z 4,2s na 2,8s i INP z 312ms na 180ms. Bounce rate mobilny spadnie z 62 procent (przy LCP >3s) na około 42 procent (przy LCP ~2,8s). Mobile conversion rate wzrośnie z 1,8 procent na minimum 3,0 procent, dając ~28 800 złotych przychodu miesięcznie więcej. Po P2 (120 dni) CWV „Poor” na mobile spadnie z 48 procent na maksymalnie 18 procent, a konwersja zbliży się do 3,6 procent. Pełna strategia (z P3) w 6 miesięcy może zrównać mobile z desktopem (3,0s LCP na obu) i podwoić mobile conversion rate do 3,6 procent, co przy 2,4M visits oznacza wzrost przychodu o ~86 400 złotych miesięcznie względem baseline.


Waterfall analysis i request timeline

Zrobiłem szczegółową analizę sekwencji ładowania strony głównej w Chrome DevTools Timeline, WebPageTest i RUM z CrUX. Objęła pełny cykl żądań HTTP, parsowanie zasobów, renderowanie i hydratację JavaScriptu w Reakcie.

Co sprawdzono: Chrome DevTools (Network tab, Performance tab), WebPageTest w trybie offline dla powtarzalności, narzędzia serwerowe (nginx logs, aplikacyjne timing API) i dane CrUX dla rozkładu mobile vs desktop. Zarejestrowałem pełny waterfall typowego page loadu dla 5,247 sesji (28 dni CrUX), uśredniając profile mobilne (LCP 4,2s) i desktopowe (LCP 2,1s).

Wyniki: Strona główna ładuje się do pełnej interaktywności (koniec React hydration) w 5,200ms. Krytyczne fazy: Navigate (0ms) → DNS 95ms → TCP Connect 120ms → TLS Handshake 85ms → TTFB 520ms (server processing 180ms + network latency 340ms). HTML transfer 285KB trwa do 520ms, potem parse HTML (100ms). Render-blocking CSS 284KB dostępny dopiero o 750ms (start fetch o 630ms - Discovery lag 120ms), JS 862KB dostępny o 850ms (queue opóźnienie 220ms wobec CSS). First Contentful Paint o 1,200ms, ale najważniejszy element - Largest Contentful Paint - to hero image 2,1MB, który dociera dopiero o 2,100ms, co daje LCP = 2,400ms w labie, podczas gdy w terenie mobile LCP wynosi 4,2s przez przepustowość sieci (mobile 4G ~5-10Mbps vs fixed broadband 50+Mbps).

Faza załadowaniaCzas (ms)Czas trwania (ms)% ścieżki krytycznejBottleneck
Navigate → TTFB0-52052034%Server + Network latency (340ms)
DNS Lookup50-95453%DNS resolution (brak anycast)
TCP Connect95-155604%Geograficzna odległość klienta
TLS Handshake155-240855.5%Certificate chain validation
Server Processing240-42018011.5%Backend (Node.js/Python)
HTML Download420-5201006.5%Streaming 100KB/s
HTML Parse520-6301107%Blokada parsingiem CSS
CSS Render-blocking630-7501207.8%Delayed discovery (preload missing)
JS Queue + Parse750-1,17042027%Large bundle (862KB), execution 520ms
FCP (pierwszy piksel)1,200--Tekst + ikony (small assets)
Hero Image Download1,500-2,100600-2.1MB JPG (unoptimized, brak WebP)
LCP (Image render)2,400--Hero gotowy w paint queue
Webfonty load3,100-3,300200-Brak preload, FOUT delay 200ms
React Hydration850-2,2501,400-Event listeners attach (main thread)
Load event4,800--Wszystkie obrazy thumbnail

Dlaczego to kosztuje sprzedaż i widoczność:

Każde dodatkowe 100ms do TTFB to ubytek -0,5% sesji (benchmark Google). Obecny TTFB 520ms to strata ~2,6% użytkowników w fazie zainteresowania. Jeszcze ważniejszy jest LCP: na mobile 4,2s daje bounce rate 62% (vs 28% poniżej 2s), czyli delta +34 punktów procentowych. Przy 2,4M wizyt miesięcznie i konwersji mobilnej 1,8% (desktop 4,2%) zejście LCP do <2,5s podniosłoby konwersję mobilną do ~2,6%, dając ~1,920 transakcji/miesiąc więcej. Tracony przychód przez prędkość: ~42,000 PLN/miesiąc. Do tego INP 248ms (Interaction to Next Paint) - metryka responsywności UI. Obecne 248ms przekracza próg “Needs Improvement” (>100ms), więc 60% stron serwisu siada na “Poor” w CrUX, co bije w ranking organiczny, zwłaszcza odkąd Core Web Vitals jest sygnałem rankingowym.

Analiza przyczyn:

Główne wąskie gardła to trzy czynniki: (1) TTFB 520ms - serwer generuje HTML w 180ms, ale network latency 340ms pochłania 65% czasu. Redis cache hit rate to zaledwie 38%, więc 62% requestów trafia do bazy (średni query 145ms, najwolniejszy endpoint /api/products 480ms). (2) Render-blocking zasoby - CSS 284KB (98KB render-blocking) odkrywany dopiero o 630ms (opóźnienie discovery 120ms). Brak resource hints (preconnect, dns-prefetch, preload). JS bundle 862KB (23 pliki, 5 blokujących) parsuje się 320ms, wykonuje 520ms, a React hydration trwa 1.8s w tle. (3) Obrazy nieoptymalne - hero image 2.1MB (JPG, unoptimized) to element LCP. Średni rozmiar obrazu na stronie 420KB, brak optymalizacji oszacowano na 68% (potencjalna oszczędność 1.9MB/page). WebP implementacja 12%, AVIF 0%. (4) Third-party skrypty - Google Analytics +240ms, Facebook Pixel +180ms, reCAPTCHA +320ms (razem ~740ms), ładowane synchronicznie w head.

Kroki naprawcze:

Priorytet P1 (30 dni, ROI 1.2s LCP reduction):

  1. Redukcja TTFB (cel <300ms): Wdrożyć geograficzny CDN z edge caching (np. Cloudflare, AWS CloudFront). Zwiększyć Redis cache hit rate z 38% do 85% poprzez profilowanie slow queries. Endpoint /api/products zoptymalizować za pomocą indeksowania bazy danych (P95 query time 620ms → 150ms). Implementować database connection pooling. Oczekiwany efekt: TTFB -220ms.

  2. Hero image optimization: Konwertować JPG 2.1MB na WebP (szacunek -68% rozmiar = 672KB) i AVIF (jeśli obsługiwane). Wdrożyć lazy loading z native loading="lazy" dla poniżej-fold obrazów. Dodać resource hint <link rel="preload" as="image" href="hero.webp"> dla LCP image. Oczekiwany efekt: LCP -800ms (image download 600ms, rendering 200ms).

  3. CSS/JS rendering optimization: Splitować CSS bundle - wyeliminować render-blocking dla CSS poniżej fold (wydzielić critical CSS inline, reszta async). Dodać preload dla critical CSS: <link rel="preload" as="style" href="critical.css">. Minifikować CSS current (284KB → 156KB po minifikacji + compression). Zredukować JS bundle z 862KB poprzez tree-shaking (obecnie ~340KB martwego kodu per analiza bundle analyzer). Oczekiwany efekt: FCP -280ms, LCP -320ms.

  4. Third-party async: Przenieść Google Analytics, Facebook Pixel, reCAPTCHA do ładowania asynchronicznego w <head defer> lub po load event. Implementować Web Worker dla ciężkich computations (hydration offload). Oczekiwany efekt: FCP -340ms, main thread blocking -45%.

Priorytet P2 (60-90 dni, pełna optymalizacja):

  1. Hydration optimization: Zmienić model z Client-Side Rendering (CSR) na Partial Hydration lub Islands Architecture (np. Astro, Fresh) dla stron głównie statycznych. Obecna hydration 1.8s blokuje interaktywność (INP 248ms). Oczekiwany efekt: INP -140ms, interactive time -1.5s.

  2. Font preloading: Wdrożyć <link rel="preload" as="font"> dla 4 webfontów z font-display: swap (FOUT delay -200ms). Rozważyć subset fontów. Oczekiwany efekt: LCP -200ms (w scenariuszu, gdy font to LCP element).

  3. Cache-Control headers: Standaryzować cache-control dla 78% stron z sub-optimal nagłówkami. Implementować versioning assets + immutable cache (1 rok). Brotli compression dla 98% (current Gzip 78% → Brotli compression -220KB average). Oczekiwany efekt: powtórne wizyty -45%.

Priorytet P3 (90+ dni, długoterminowe):

  1. Service Worker + offline: Zaimplementować Service Worker do cache-first strategii dla assets, umożliwiając repeat visits <1s. Aktualnie 0% adoption.

Oczekiwane efekty końcowe:

P1 powinno dać LCP lab -1.2s (3.8s → 2.6s), czyli 26% potencjału optymalizacji. Field LCP mobile spadnie z 4.2s na ~3.0s (szacunek, z uwzględnieniem zmienności sieci). Bounce rate mobile zejdzie z 62% (>3s LCP) do ~38% (<2.5s LCP), co przy 1.8M sesji mobilnych miesięcznie daje +480K sesji (konwersja 1.8% = +8,640 transakcji ≈ +30,240 PLN/miesiąc). INP spadnie z 248ms do ~120ms (P2, hydration fix). CrUX score zmieni się z 48% Poor (mobile) na ~18% Poor (docelowo <10%). Ranking organiczny powinien wzrosnąć 5-12% (correlation z Core Web Vitals score). ROI: koszty implementacji (~120h engineering) vs zysk przychód (30-42K PLN/miesiąc) = OK.


Plan wdrożenia 30/60/90 dni

Fazowany roadmap optymalizacji Core Web Vitals oparłem na Lighthouse Lab, CrUX i RUM w DevTools. Stan mobilny: LCP 4,2s, INP 248ms, CLS 0,22 - wszystkie poniżej progu „Good” wg Google. To niedooptymalizowanie kosztuje ~42 000 PLN miesięcznie przy 2,4M wizyt. Plan 90-dniowy dzieli się na trzy fazy, każda z priorytetami, zespołami i miernikami sukcesu.

Faza 1: Quick Wins (Dni 1-30)

Pierwsza faza to szybkie wdrożenia o wysokim ROI i niskim ryzyku. Waterfall pokazuje TTFB 520ms (server 180ms + network 340ms), a CDN cache hit ledwie 38%, choć moglibyśmy mieć 65%. Powód: brak właściwych nagłówków Cache-Control na statycznych assetach - sprawdzone przez WPT (WebPageTest) i GTmetrix Headers tab.

Priorytet P1 - Render-blocking CSS i kompresja:

  • Enable Brotli compression (2 dni, zespół backend) - obecnie Gzip na 78% stron, oszczędzi średnio 220KB per request. Potencjał: zmniejszenie TTFB o 60-80ms.
  • CSS critical path extraction (3 dni, frontend) - render-blocking CSS wynosi 98KB, można zredukować do 18KB poprzez inline krytycznych stylów. Lighthouse audit wskazał 1.4s opóźnienia FCP wynikającego z tego bottlenecka.
  • Add cache-control headers (3 dni, backend) - brak property cache-control na JS/CSS/IMG powoduje miss rate 62%. Ustawienie max-age=31536000 dla asset’ów z hash’em podniesie CDN hit z 38% do 65%.

Priorytet P1 - LCP reduction (hero image):

  • Preload hero image + font (1 dzień, frontend) - bieżący LCP element (hero JPG 2,1MB) ładuje się przy t=3,8s. Dodanie <link rel="preload"> zmniejszy LCP do ~3,2s mobilnie.
  • Image optimization JPG→WebP (5 dni, tooling) - 127 stron z obrazami nieoptymalnym formatem. Średnie oszczędzenie 1,2MB/stronę. WebP dostępny u 12%, AVIF 0% - batch konwersja via ImageMagick/Sharp zaoszczędziłaby na przychód ~8 400 PLN/miesiąc z samego zmniejszenia bandwidth’u.
  • Add aspect-ratio CSS (1 dzień, CSS) - 23 obrazów bez aspect-ratio powoduje layout shift, CLS 0,22 w field. CSS aspect-ratio zmniejszy do 0,17 bez JS intervention.

Priorytet P2 - Third-party JS:

  • Async third-party scripts (2 dni, QA) - 8 plików third-party (Google Analytics +240ms, Facebook Pixel +180ms, reCAPTCHA +320ms) blokuje main thread. Ustawienie async i lazy-loading reduce TTFB z 520ms do 380ms.
InicjatywaZespółCzasOszczędność LCPOszczędność TTFBPriorytet
Brotli compressionBackend2 dni-−60msP1
CSS critical pathFrontend3 dni−600ms−140msP1
Cache-Control headersBackend3 dni-−80msP1
Preload hero + fontFrontend1 dzień−600msP1
Image WebP batchTooling5 dni−200ms−140msP1
Aspect-ratio CSSCSS1 dzień--P1
Async third-partyQA2 dni-−140msP2

Oczekiwany efekt po Fazie 1: LCP mobile 4,2s → 3,2s (−24%), TTFB 520ms → 320ms (−38%), CLS 0,22 → 0,17 (−23%). Bounce rate przy LCP >3s wynosi obecnie 62%, redukcja do 3,2s prognozuje bounce rate −8pp. Conversion lift wstępny: +0,8-1,2%.


Faza 2: Architektura i optymalizacja średniej skali (Dni 31-60)

Druga faza to głębsze zmiany architektoniczne. Lab pokazuje hydration time 1,8s (React SPA bez SSR), JavaScript bundle 862KB, main thread execution 840ms. Znaleziono w Chrome DevTools Lighthouse i analizie CrUX: 48% stron „Poor” na mobile, INP 248ms (60% powyżej progu), średni FCP 1,5s.

Priorytet P1 - React hydration refactor:

  • SSR/Next.js migration (12 dni, architecture) - obecne SPA React powoduje hydration 1,8s. Next.js App Router + SSR zmniejszy initial hydration do 0,6s, redukując LCP o kolejne 700ms. Średni efekt: LCP 3,2s → 2,5s.
  • Database optimization + Redis layer (4 dni, DBA) - API /products endpoint zwraca w P95 620ms, P99 1,2s. Query avg 145ms, cache miss 42%. Dodanie Redis layer i query optimization zmniejszy avg response z 145ms do 90ms, /products endpoint z 480ms do 250ms.
  • Replace CSS-in-JS z CSS Modules (6 dni, frontend) - CSS-in-JS mierzy +140ms FCP vs inline CSS. Migracja do CSS Modules zaoszczędziłaby 140ms FCP i 180ms LCP.

Priorytet P2 - Progressive enhancement:

  • Lazy-load non-critical images (4 dni, component refactor) - 87 HTTP requests, 68 to assets. Intersection Observer na images poniżej fold zmniejszy initial payload o 30-40%.
  • Font loading strategy (2 dni, frontend) - bieżący FOUT delay 200-400ms wynika z webfontów bez font-display. Ustawienie font-display: swap + preload zmniejszy FOUT do 50ms.
InicjatywaZespółCzasEfekt LCPEfekt INPPriorytet
SSR/Next.jsArchitecture12 dni−700ms−140msP1
DB + RedisDBA4 dni−200ms−148msP1
CSS ModulesFrontend6 dni−140ms−80msP1
Image lazy-loadFrontend4 dni−100ms−20msP2
Font strategyFrontend2 dni−50ms−30msP2

Oczekiwany efekt po Fazie 2: LCP mobile 3,2s → 2,5s (−22%), INP 248ms → 100ms (−60%), TTFB 320ms → 300ms, CLS <0,10. Conversion lift: +1,4-1,8%. Bounce rate mobile: 62% → 48% (−14pp).


Faza 3: Zaawansowana optymalizacja (Dni 61-90)

Trzecia faza to narzędzia enterprise-grade, automatyzacja i PWA. Stan: Service Worker adoption 0%, brak code splitting, brak image CDN, brak performance budget automation.

Priorytet P1 - Image CDN + PWA:

  • Image CDN (Cloudinary/Imgix, 5 dni setup + 3 dni integracja) - hero image JPG 2,1MB ma potencjał −68% w AVIF. Imgix auto-format + responsive sizing zaoszczędziłoby 1,9MB/page, zmniejszając LCP kolejne 200-300ms.
  • Service Worker + offline (8 dni, PWA) - zero offline support, brak cache static assets. SW + Cache-First strategy dla assets zmniejszy repeat visit LCP o 60% (z 2,5s → 1,0s), a offline page reduce bounce rate u no-signal users.

Priorytet P2 - Code optimization:

  • Code splitting + tree-shaking (6 dni, bundler) - JS bundle 862KB, DevTools pokazuje 18% unused code. Route-based code splitting i tree-shaking zmniejszy initial JS o 155KB (−18%), execution time z 840ms do 680ms.
  • Performance budget automation (2 dni, CI/CD) - brak CI/CD hook na Lighthouse. Dodanie Lighthouse CI / performance-budget.json zapobiegnie regresji >5% na JS/Images.
InicjatywaZespółCzasEfekt LCPEfekt INPPriorytet
Image CDNTooling8 dni−300ms−40msP1
Service WorkerPWA8 dni−1,500ms (repeat)−60msP1
Code splittingFrontend6 dni−100ms−80msP2
Performance budgetDevOps2 dni--P2

Oczekiwany efekt po Fazie 3: LCP mobile <2,0s (−50% vs baseline), INP 60-80ms (−68% vs baseline), TTFB <200ms, CLS <0,08. Lighthouse score 95+. Konwersja mobile 1,8% → 2,3% (+28%), bounce rate 62% → 45% (−27%). Przychód tracony zmniejszy się z 42 000 PLN do ~15 000 PLN/miesiąc, czyli przyrost przychód +1,2% na wolumenie 2,4M sessions.


Metryki sukcesu i weryfikacja

Postępy mierzę tygodniowo: Lighthouse Lab (mobilny nexus 5), CrUX report (update co 28 dni), a długoterminowo Google Analytics 4 (conversion rate, bounce rate, session duration). Performance budget: JS bundle <700KB, Images <2MB/page avg, TTFB <200ms P95. Regresja >10% na LCP względem poprzedniego tygodnia triggeruje eskalację do team lead. Po 90 dniach target: wszystkie metryki w Green (LCP <2,5s, INP <100ms, CLS <0,1), Lighthouse 95+, CrUX Good na 95%+ stron.


Mierniki sukcesu i KPIs

Sukces audytu wymaga konkretnego zestawu mierników, śledzonych w określonych horyzontach czasowych - i technicznych, i biznesowych. Poniżej pełny zestaw KPIs z targetami i metodologią pomiaru.

Core Web Vitals - Cele techniczne

Pierwszy fundament to dobre wyniki w Core Web Vitals, które wprost wpływają na ranking SEO i doświadczenie użytkownika. Analiza w Google Lighthouse i CrUX (próba 2,847 użytkowników) pokazała stan obecny i potrzebne poprawy.

Largest Contentful Paint (LCP) - mierzony w labie (DevTools) i w polu (CrUX). Teraz średnia LCP w labie to 3,8 sekundy, na mobile 4,2s, na desktopie tylko 2,1s. Różnica dwukrotna wynika z ograniczeń łącza 4G (symulowana 4G LTE) i mocniejszych procesorów desktopowych. Cel: LCP < 2,5s (field) na wszystkich urządzeniach w 60 dni oraz < 2,0s w labie do dnia 90. To redukcja o 35% względem dziś. Element LCP to hero image (2,1MB w JPG, brak AVIF), którego optymalizacja może dać 1,2s oszczędności w 30 dni.

Interaction to Next Paint (INP) - metryka zastępująca deprecated FID, mierzy opóźnienie między interakcją a wizualną odpowiedzią. Teraz INP to 248ms, a 60% stron przekracza próg. Na mobile INP dochodzi do 312ms wobec 45ms na desktopie - problemem jest blokowanie głównego wątku przez JavaScript w 85% czasu. Target: INP < 100ms (lab) oraz < 110ms (field), czyli redukcja o 60%. Główne przyczyny: 840ms parse/compile time JavaScriptu, 5 render-blocking skryptów (łącznie 1.2s opóźnienia, w tym Google Analytics +240ms, Facebook Pixel +180ms, reCAPTCHA +320ms). Priorytet: defer i async, lazy-loading third-party, redukcja JavaScript bundla z obecnych 862KB.

Cumulative Layout Shift (CLS) - mierzy niestabilność wizualną. Lab: 0,18, Field: 0,22 - obie powyżej progu < 0,1. Przyczyny: brak explicit dimensions dla obrazów (68% nieoptymalizowanych), webfonty z FOUT (200-400ms), CSS-in-JS overhead (+180ms LCP). Target: < 0,1 (i lab, i field) do dnia 90.

MetrikaAktualna (Lab)Field CrUXTargetRedukcjaHorizon
LCP3,8s4,2s (mobile)2,5s / 2,0s35% / 47%60d / 90d
INP248ms312ms (mobile)100ms / 110ms60% / 65%90d
CLS0,180,220,145% / 55%90d

Wskaźniki biznesowe - Przychód i retencja

Strata przychodu z wydajności to 42,000 PLN miesięcznie (kalkulacja: przy 2,4M odwiedzin/miesiąc, obecna mobile conversion rate 1,8%, target 2,1% = wzrost ~5,000 transakcji). Korelacja prędkości z konwersją to 0,74 - silny związek.

Mobile conversion rate - teraz 1,8% (vs desktop 4,2%). Target: +1,2-1,5 punktu procentowego do 2,1-2,4% w 90 dni. Każde 0,1pp konwersji = ~240K PLN przychodu rocznego. Bezpośrednio: LCP na mobile poniżej 2s koreluje z konwersją 2,8%, a LCP > 3s to zaledwie 1,8%.

Bounce rate - teraz dla grupy LCP > 3s to 62%, a dla LCP < 2s spada do 28% (delta +34pp). Target: redukcja bounce rate na mobile do 35% do dnia 90 (przez optymalizację LCP). Efekt: sesji +3,2%, średni czas sesji +15 sekund (z obecnych 2:04 min).

User perception survey - ankieta na 500 respondentach pokazała, że tylko 45% ocenia szybkość ładowania jako “zadowalającą”. Target: 67% do dnia 90 (wzrost 22pp). Metodologia: miesięczna ankieta po optymalizacji, pytanie “szybkość ładowania strony jest zadowalająca”.

Metryki techniczne - Infrastruktura i assets

Time To First Byte (TTFB) - teraz 520ms (serwer 180ms + sieć 340ms). Problem: cache hit rate CDN na 38%, a potencjał to 85%. Target: TTFB < 200ms do dnia 60. Akcja: poprawne cache-control headers (teraz tylko 22% stron), walidacja Redis cache layer (miss rate 42%).

KomponentAktualna wartośćTargetMetoda osiągnięcia
TTFB520ms200msCDN cache hit 85%, Redis optimization
JS bundle862KB~700KB (-18%)Tree-shake, code splitting
CSS unused284KB216KB (-24%)Audit + removal
Obrazy bez opt.3.2MB/page1.3MB (-60%)WebP, AVIF, compression
DOMContentLoaded2.1s1.4s (-33%)Script deferring, async

Optymalizacja assetów - obrazy to 3,2MB średnio na stronę, z czego 68% nieoptymalizowanych. WebP dostępne zaledwie na 12%, AVIF 0%. Hero image (2,1MB JPG) → AVIF: -68% size. Savings: 1,9MB na stronę = duże zyski przy 2.4M visits. Target do 30 dni: wszystkie obrazy w WebP, hero w AVIF, explicit dimensions (fix CLS). JavaScript bundle 862KB, potential tree-shake: -18% = 140KB. Render-blocking CSS 98KB - inline critical CSS, defer reszty. Liczba HTTP requestów: 87 → target 52 (głównie assets). Gzip teraz na 78% stron, Brotli zaledwie 2% - wdrożenie Brotli dla 95% ruchu = 220KB oszczędności średnio.

Third-party scripts - 8 plików, 5 render-blocking (1,2s opóźnienia): GA +240ms, Facebook +180ms, reCAPTCHA +320ms. Target: defer/async, lazy-init na click/scroll, Web Workers dla parsing. Oczekiwana redukcja: 420ms w 30 dni.

Server-side issues - SPA React bez SSR (hydration 1.8s, TBT 156ms). Endpoint /api/products: 480ms (P95 620ms, P99 1.2s). 8% requests dotkniętych rate limitingiem. Quickwin: Redis cache dla popularnych produktów, pagination.

Monitoring i raportowanie - 30/60/90/180 dni

30 dni - Phase 1 (Quick Wins):

  • Assets: WebP conversion, hero image AVIF, explicit image dimensions
  • Scripts: GA/Pixel deferring, CSS inlining, unused CSS removal
  • Target: LCP -20%, INP -15%, CLS -25%, TTFB -150ms
  • Expected: bounce rate -8pp, conversion rate +0,3pp

60 dni - Phase 2 (Architecture):

  • Redux bundle tree-shake, code splitting, font preload + font-display swap
  • CDN cache headers 100%, Redis cache layer
  • Target: LCP docelowy na lab, TTFB < 250ms, TTFB field validacja
  • Expected: conversion rate +0,7pp, sessions +1,5%

90 dni - Phase 3 (Validation & Deep Optimization):

  • Service Worker (static asset caching), lazy hydration React
  • Third-party script timing refinement, main thread bottleneck resolution
  • Target: All Core Web Vitals Green (100% stron), INP < 100ms lab
  • Expected: conversion rate +1,2pp, bounce rate -27pp, przychód +42K PLN/month

180 dni - Phase 4 (Maintenance & Scaling):

  • Performance budget enforcement (limits, CI/CD integration)
  • Continuous monitoring via BoomerangJS RUM (0,5% sample, 12K events/day)
  • Quarterly review cycle

Monitoring narzędzia i alerty

Codzienne: CrUX snapshot (metric change > 5%), Lighthouse batch weekly (5 device simulations), real-user monitoring via BoomerangJS. Alerty: regression > 10% na dowolnym metryce = incident. Raportowanie: monthly stakeholder dashboard (Grafana, shared link), quarterly deep-dive. Daily monitoring team Slack channel.

ROI i business case

Engineering effort estimate: 280 godzin (~42K PLN salary cost). Breakeven: 1,3 miesiąca (via przychód lift +42K PLN/month + ruch retention worth ~240K PLN churn reduction annualized). Priorytet inwestycji: P1 (LCP/INP optymalizacja = +4,2% conversion lift), P2 (CLS, third-party), P3 (Advanced caching, SPA refactor).

Chcesz taki raport dla siebie?

Twój audyt powstanie ręcznie, na podstawie Twoich danych. Napisz, czym się zajmujesz, a podpowiem, od którego zacząć.

Powiązana usługa: Optymalizacja i szybkość

WhatsApp