Skip to content

Reduce Time to First Byte — TTFB Optimization Guide

DodaTech Updated 2026-06-23 8 min read

In this tutorial, you will learn how to measure and reduce Time to First Byte (TTFB), the time between the browser requesting a page and receiving the first byte of the response. TTFB is a critical performance metric that affects all subsequent loading steps. DodaTech reduced its API TTFB from 1.2 seconds to 320 milliseconds by optimizing server configuration and database queries.

What You Will Learn

  • How to measure TTFB using browser DevTools and curl
  • How to identify the three components of TTFB: DNS, TCP/SSL, and server processing
  • How to optimize server-side processing with caching and query optimization
  • How to use Edge Computing to reduce network latency

Why It Matters

Every 100 milliseconds of TTFB improvement reduces bounce rates by approximately 1 percent. Google recommends a TTFB of under 800 milliseconds. Pages with TTFB over 1.5 seconds lose significant search ranking advantage. Doda Browser users expect instant responses, and TTFB optimization is the foundation of that experience.

Real-World Use Case

The DodaTech blog had a TTFB of 2.1 seconds on mobile due to a shared hosting server located in a single region. After migrating to a CDN with edge caching and moving the origin to a cloud provider with multi-region deployment, TTFB dropped to 350 milliseconds.

Prerequisites

You should understand HTTP request-response flow and how DNS resolution works. Experience with NGINX or Apache configuration and database query optimization is beneficial.

Step-by-Step Tutorial

Step 1: Measure TTFB Correctly

TTFB can be measured from the browser or the command line. Always test from a location representative of your users.

# Measure TTFB using curl
curl -o /dev/null -s -w "TTFB: %{time_starttransfer}s\n" https://dodatech.com

Expected output: TTFB: 0.342s (example). If this value exceeds 0.8 seconds, optimization is needed.

For more detailed breakdown:

curl -o /dev/null -s -w "\
  DNS: %{time_namelookup}s\n\
  TCP: %{time_connect}s\n\
  TLS: %{time_appconnect}s\n\
  Server: %{time_starttransfer}s\n\
  Total: %{time_total}s\n" https://dodatech.com

Expected output shows each phase. If DNS takes over 200ms, optimize DNS resolution. If Server takes over 500ms, optimize backend processing.

Step 2: Optimize DNS Resolution

DNS resolution converts domain names to IP addresses. Slow DNS adds to TTFB before any connection is made.

# Test DNS resolution time
dig dodatech.com | grep "Query time"

Expected output: ;; Query time: 12 msec. Target under 30ms.

Optimization techniques:

  1. Use a fast DNS provider (Cloudflare, Google, Quad9)
  2. Reduce the number of unique domains your page connects to
  3. Use DNS prefetch hints
<!-- DNS prefetch for third-party origins -->
<link rel="dns-prefetch" href="https://cdn.dodatech.com" />
<link rel="dns-prefetch" href="https://api.dodatech.com" />

Step 3: Optimize TLS Handshake

The TLS handshake adds 1-2 round trips before the server can send data. Enable TLS 1.3 to reduce this to one round trip (or zero with 0-RTT).

# NGINX TLS 1.3 configuration
server {
    listen 443 ssl http2;
    server_name dodatech.com;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
}

Expected behavior: With TLS 1.3, the handshake completes in one round trip. SSL session caching avoids the handshake entirely for returning visitors.

Step 4: Optimize Server-Side Processing

The largest contributor to TTFB is often the server processing time. Focus on database query optimization, caching, and efficient application code.

// Before: slow database query in Express route
app.get('/api/products', async (req, res) => {
  const products = await db.query('SELECT * FROM products ORDER BY created_at DESC');
  res.json(products);
});

// After: with query-level caching
app.get('/api/products', async (req, res) => {
  const cacheKey = 'products:list';
  let products = await cache.get(cacheKey);
  if (!products) {
    products = await db.query('SELECT id, name, price FROM products ORDER BY created_at DESC LIMIT 50');
    await cache.set(cacheKey, products, 300); // Cache for 5 minutes
  }
  res.json(products);
});

Expected improvement: The uncached query takes 800ms. The cached query returns in under 10ms, reducing server processing time by 98 percent.

Step 5: Use Edge Caching

Move content closer to users by caching full pages at the CDN edge.

# NGINX configuration for edge caching headers
server {
    location / {
        # Cache HTML for 5 minutes at CDN level
        add_header Cache-Control "public, s-maxage=300, stale-while-revalidate=3600";
    }
}

The CDN serves cached pages without contacting the origin, effectively reducing TTFB to the time it takes to reach the nearest edge node (typically under 30ms).

Step 6: Use Edge Computing for Dynamic Content

For dynamic pages that cannot be fully cached, use Edge Computing to process requests closer to users.

