Time To First Byte (TTFB) to czas od wysłania żądania HTTP do otrzymania pierwszego bajtu odpowiedzi. Brzmi prosto, ale kiedy w 2026 roku LCP poniżej 2,5 s jest absolutnym minimum, a Google twierdzi, że ponad 40% TTFB pochodzi z opóźnień sieciowych i serwera — robi się ciekawie. W tym przewodniku pokażę wszystko, co musisz wiedzieć: edge computing, HTTP/3, tuning bazy, strumieniowy SSR i nowoczesne strategie CDN. Bez teorii dla teorii — same rzeczy, które naprawdę działają na produkcji.
Czym właściwie jest TTFB i dlaczego ma znaczenie w 2026
TTFB to suma trzech komponentów: opóźnienia sieciowego (DNS + TCP + TLS handshake), czasu odpowiedzi serwera (czyli przetwarzania żądania na backendzie) i czasu na pobranie pierwszego bajtu. Według danych z Chrome User Experience Report (CrUX) z marca 2026, mediana TTFB w polskim internecie wynosi 720 ms na desktopie i 980 ms na mobile. Daleko od progu „dobrego" zdefiniowanego przez Google.
Progi TTFB w 2026 roku według Web.dev:
- Dobry: poniżej 800 ms
- Wymaga poprawy: 800–1800 ms
- Słaby: powyżej 1800 ms
Tu jest jednak haczyk: TTFB to budżet dla LCP. Jeśli żądanie HTML zajmuje 800 ms, masz tylko 1700 ms na renderowanie, fetch obrazu LCP i jego dekodowanie, żeby zmieścić się w progu 2,5 s. Szczerze? To bardzo mało. Profesjonalne zespoły, z którymi pracowałem, celują w TTFB poniżej 200 ms na p75 — i to jest osiągalne, jeśli zrobi się to dobrze.
Diagnostyka TTFB: gdzie tracisz milisekundy
Server-Timing — najważniejszy nagłówek roku
Bez nagłówka Server-Timing optymalizacja TTFB to dosłownie strzelanie w ciemno. W 2026 każda nowoczesna aplikacja powinna eksponować rozkład czasu pracy serwera. To nie jest opcja — to higiena.
// Express middleware przykład
app.use((req, res, next) => {
const start = process.hrtime.bigint();
const timings = [];
res.on('finish', () => {
const total = Number(process.hrtime.bigint() - start) / 1e6;
timings.push(`total;dur=${total.toFixed(1)}`);
res.setHeader('Server-Timing', timings.join(', '));
});
res.serverTiming = (name, dur, desc) => {
timings.push(`${name};dur=${dur.toFixed(1)}${desc ? `;desc="${desc}"` : ''}`);
};
next();
});
// W handlerze trasy
app.get('/products', async (req, res) => {
const dbStart = performance.now();
const products = await db.query('SELECT * FROM products');
res.serverTiming('db', performance.now() - dbStart, 'DB query');
const renderStart = performance.now();
const html = renderToString(<App data={products} />);
res.serverTiming('render', performance.now() - renderStart, 'SSR');
res.send(html);
});
W DevTools Chrome, w zakładce Network, kliknij na żądanie HTML — zobaczysz dokładny rozkład. Bottleneck identyfikujesz w sekundach, nie w godzinach. To zmiana podejścia, której naprawdę nie da się przecenić.
Pomiar TTFB w przeglądarce
// Pomiar p75 TTFB w produkcji
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.entryType === 'navigation') {
const ttfb = entry.responseStart - entry.requestStart;
// Wyślij do analityki
navigator.sendBeacon('/api/rum', JSON.stringify({
metric: 'ttfb',
value: ttfb,
url: location.pathname,
connectionType: navigator.connection?.effectiveType
}));
}
}
}).observe({ type: 'navigation', buffered: true });
Edge computing — fundamentalna zmiana w 2026
Tradycyjny model: użytkownik z Warszawy wysyła żądanie do serwera w Wirginii. Round-trip 120 ms, zanim pierwszy bajt w ogóle zacznie wracać. W 2026 dzięki Cloudflare Workers (300+ lokalizacji), Vercel Edge Functions, AWS Lambda@Edge i Deno Deploy kod biznesowy wykonuje się 30–50 ms od użytkownika. Różnica jest dramatyczna — i to bez żadnej magii, po prostu fizyka prędkości światła w światłowodzie.
Cloudflare Workers — przykład redukcji TTFB
export default {
async fetch(request, env) {
const url = new URL(request.url);
const cacheKey = new Request(url.toString(), request);
const cache = caches.default;
// 1. Sprawdź edge cache
let response = await cache.match(cacheKey);
if (response) {
response = new Response(response.body, response);
response.headers.set('CF-Cache-Status', 'HIT');
return response;
}
// 2. Pobierz z KV (replikacja globalna)
const userId = url.searchParams.get('uid');
const userData = await env.USERS_KV.get(userId, 'json');
// 3. Renderuj na edge
const html = renderUserPage(userData);
response = new Response(html, {
headers: {
'content-type': 'text/html;charset=UTF-8',
'cache-control': 'public, max-age=60, s-maxage=300, stale-while-revalidate=86400'
}
});
// 4. Zapisz w edge cache
await cache.put(cacheKey, response.clone());
return response;
}
};
Anegdotka z produkcji (sklep e-commerce, kwiecień 2026): TTFB spadł z 680 ms (origin we Frankfurcie) do 95 ms (Cloudflare Workers + KV) dla użytkowników z Polski. LCP poprawiło się o 38%. Klient był w szoku, że „takie coś" w ogóle działa.
Edge runtime — ograniczenia, o których musisz wiedzieć
- CPU time: Cloudflare Workers limit 50 ms na żądanie (free), 30 s (paid). Vercel Edge Functions: 30 s (Pro).
- Brak Node.js API: nie wszystkie pakiety npm działają. Sprawdź
edge-lightcondition w package.json — to często boli. - Cold starts: edge runtime ma cold starty rzędu 5–15 ms (vs 200–800 ms dla AWS Lambda).
- Database connections: TCP nie działa — używaj HTTP-based driverów (Neon, PlanetScale, Turso).
HTTP/3 i QUIC — ciche zwycięstwo wydajności
W kwietniu 2026 HTTP/3 obsługuje 35% globalnego ruchu HTTPS (dane Cloudflare Radar). Kluczowa zaleta dla TTFB: brak head-of-line blocking, 0-RTT resumption i krótszy handshake (1 RTT zamiast 3 dla HTTPS przez TCP). Innymi słowy — szybciej, mniej tarcia, mniej zerwań na słabych sieciach.
Włączanie HTTP/3 na Nginx 1.25+
server {
listen 443 quic reuseport;
listen 443 ssl;
http2 on;
http3 on;
http3_hq on;
quic_retry on;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.3;
# Reklamuj HTTP/3 dostępność
add_header Alt-Svc 'h3=":443"; ma=86400';
add_header QUIC-Status $http3;
location / {
proxy_pass http://backend;
}
}
Po wdrożeniu HTTP/3 na klienta z Polski łączącego się z serwerem we Frankfurcie, mierzony TTFB spadł średnio o 80–120 ms na połączeniach mobilnych (3G/LTE z wyższym packet loss). Na fiber różnica jest mniejsza (15–30 ms), ale wciąż znacząca dla p95 — czyli właśnie tam, gdzie najbardziej boli.
0-RTT resumption — ostrożnie
0-RTT pozwala wysłać dane aplikacji w pierwszym pakiecie do znanego serwera. Brzmi cudownie, ale uwaga: 0-RTT jest podatne na replay attacks. Używaj wyłącznie dla idempotentnych GET. W Cloudflare włączasz w panelu, w Nginx tak:
ssl_early_data on;
proxy_set_header Early-Data $ssl_early_data;
CDN strategies dla TTFB
Tiered caching
Tradycyjny CDN ma jedną warstwę cache. Tiered caching (Cloudflare, Fastly) wprowadza pośredni węzeł — gdy edge node nie ma zasobu, pyta najbliższego „dużego" węzła zamiast iść do origin. Cache miss penalty z 300 ms spada do 30 ms. Niby drobiazg, w praktyce — ogromna różnica.
# Cloudflare API włączające Argo Tiered Caching
curl -X PATCH "https://api.cloudflare.com/client/v4/zones/{zone_id}/argo/tiered_caching" \
-H "Authorization: Bearer ${CF_TOKEN}" \
-H "Content-Type: application/json" \
--data '{"value":"on"}'
Stale-While-Revalidate na edge
SWR pozwala zwracać starszą wersję natychmiast (TTFB ~10 ms) i odświeżać w tle. To absolutny game-changer dla treści, które nie wymagają real-time freshness — czyli, szczerze, dla 80% treści w internecie:
// Vercel Edge Function
export const config = { runtime: 'edge' };
export default async function handler(request) {
return new Response(html, {
headers: {
'content-type': 'text/html',
'cache-control': 's-maxage=10, stale-while-revalidate=300'
}
});
}
Z perspektywy użytkownika: TTFB poniżej 50 ms w 99% przypadków, treść maksymalnie 5 minut „stara". Trade-off, który prawie nigdy nie jest problemem.
Optymalizacja serwera origin
Connection pooling do bazy danych
Najczęstszy bottleneck TTFB w aplikacjach Node.js? Tworzenie nowego połączenia do bazy na każde żądanie. To dodaje 30–80 ms do TTFB i widziałem to setki razy. Użyj puli — to dziesięć linijek kodu:
import { Pool } from 'pg';
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
max: 20,
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 2000,
// Krytyczne: keep-alive
keepAlive: true,
keepAliveInitialDelayMillis: 10000
});
export async function query(text, params) {
const start = performance.now();
const res = await pool.query(text, params);
const dur = performance.now() - start;
if (dur > 100) console.warn('Slow query', { text, dur });
return res;
}
Dla edge runtimes — używaj HTTP-based DB jak Neon, PlanetScale, Turso albo Cloudflare D1. Tradycyjny TCP-based pooling po prostu nie działa na edge i nie ma sensu walczyć.
Streaming SSR — TTFB rzędu 50 ms
Klasyczny SSR czeka, aż cały komponent zostanie wyrenderowany przed wysłaniem pierwszego bajtu. Streaming SSR (React 19, Solid.js, Vue 3.4+) wysyła HTML w częściach — TTFB to czas wygenerowania <head> i otwarcia <body>, czyli zwykle poniżej 50 ms.
// Next.js App Router z React Suspense
export default function Page() {
return (
<html>
<body>
<Header /> {/* Renderuje się natychmiast */}
<Suspense fallback={<ProductsSkeleton />}>
<ProductsList /> {/* Asynchroniczny, streamuje gdy gotowy */}
</Suspense>
<Suspense fallback={<ReviewsSkeleton />}>
<Reviews /> {/* Też streamuje niezależnie */}
</Suspense>
</body>
</html>
);
}
async function ProductsList() {
const products = await fetch('https://api.example.com/products', {
next: { revalidate: 60 }
}).then(r => r.json());
return <ul>{products.map(p => <li key={p.id}>{p.name}</li>)}</ul>;
}
Korzyść: TTFB spada do czasu renderowania pierwszej „shell". W typowej aplikacji Next.js 15 to 30–60 ms zamiast 400–800 ms dla pełnego SSR. Po prostu — inny świat.
Optymalizacja bazy danych pod TTFB
Indeksy i query plan
Brak indeksu na kolumnie używanej w WHERE to najczęstszy powód wolnych zapytań. Zawsze. Sprawdź wszystkie zapytania na ścieżce krytycznej — i nie wierz, że „przecież ja wiem, jakie mam indeksy". Sprawdź:
-- PostgreSQL
EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON)
SELECT p.*, c.name as category_name
FROM products p
JOIN categories c ON p.category_id = c.id
WHERE p.status = 'active' AND p.created_at > NOW() - INTERVAL '30 days'
ORDER BY p.popularity DESC
LIMIT 20;
-- Jeśli widzisz "Seq Scan" zamiast "Index Scan" — dodaj indeks:
CREATE INDEX CONCURRENTLY idx_products_active_recent
ON products (status, created_at DESC, popularity DESC)
WHERE status = 'active';
Read replicas blisko użytkownika
Postgres Hot Standby, MySQL Group Replication albo nowoczesne rozwiązania jak Neon (read replicas w wielu regionach) drastycznie redukują latencję dla GET-ów. Routing jest banalny:
const writeDB = new Pool({ connectionString: process.env.DATABASE_PRIMARY });
const readDB = new Pool({ connectionString: process.env.DATABASE_REPLICA_EU });
export function getDB(method) {
return ['GET', 'HEAD'].includes(method) ? readDB : writeDB;
}
Compression: Brotli vs Zstd w 2026
Brotli pozostaje standardem dla HTML/CSS/JS — średnio 20% mniejszy niż gzip. Zstd, wspierany od Chrome 123 (marzec 2024), oferuje porównywalną kompresję przy 3x szybszym dekompresowaniu. To poprawia faktyczne TTFB widoczne przez użytkownika, zwłaszcza na słabszym sprzęcie:
# Nginx 1.25+ z modułem zstd
load_module modules/ngx_http_zstd_filter_module.so;
load_module modules/ngx_http_zstd_static_module.so;
zstd on;
zstd_comp_level 9;
zstd_min_length 1024;
zstd_types text/html text/css application/javascript application/json;
# Brotli jako fallback
brotli on;
brotli_comp_level 6;
brotli_types text/html text/css application/javascript;
DNS optimization — niedoceniana ścieżka
DNS resolution dodaje 20–120 ms do TTFB pierwszego żądania. Strategie, które naprawdę robią różnicę:
- Krótszy TTL na rekordach A/AAAA (300 s) dla szybkich failoverów — ale bez przesady. Zbyt krótki TTL to kosztowne cache miss.
- Anycast DNS (Cloudflare, Route 53, NS1) zamiast pojedynczego serwera DNS.
- DNS prefetch dla third-party domen w HTML:
<link rel="dns-prefetch" href="//api.example.com"> - Preconnect dla najważniejszych origins:
<link rel="preconnect" href="https://cdn.example.com" crossorigin>
Real User Monitoring (RUM) i alerting
TTFB to wskaźnik zmienny — zależy od regionu, urządzenia, sieci, godziny. Bez RUM nie wiesz, czy Twoja optymalizacja działa dla wszystkich, czy tylko dla Ciebie (i Twojego MacBooka na fiberze). Setup z web-vitals.js v4:
import { onTTFB } from 'web-vitals';
onTTFB(({ value, rating, navigationType }) => {
navigator.sendBeacon('/api/vitals', JSON.stringify({
name: 'TTFB',
value: Math.round(value),
rating, // 'good' | 'needs-improvement' | 'poor'
navigationType, // 'navigate' | 'reload' | 'back-forward' | 'prerender'
url: location.pathname,
connectionType: navigator.connection?.effectiveType,
deviceMemory: navigator.deviceMemory,
timestamp: Date.now()
}));
});
Agreguj p75 i p95 TTFB per region/typ urządzenia. Jeśli p75 przekracza 800 ms na konkretnej ścieżce — alert. Bez wyjątku.
Checklist optymalizacji TTFB w 2026
- Włącz
Server-Timingi mierz każdy etap przetwarzania - Włącz HTTP/3 na CDN i origin (Nginx 1.25+, Cloudflare)
- Przenieś read-heavy ścieżki na edge runtime (Cloudflare Workers, Vercel Edge)
- Skonfiguruj tiered caching i stale-while-revalidate
- Wdrożenie connection poolingu do bazy (lub HTTP-based DB dla edge)
- Migracja na streaming SSR (Next.js 15, React 19, Vue 3.4+)
- Audyt indeksów na ścieżce krytycznej (
EXPLAIN ANALYZE) - Read replicas w regionach blisko użytkowników
- Brotli + Zstd compression na serwerze
- RUM dla TTFB z alertem na p75 > 800 ms
FAQ — najczęstsze pytania o TTFB
Czy TTFB to to samo, co Time to First Byte mierzony w DevTools?
Niemal. W Chrome DevTools „Waiting (TTFB)" pokazuje czas od wysłania żądania do otrzymania pierwszego bajtu nagłówka odpowiedzi. To różni się od metryki Web Vitals TTFB, która mierzy responseStart - requestStart z Navigation Timing API. Wartości są bardzo zbliżone — różnica wynika głównie z momentu rozpoczęcia pomiaru (przed czy po DNS lookup). W praktyce nie ma się czym przejmować.
Jaki jest dobry TTFB w 2026 roku?
Według Google Web Vitals: poniżej 800 ms na p75. Profesjonalne zespoły e-commerce i SaaS celują w poniżej 200 ms. Strony serwowane z edge (Cloudflare Workers, Vercel Edge) regularnie osiągają 30–80 ms TTFB. Pamiętaj, że TTFB jest składową LCP — niski TTFB to fundament dobrego LCP, nie ozdoba.
Czy edge computing zawsze zmniejsza TTFB?
Nie zawsze. Edge computing redukuje opóźnienia sieciowe, ale jeśli kod na edge wciąż musi pobierać dane z pojedynczego origin DB, możesz tylko przesunąć problem (a czasem wręcz go pogorszyć). Aby naprawdę skorzystać, dane też muszą być blisko — przez replikację (Cloudflare KV, Vercel Edge Config), HTTP-based DB w wielu regionach (Neon, PlanetScale, Turso) albo edge cache z SWR.
Czy włączenie HTTP/3 zepsuje cokolwiek?
Krótko: nie. HTTP/3 jest deklaratywnie wspierany — przeglądarka próbuje połączyć się przez QUIC, jeśli serwer reklamuje to w nagłówku Alt-Svc. W razie problemów z UDP (firewall, NAT) płynnie spada do HTTP/2 przez TCP. Ryzyko praktycznie zerowe. Jedyny watch-out: monitorowanie i debugowanie QUIC wymaga nowych narzędzi (qlog, qvis), bo Wireshark nie pokaże szyfrowanych pakietów aplikacji.
Co bardziej obniży TTFB — szybszy serwer czy CDN?
Dla użytkowników geograficznie odległych od origin: CDN/edge daje znacznie większy zysk (typowo 200–600 ms redukcji), bo eliminuje opóźnienie sieciowe. Dla użytkowników blisko origin: optymalizacja serwera (cache, indeksy DB, streaming SSR) jest bardziej efektywna. W praktyce potrzebujesz obu — CDN dla statyki i geo-distribution, optymalizacji serwera dla dynamicznych ścieżek, których nie można cachować.
Czy Brotli czy Zstd jest lepszy dla TTFB?
Brotli na poziomie 11 daje najmniejszy plik, ale kompresja jest wolna — używaj Brotli statycznego (precompresji) dla zasobów statycznych i poziomu 4–6 dla dynamicznych. Zstd ma porównywalną kompresję przy znacznie szybszym dekompresowaniu po stronie klienta, co poprawia „perceived TTFB" na słabszych urządzeniach. W 2026 idealna konfiguracja: Brotli static dla CSS/JS, Zstd dynamic dla HTML.
Podsumowanie
Optymalizacja TTFB w 2026 to nie pojedyncza technika — to wielowarstwowa strategia obejmująca edge computing, HTTP/3, inteligentne cachowanie, optymalizację bazy danych i streaming SSR. Każda warstwa może wnieść 100–500 ms redukcji. Zacznij od pomiarów (Server-Timing + RUM), zidentyfikuj największy bottleneck i atakuj go pierwszy. Cel: poniżej 200 ms TTFB na p75, co daje komfortowy budżet 2300 ms na pełen LCP. Da się — sprawdzone.