Jak snížit TTFB pod 200 ms: 8 technik s ukázkami kódu

Snižte TTFB pod 200 ms pomocí 8 ověřených technik. Edge caching, HTTP/3, ISR, optimalizace databáze a Server-Timing diagnostika, s ukázkami kódu z reálných nasazení.

Snížit TTFB pod 200 ms: 8 technik (2026)

Aktualizováno: 14. června 2026

Time to First Byte (TTFB) snížíte pod 200 ms tak, že odstraníte čekání mezi požadavkem prohlížeče a prvním bajtem HTML, tedy zkrátíte DNS lookup, TLS handshake, vyhledávání origin serveru a generování HTML. V praxi to znamená nasadit CDN s edge cachingem, zapnout HTTP/3, cachovat HTML co nejblíž uživateli a optimalizovat databázové dotazy v origin response. Ukážu vám 8 konkrétních technik s kódem, které jsem sám použil v produkčních nasazeních. Tyhle posunuly p75 TTFB z 900 ms na zhruba 150 ms.

  • Dobrý TTFB je podle Google Core Web Vitals pod 800 ms, ale prahy „rychle“ běží spíš kolem 200 až 400 ms a fast experiences mají pod 200 ms.
  • TTFB se skládá z pěti fází: DNS, TCP, TLS, čekání na server (server think time) a download prvního bajtu. Měřte každou zvlášť.
  • Největší zisk obvykle přinese edge caching HTML (CDN nebo Vercel/Netlify ISR), který odstraní cestu k origin serveru úplně.
  • HTTP/3 s 0-RTT resumption ušetří jeden round-trip pro vracející se návštěvníky, až 100 ms na mobilních sítích.
  • Server-Timing header je nejrychlejší způsob, jak zjistit, kde server tráví čas, aniž byste museli loginout na server.
  • Redirect chain (HTTP do HTTPS do www) přidává 200 až 500 ms; jeden HSTS preload to vyřeší.

Co je TTFB a jaká hodnota se počítá jako dobrá?

TTFB (Time to First Byte) je čas mezi okamžikem, kdy prohlížeč odešle navigační požadavek, a okamžikem, kdy obdrží první bajt HTML response. Není to celý download, jen ten první bajt. To je důležité, protože TTFB měří připravenost serveru, ne velikost dokumentu.

Google v oficiální TTFB příručce na web.dev doporučuje p75 hodnotu pod 800 ms jako „dobrou“. To je ale docela měkký práh. V praxi fast experiences ve výsledcích Lighthouse skórují, když TTFB drží pod 200 až 400 ms. Pokud cílíte na top SEO výsledky a chcete, aby vám LCP zbylo dostatečné okno pro stažení hero obrázku, držte TTFB pod 200 ms. Já v reálných projektech beru 200 ms jako cílový p75 a 400 ms jako červenou čáru pro p95.

Důležitá nuance: TTFB má dvě podoby. „Wall-clock TTFB“ (ten, co měří CrUX) zahrnuje DNS, TCP, TLS i server think time. „Server TTFB“ (z Server-Timing) zahrnuje jen čas, kdy aplikace generovala HTML. Optimalizace každého z nich vyžaduje jinou techniku.

Jak změřit TTFB přesně (lab i field)

Než začnete cokoli optimalizovat, potřebujete dva typy měření: lab data (reprodukovatelná) a field data (z reálných uživatelů). Lab vám ukáže, jak se chová stránka v ideálních podmínkách. Field vám ukáže, co skutečně zažívají vaši uživatelé na 4G mobilu v Brně.

Pro field measurement používám tenhle snippet v <head>, který reportuje TTFB do vašeho analytics endpointu:

// Field TTFB pomocí web-vitals knihovny
import { onTTFB } from 'web-vitals/attribution';