// Cloudflare Workers example: geo-aware response
export default {
  async fetch(request, env, ctx) {
    const country = request.cf.country;
    const cacheKey = `homepage:${country}`;
    const cached = await env.KV.get(cacheKey);
    if (cached) return new Response(cached);

    // Fetch from origin with country parameter
    const response = await fetch(`https://origin.dodatech.com/?country=${country}`);
    const text = await response.text();
    await env.KV.put(cacheKey, text, {expirationTtl: 300});
    return new Response(text);
  }
}

Expected behavior: The worker runs at Cloudflare edge, caching a version of the page per country. TTFB drops from 500ms (origin) to 30ms (edge).

Step 7: Optimize Database Connection Pooling

Database connection overhead adds to TTFB. Use connection pooling and persistent connections.

// Before: new connection per request
app.get('/api/data', async (req, res) => {
  const client = new pg.Client(CONNECTION_STRING);
  await client.connect(); // Takes 50-100ms
  const result = await client.query('SELECT * FROM data');
  await client.end();
  res.json(result.rows);
});

// After: connection pooling
const pool = new pg.Pool({connectionString: CONNECTION_STRING, max: 20});

app.get('/api/data', async (req, res) => {
  const result = await pool.query('SELECT * FROM data');
  res.json(result.rows);
});

Expected improvement: The first query takes 60ms (pool warmup). Subsequent queries reuse idle connections, taking under 2ms for the database connect step.

Step 8: Use Keep-Alive Connections

Ensure your server keeps connections alive so subsequent requests reuse the same TCP connection.

# NGINX keep-alive configuration
http {
    keepalive_timeout 65;
    keepalive_requests 1000;
    upstream backend {
        server 127.0.0.1:3000;
        keepalive 32;
    }
}

Expected behavior: The first request opens a TCP connection. Subsequent requests reuse that connection, eliminating the TCP and TLS handshake overhead.

Learning Path

flowchart LR
  A[CDN Configuration] --> B[Reduce TTFB]
  A --> C[HTTP/2 and HTTP/3]
  B --> D[Database Query Optimization]
  B --> E[Cache Strategy]
  
  style B fill:#4f46e5,color:#fff
  style A fill:#6366f1,color:#fff
  style C fill:#6366f1,color:#fff

Common Errors

  1. Measuring TTFB from the server itself: TTFB measured from localhost does not include network latency. Always measure from a remote location that represents real user geography.

  2. Ignoring DNS time: Adding a new third-party domain adds 20-120ms of DNS resolution time. Use DNS prefetch and limit external domains.

  3. Synchronous database queries: Running sequential database queries instead of parallel ones adds each query latency to the TTFB. Use Promise.all or async queries.

  4. No CDN for static content: Serving images and scripts directly from the origin adds 200-500ms of server processing to every request. Offload static content to a CDN.

  5. Large session storage: Storing large objects in session state forces the server to load and serialize data on every request. Store only session identifiers and reference cached objects.

  6. Using shared hosting with resource contention: Shared hosting splits CPU and memory across tenants. A neighbor site with a traffic spike can increase your TTFB by 10x.

Practice Questions

  1. What are the four main components of TTFB?
  2. Why is measuring TTFB from localhost unreliable?
  3. How does TLS 1.3 improve TTFB compared to TLS 1.2?
  4. What is stale-while-revalidate and how does it help TTFB?
  5. How does connection pooling reduce server processing time?

Answers: 1. DNS resolution, TCP connection, TLS handshake, and server processing time. 2. Localhost measurement excludes network latency and reflects only server processing, not the full user experience. 3. TLS 1.3 reduces the handshake from two round trips to one (or zero with 0-RTT). 4. It serves cached content immediately while fetching a fresh response in the background, eliminating wait time for the user. 5. It eliminates the overhead of creating a new database connection for each request by reusing existing connections from the pool.

Challenge

Set up a monitoring script that measures TTFB from three geographic regions (US, Europe, Asia) every 5 minutes and logs the results to a file. Configure thresholds: green (under 800ms), yellow (800-1500ms), red (over 1500ms). Use the script to identify the slowest region and investigate the cause.

FAQ

What is a good TTFB target?

Google recommends under 800ms. Ideally, aim for under 300ms for server processing and under 500ms total including network latency.

Can TTFB affect other Core Web Vitals?

Yes, a high TTFB delays the start of all subsequent loading steps. High TTFB is a common root cause of poor LCP and FCP scores.

Does using a CDN always improve TTFB?

A CDN improves TTFB for cached content because it serves from edge locations. For uncached dynamic content, the CDN adds one extra network hop, which can slightly increase TTFB.

Is TTFB important for SPAs?

Yes, the initial HTML request for an SPA still has a TTFB. Additionally, API requests the SPA makes also have their own TTFB that affects perceived performance.

How does DodaTech monitor TTFB in production?

DodaTech uses a combination of Lighthouse CI for lab data and a custom RUM solution that captures TTFB via the Navigation Timing API from real user sessions.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro