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.