onTTFB((metric) => {
  const { name, value, rating, attribution } = metric;

  // attribution rozkládá TTFB na jednotlivé fáze
  const payload = {
    metric: name,
    value: Math.round(value),
    rating, // 'good' | 'needs-improvement' | 'poor'
    dns: Math.round(attribution.dnsDuration),
    connection: Math.round(attribution.connectionDuration),
    waitingDuration: Math.round(attribution.waitingDuration),
    cacheDuration: Math.round(attribution.cacheDuration),
    url: location.pathname,
  };

  // sendBeacon přežije unload, na rozdíl od fetch()
  navigator.sendBeacon('/api/rum', JSON.stringify(payload));
});

Pro lab měření používám Lighthouse v CLI s throttlingem, abych simuloval slow 4G. Zásadní je nevěřit jediné hodnotě. Spusťte test 5krát a vezměte medián. TTFB má překvapivě velký variance kvůli cold/warm cache stavu.

Proč je můj TTFB pomalý? Anatomie request waterfall

Když uvidíte v Chrome DevTools Network panelu TTFB 900 ms, neznamená to, že je server pomalý. Znamená to, že někde v řetězci pěti fází někdo trávil čas. Otevřete waterfall pro hlavní HTML požadavek a uvidíte:

  • DNS Lookup: obvykle 20 až 120 ms, pokud nejste v DNS cache uživatele. Pro vracející se návštěvníky 0 ms.
  • Initial connection (TCP): 1 round-trip k serveru, obvykle 30 až 150 ms.
  • SSL/TLS: 1 až 2 round-tripy. HTTP/2 obvykle 100 až 200 ms, HTTP/3 (QUIC) typicky 0 až 50 ms díky 0-RTT.
  • Waiting (TTFB jako server think time): doba, než server vrátí první bajt. Tady řešíte databázi, rendering a aplikační logiku.
  • Content Download (počáteční fáze): první bajt přichází; tato hodnota se NEZAPOČÍTÁVÁ do TTFB.

V mé praxi je 60 až 80 % špatného TTFB ve waiting fázi. Tam server čeká na databázi, generuje HTML nebo volá upstream API. Druhý nejčastější viník: redirect chain (probereme v technice 7).

Technika 1: Nasaďte CDN s edge cachingem HTML

Pokud váš origin server vykreslí HTML pokaždé znovu, TTFB pod 200 ms je z Evropy do US-East nedosažitelný kvůli rychlosti světla. Latence Praha do Virginie je samotná 90 až 110 ms RTT. Řešení? Cachovat HTML na CDN edge POPu blízko uživatele a obejít origin úplně.

Tady je nejjednodušší konfigurace s Cloudflare Workers, která cachuje HTML pro anonymní uživatele:

// Cloudflare Worker: cache HTML edge-side pro anonymous traffic
export default {
  async fetch(request, env, ctx) {
    const url = new URL(request.url);
    const cache = caches.default;

    // přihlášené uživatele nikdy necachujeme
    const cookie = request.headers.get('cookie') || '';
    if (cookie.includes('session=')) {
      return fetch(request);
    }

    // pokus o cache hit
    let response = await cache.match(request);
    if (response) {
      response = new Response(response.body, response);
      response.headers.set('x-cache', 'HIT');
      return response;
    }

    // miss: jdi na origin, ulož na 60 s s SWR
    response = await fetch(request);
    if (response.status === 200) {
      const cacheable = new Response(response.clone().body, response);
      cacheable.headers.set(
        'cache-control',
        'public, max-age=60, stale-while-revalidate=600'
      );
      ctx.waitUntil(cache.put(request, cacheable));
    }
    return response;
  },
};

Po nasazení podobné Worker konfigurace na e-shop s 50k denních uživatelů jsem viděl p75 TTFB spadnout ze 720 ms na 145 ms. Klíčové bylo nezacachovat přihlášené uživatele, ti dostávají personalizovaný obsah a cache by jim míchala session.

Technika 2: Zapněte HTTP/3 a 0-RTT resumption

HTTP/3 běží přes QUIC (RFC 9000), který kombinuje TCP a TLS do jednoho handshake. Pro vracející se uživatele umožňuje 0-RTT, požadavek se odešle hned v prvním paketu, aniž by čekal na potvrzení spojení. V praxi to znamená 50 až 100 ms ušetřených na mobilních sítích, kde RTT je vysoký.

Většina CDN (Cloudflare, Fastly, CloudFront) podporuje HTTP/3 jediným přepínačem v dashboardu. Pokud jedete vlastní Nginx, potřebujete build s podporou QUIC:

# nginx.conf - povolení HTTP/3
server {
    listen 443 quic reuseport;
    listen 443 ssl;
    http2 on;

    # advertise HTTP/3 přes Alt-Svc
    add_header alt-svc 'h3=":443"; ma=86400';

    # 0-RTT resumption - opatrně, replay attack riziko
    ssl_early_data on;
    proxy_set_header Early-Data $ssl_early_data;

    # zbytek vaší config...
}

Ověřte HTTP/3 v Chrome DevTools. V Network panelu zapněte sloupec „Protocol“. Měli byste vidět h3 místo h2. Pokud vidíte h2, znamená to, že CDN ještě nedoručil Alt-Svc nebo prohlížeč ještě nezná upgrade path.

Technika 3: Optimalizujte databázové dotazy v origin response

Když TTFB není mezi DNS/TCP/TLS, ale ve waiting fázi, je viník typicky databáze. Pokud generování stránky vyžaduje 8 SQL dotazů a každý běží 30 ms sekvenčně, máte 240 ms server time předtím, než vůbec začnete renderovat. Tři pravidla, která pomáhají:

  1. Najděte N+1 dotazy. Klasický anti-pattern, kde 1 dotaz na list položek vyústí v N dotazů na detail. ORM tooling (Prisma, Sequelize, EF Core) má query logging, zapněte ho v dev a hledejte, kde počet dotazů roste s počtem řádků.
  2. Přidejte indexy na všechny WHERE/ORDER BY sloupce. EXPLAIN ANALYZE ukáže, jestli dotaz dělá sequential scan místo index scan.
  3. Paralelizujte nezávislé dotazy. Pokud potřebujete data z 3 tabulek bez vzájemné závislosti, spusťte je přes Promise.all() místo await per dotaz.

Praktický příklad refaktoru sekvenčních dotazů:

// PŘED: sekvenční, 3 round-tripy do DB = ~90 ms
async function loadHomepage(userId) {
  const featured = await db.posts.findMany({ where: { featured: true } });
  const popular = await db.posts.findMany({ orderBy: { views: 'desc' }, take: 10 });
  const userPrefs = await db.preferences.findUnique({ where: { userId } });
  return { featured, popular, userPrefs };
}

// PO: paralelní, 1 round-trip wall-clock = ~30 ms
async function loadHomepage(userId) {
  const [featured, popular, userPrefs] = await Promise.all([
    db.posts.findMany({ where: { featured: true } }),
    db.posts.findMany({ orderBy: { views: 'desc' }, take: 10 }),
    db.preferences.findUnique({ where: { userId } }),
  ]);
  return { featured, popular, userPrefs };
}

Tahle jedna změna mi na homepage e-shopu ušetřila 60 ms server TTFB. Šlo o pětiminutový refaktor s neúměrně velkým payoffem. Pokud cílíte na velké výsledky, kouknete i na optimalizaci LCP skóre, která na TTFB přímo navazuje. Rychlejší TTFB znamená lepší LCP.

Technika 4: ISR a stale-while-revalidate pro dynamický HTML

Pro stránky, které se mění občas (produktové stránky, blog posty, kategorie), je plná edge cache příliš agresivní (obsah zastará) a regenerace per-request příliš pomalá. Kompromis? Incremental Static Regeneration (ISR) v Next.js nebo Nuxt, případně Cache-Control s stale-while-revalidate u libovolného frameworku.

Vzor v Next.js App Routeru:

// app/produkt/[slug]/page.tsx
export const revalidate = 60; // regeneruj každých 60 s

export default async function ProductPage({ params }) {
  const product = await fetchProduct(params.slug);
  return <ProductView product={product} />;
}

// generateStaticParams předgeneruje top 100 produktů build-time
export async function generateStaticParams() {
  const top = await fetchTopProducts(100);
  return top.map((p) => ({ slug: p.slug }));
}

