Core Web Vitals — optymalizacja LCP, CLS i INP dla hostingu
Opublikowano: 10 kwietnia 2026 · Kategoria: Hosting / Wydajność
Core Web Vitals to trzy metryki Google mierzące doświadczenie użytkownika: LCP (szybkość ładowania), CLS (stabilność układu) i INP (responsywność). Od 2021 roku są czynnikiem rankingowym — strona z dobrymi vitals może wyprzedzić konkurenta z lepszymi linkami, jeśli jest szybsza i stabilniejsza.
Progi Core Web Vitals — co jest "good"?
| Metryka | Good (zielony) | Needs Improvement | Poor (czerwony) |
|---|---|---|---|
| LCP (Largest Contentful Paint) | < 2.5s | 2.5s – 4.0s | > 4.0s |
| CLS (Cumulative Layout Shift) | < 0.1 | 0.1 – 0.25 | > 0.25 |
| INP (Interaction to Next Paint) | < 200ms | 200ms – 500ms | > 500ms |
LCP — Largest Contentful Paint
LCP mierzy czas do wyświetlenia największego elementu widocznego w viewporcie (najczęściej hero image, nagłówek H1 lub baner). Cel: poniżej 2.5s.
1. Optymalizacja TTFB — pierwszy krok
LCP = TTFB + czas renderowania. Jeśli TTFB jest 1.5s, LCP poniżej 2.5s jest prawie niemożliwe. Dobre TTFB to mniej niż 200ms (Google) lub 600ms (akceptowalne).
# Pomiar TTFB przez curl
curl -o /dev/null -s -w "TTFB: %{time_starttransfer}s\nTotal: %{time_total}s\n" https://example.com
# Narzędzia online:
# - https://www.webpagetest.org/ (zakładka "Web Vitals")
# - https://pagespeed.web.dev/ (Google PageSpeed Insights)
# - Google Search Console → Core Web Vitals (dane rzeczywiste) Jak obniżyć TTFB:
- Cache strony — WP Rocket, LiteSpeed Cache, Nginx FastCGI Cache. Cached TTFB to 20-50ms zamiast 300-800ms.
- OPcache PHP — kompilacja PHP do bytecode. Bez OPcache każde żądanie parsuje PHP od nowa.
- Redis/Memcached — cache bazy danych. Wolne zapytania SQL = wysoki TTFB.
- CDN z edge caching — Cloudflare, BunnyCDN serwują HTML z serwera brzegowego, 20-50ms TTFB globalnie.
- Lepszy hosting — NVMe zamiast HDD, LiteSpeed zamiast Apache, VPS zamiast shared z przeładowanym serwerem.
2. Preload LCP image
<!-- W <head> — przeglądarka pobiera obraz ZANIM przetworzy CSS --> <link rel="preload" as="image" href="/images/hero.webp" fetchpriority="high"> <!-- Dla responsive images --> <link rel="preload" as="image" href="/images/hero-800.webp" imagesrcset="/images/hero-400.webp 400w, /images/hero-800.webp 800w" imagesizes="(max-width: 600px) 400px, 800px" fetchpriority="high"> <!-- NIE dodawaj loading="lazy" do LCP image! --> <img src="/images/hero.webp" alt="Hero" fetchpriority="high">
3. Optymalizacja obrazów
- Format WebP/AVIF — 25-50% mniejszy niż JPEG przy tej samej jakości
- Właściwy rozmiar — obraz 1920px na mobile to przepalona przepustowość. Użyj srcset.
- Kompresja — Squoosh, ImageMagick z quality=80
- Unikaj CSS background-image dla LCP — przeglądarka nie może preload-ować. Użyj
<img>.
CLS — Cumulative Layout Shift
CLS mierzy nieoczekiwane przesunięcia elementów podczas ładowania. Wartość 0.1 to próg "good" — powyżej użytkownicy przypadkowo klikają nie te przyciski.
Główne przyczyny CLS i jak je naprawić
<!-- Problem: obraz bez wymiarów przesuwa treść podczas ładowania -->
<img src="photo.jpg" alt="Zdjęcie"> <!-- ZLE -->
<!-- Rozwiązanie: zawsze podawaj width i height -->
<img src="photo.jpg" alt="Zdjęcie" width="800" height="600"> <!-- DOBRZE -->
<!-- CSS: rezerwuj miejsce dla aspect ratio -->
<style>
img { aspect-ratio: attr(width) / attr(height); height: auto; width: 100%; }
</style> /* Fonty: font-display: swap może powodować CLS jeśli fallback font jest inny */
/* Lepiej: font-display: optional (nie podmienia fontów) lub size-adjust */
@font-face {
font-family: 'MyFont';
src: url('/fonts/myfont.woff2') format('woff2');
font-display: swap; /* dobry dla LCP, może powodować CLS */
}
/* Bezpieczniej: preload fontu + font-display: block z krótkim timeout */
/* Lub użyj font-display: optional — brak FOUT, brak CLS */ - Reklamy i embeds — zarezerwuj miejsce przez
min-heightprzed załadowaniem - Bannery cookie — nie wstawiaj na górze strony, używaj fixed/sticky pozycji
- Animacje CSS — używaj tylko
transformiopacity— nie powodują layout shift - Dynamiczne treści — skeleton screens zamiast pojawienia się elementu z niczego
INP — Interaction to Next Paint
INP mierzy czas od interakcji użytkownika (klik, tapnięcie) do następnego renderowania. Główne przyczyny złego INP:
- Long Tasks JS — zadania JS trwające ponad 50ms blokują główny wątek. Narzędzie: Chrome DevTools → Performance → "Long Tasks" (czerwone bloki)
- Zbyt dużo JS — bundle 1MB+ = długi parsing. Code splitting, lazy loading komponentów, tree shaking
- Ciężkie event handlers — np. klik triggeruje synchroniczne obliczenia. Przenieś do Web Worker lub użyj requestAnimationFrame
- WordPress plugins — Contact Form 7, WooCommerce, slidersy — każdy dodaje JS. Audyt przez Query Monitor lub WP Hive
// Pomiar INP w JavaScript (PerformanceObserver)
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.interactionId) {
console.log('INP candidate:', entry.duration, 'ms', entry.name);
}
}
});
observer.observe({ type: 'event', buffered: true, durationThreshold: 16 }); Narzędzia pomiarowe
- PageSpeed Insights — pagespeed.web.dev — dane lab (Lighthouse) + dane field (CrUX). Najważniejsze do sprawdzenia.
- Lighthouse CLI —
npx lighthouse https://example.com --output=html— lokalne testy, działa bez Chrome DevTools. Dobry do CI/CD. - WebPageTest — webpagetest.org — waterfall chart, CWV po lokalizacji, film z ładowania strony.
- Google Search Console — Core Web Vitals report z danymi CrUX — rzeczywiste doświadczenie użytkowników.
- Chrome DevTools — F12 → Performance → nagraj ładowanie. Widoczne Long Tasks, Layout Shifts i Main Thread Activity.