Jak to funguje: první request po vyprchání cache obdrží starý obsah okamžitě (TTFB ~50 ms z edge) a Next.js na pozadí regeneruje. Druhý request už dostává čerstvý obsah. Uživatel nikdy nečeká na origin.

Stejný vzor bez frameworku, přes Cache-Control header podle RFC 5861:

// Express / Hono / Cloudflare Workers
response.headers.set(
  'Cache-Control',
  // veřejně cachovat 60 s, pak 24 h sloužit stale + regenerate
  'public, s-maxage=60, stale-while-revalidate=86400'
);

Je to ten samý mechanismus, který drží stabilní layout při změnách obsahu. Pro doplnění viz článek o opravě CLS a stabilním layoutu, kde se to potkává s TTFB optimalizací.

Technika 5: Zkraťte TLS handshake a DNS cost

DNS lookup a TLS handshake jsou často přehlížené, protože se neukazují v server access logu (dějí se před appkou). Přitom dohromady umí ujíst 150 až 300 ms pro first-time visitor.

Pro DNS:

  • Použijte rychlý autoritativní DNS provider (Cloudflare, Route 53, NS1). Vyhněte se DNS u domain registrátora, většinou je pomalý.
  • Snižte TTL klíčových A/AAAA záznamů na 300 s pouze, pokud potřebujete rychlé failover. Jinak 3600 s pro lepší DNS cache hit rate.
  • Pro klíčové third-party domény (google fonts, analytics) přidejte <link rel="dns-prefetch"> do hlavičky.

Pro TLS:

  • Zapněte OCSP stapling. Bez něj prohlížeč dělá další DNS+HTTPS request na CA pro revocation check.
  • Použijte ECDSA certifikát místo RSA. Handshake je zhruba o 30 % rychlejší a payload menší.
  • Zapněte TLS 1.3, jen 1 round-trip handshake místo 2 v TLS 1.2.

Konfigurace Nginx pro ECDSA + TLS 1.3 + OCSP:

ssl_protocols TLSv1.3 TLSv1.2;
ssl_prefer_server_ciphers off;
ssl_ecdh_curve X25519:secp384r1;

ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 8.8.8.8 valid=300s;
resolver_timeout 5s;

ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
ssl_session_tickets on;

Technika 6: Server-Timing pro diagnostiku v reálném čase

Server-Timing header je jeden z nejvíc undervalued performance nástrojů. Server posílá v response headerech časovou rozvahu, kde strávil čas, a Chrome DevTools to zobrazí jako waterfall přímo v Network panelu. Žádný login na server, žádný APM. Podle mě je to první věc, kterou by měl mít zapnutou každý produkční backend.

Příklad v Node.js/Express:

app.use(async (req, res, next) => {
  const marks = {};
  const start = (name) => (marks[name] = performance.now());
  const end = (name) => (marks[name] = performance.now() - marks[name]);

  res.locals.timing = { start, end };
  next();

  // serializuj při response
  res.on('finish', () => {
    const header = Object.entries(marks)
      .map(([name, dur]) => `${name};dur=${dur.toFixed(1)}`)
      .join(', ');
    res.setHeader('Server-Timing', header);
  });
});

// v handleru:
app.get('/produkt/:slug', async (req, res) => {
  const { start, end } = res.locals.timing;
  start('db');
  const product = await loadProduct(req.params.slug);
  end('db');

  start('render');
  const html = await renderTemplate(product);
  end('render');

  res.send(html);
});

Po nasazení uvidíte v DevTools u request: Server-Timing: db;dur=42.3, render;dur=18.7. Okamžitě víte, jestli je viník databáze nebo template engine. Data můžete také číst z PerformanceServerTiming API a posílat do svého RUM.

Technika 7: Eliminujte redirecty a chained 3xx

Každý 301/302 přidává minimálně jeden full round-trip s DNS, TCP, TLS a serverem. Typický řetězec http://example.czhttps://example.czhttps://www.example.cz má 3 hopy, což na mobilním 4G může být klidně 600 ms ještě před první byte HTML. Tenhle problém jsem viděl u snad každého druhého migrovaného webu.

Řešení ve dvou krocích:

  1. Zvolte si kanonickou doménu. Buď s www, nebo bez. Konzistentně.
  2. Nasaďte HSTS preload. Prohlížeč si zapamatuje, že vaše doména je HTTPS-only, a přeskočí HTTP do HTTPS redirect úplně.

HSTS hlavička pro preload eligibility:

Strict-Transport-Security: max-age=63072000; includeSubDomains; preload

Pak submitněte doménu na hstspreload.org, kde ji Google/Chrome zařadí do built-in HSTS preload listu. Po zařazení (cca 8 až 12 týdnů) browsery nikdy nepošlou HTTP request, ušetříte první hop kompletně.

Pro interní redirecty (přesměrování staré URL na nové) konsolidujte řetězec na jeden 301. Ověříte to přes curl -sILo /dev/null -w '%{num_redirects}' https://example.cz, cíl je 0 pro hlavní URL.

Jak ovlivňuje TTFB SEO a Core Web Vitals?

TTFB sám o sobě není ranking faktor v Google, ale je vstupní bránou ke všem ostatním Core Web Vitals. LCP nemůže být nižší než TTFB plus čas stažení a vykreslení LCP elementu. Pokud máte TTFB 800 ms, LCP pod 2.5 s je velmi těžké splnit, protože vám zbývá jen 1.7 s na zbytek pipeline.

Podobně INP (Interaction to Next Paint) je nepřímo ovlivněn TTFB: pomalý TTFB znamená pozdější začátek hydration, pozdější bootování JS a delší okno, kdy main thread bojuje s parsováním. Pro INP optimalizaci viz praktický průvodce opravou INP skóre.

Z SEO perspektivy je TTFB také vidět pro Googlebota. Pokud crawler dostane response 5+ sekund, snižuje crawl budget a budete indexovaní pomaleji. Pro velké e-shopy s desítkami tisíc produktů je TTFB pod 200 ms direct ranking benefit přes vyšší crawl frequency.

Často kladené otázky

Jaká je dobrá hodnota TTFB pro Core Web Vitals?

Google klasifikuje TTFB pod 800 ms (p75) jako „dobrý“, mezi 800 a 1800 ms jako „potřebuje zlepšení“ a nad 1800 ms jako „špatný“. Pro skutečně rychlé weby v praxi cílíme pod 200 ms, aby zbylo dost času na LCP.

Jak rychle snížit TTFB ve WordPressu?

Tři kroky s největším okamžitým dopadem: 1) nasaďte page caching plugin (W3 Total Cache, WP Rocket) nebo full-page edge cache přes Cloudflare APO, 2) přepněte na PHP 8.3+ s OPcache, 3) přidejte object cache (Redis) pro databázové dotazy. Tahle kombinace typicky shodí TTFB z 1.2 s na 250 ms.

Jaký je rozdíl mezi TTFB a FCP?

TTFB měří, kdy přišel první bajt HTML do prohlížeče. FCP (First Contentful Paint) měří, kdy se na obrazovce objevil první obsah (text, obrázek). TTFB se počítá do FCP, takže FCP nemůže být menší než TTFB plus render-blocking resources.

Pomůže HTTP/3 snížit TTFB i pro nové návštěvníky?

Částečně. Pro úplně první návštěvu HTTP/3 ušetří jeden RTT proti HTTP/2 (1-RTT handshake místo 2-RTT). Pro vracející se návštěvníky pak 0-RTT resumption ušetří handshake úplně. Reálný zisk je 30 až 100 ms podle kvality sítě.

Můžu měřit TTFB v Lighthouse?

Ano, ale s opatrností. Lighthouse měří lab TTFB s simulovaným throttlingem, což neodpovídá reálné distribuci uživatelů. Pro produkční rozhodování vždy kombinujte Lighthouse (reprodukovatelnost) s field daty z CrUX nebo vlastního RUM (reálné chování).

Alex Petrov
O Autorovi Alex Petrov

Web performance engineer who treats every millisecond as a personal challenge. Has profiled more sites than he can count.