Most page speed investigations start and end in the wrong place. "My Lighthouse score is low" is not a diagnosis — it's a symptom. The score is a weighted average of several sub-scores, each driven by a different metric, each pointing to a different root cause. Knowing that your TTFB is 900ms is actionable. Knowing your Lighthouse score is 43 is not.
This guide walks through every major page speed metric in the order they appear in a network waterfall — from the moment a browser resolves a domain name to the moment the largest content element finishes loading — and explains what each one measures, what a good value looks like, and what causes it to be high.
Key Takeaways
- Page speed is a chain of sequential phases — DNS, TCP, TLS, TTFB, download, parse, render. A bottleneck in any phase delays everything after it
- TTFB (Time to First Byte) is the single most important server-side metric — it measures the gap between the request leaving the browser and the first byte of response arriving
- LCP (Largest Contentful Paint) is the Core Web Vitals metric most correlated with perceived load speed — it measures when the largest visible element finishes rendering
- DNS and TCP times are largely infrastructure-controlled — you fix them with CDN and server location, not code changes
- Render-blocking scripts and missing image dimensions are HTML-level problems you can fix without server changes
The Page Load Waterfall: Phase by Phase
Every page load follows the same sequence of phases. Understanding what each phase measures tells you which part of the stack to fix when it's slow.
Phase 1: DNS Lookup
Before a browser can connect to your server, it needs to translate your domain name into an IP address. This is the DNS lookup — a query to a DNS resolver that typically takes 10–100ms. Repeated visits use cached DNS records and take near-zero time. First visits (or visits from users far from DNS servers) can take longer.
What causes high DNS time:
- Short DNS TTL (time-to-live): if your TTL is set to 60 seconds, every user who hasn't visited in the last minute pays the full lookup cost
- Geographically distant authoritative nameservers: DNS queries need to reach your nameserver and return; a nameserver in US East adds latency for users in Southeast Asia
- Too many DNS lookups per page: each third-party domain (analytics, fonts, ads) requires its own DNS lookup if not preconnected
What good looks like: Under 50ms. Over 200ms consistently is worth investigating — usually a TTL or nameserver location issue.
Fix: Increase DNS TTL to 3600s or higher on records that rarely change. Use a CDN with distributed anycast DNS (Cloudflare, Fastly). Add <link rel="dns-prefetch"> for third-party domains you load on every page.
Phase 2: TCP Connection
After DNS resolves, the browser opens a TCP connection to your server. This requires a three-way handshake: SYN → SYN-ACK → ACK. Each round trip takes the network latency between client and server — typically 10–150ms depending on geographic distance.
What causes high TCP time:
- Server located far from users: a server in London adds ~150ms round-trip for a user in Sydney
- Missing HTTP/2 or HTTP/3: HTTP/1.1 opens a new TCP connection per resource; HTTP/2 multiplexes many requests over one connection, amortizing the handshake cost
What good looks like: Under 50ms for users in the same region as your server. Over 200ms points to geographic distance — the primary fix is a CDN with edge nodes closer to your users.
Phase 3: TLS Handshake
For HTTPS connections (which is every production site), the TCP connection is followed by a TLS handshake to negotiate encryption. This adds 1–2 additional round trips — typically 50–200ms on top of TCP.
What causes high TLS time:
- TLS 1.2 vs TLS 1.3: TLS 1.2 requires 2 round trips to complete the handshake; TLS 1.3 completes in 1 (roughly halving the overhead)
- No session resumption: for repeat visitors, TLS session tickets allow resuming an existing session without a full handshake
- Slow certificate chain: an overly long certificate chain (intermediate CAs) requires additional downloads
What good looks like: Under 100ms. Consistently over 300ms suggests TLS 1.2 without session resumption or a CDN without TLS termination at the edge.
TTFB — The Most Important Single Metric
TTFB (Time to First Byte) is the time from when the browser sends its HTTP request to when the first byte of the response arrives. It includes the network round trip plus the time your server spends generating the response.
TTFB is where server-side performance lives. A slow database query, a slow server-side render, a slow origin behind a CDN that missed cache — all of these show up as high TTFB.
| TTFB range | Classification | What it typically means |
|---|---|---|
| Under 200ms | Good (green) | Server-side generation is fast; CDN cache likely hitting |
| 200–600ms | Needs improvement (amber) | Some server-side latency; CDN may be missing or not caching |
| Over 600ms | Poor (red) | Slow server-side render, slow database, no CDN, or CDN misconfiguration |
| Over 1800ms | CWV failure threshold | Google classifies TTFB >1800ms as a Core Web Vitals failure |
What causes high TTFB:
- Slow server-side rendering: the server is doing expensive work — database queries, API calls, template rendering — before sending any response bytes
- No CDN or CDN cache miss: every request hitting your origin server adds the round-trip to the origin plus origin processing time
- Redirect chains: each redirect adds a full TTFB before the final page even starts loading. A 3-hop chain with 200ms TTFB per hop adds 600ms before any content arrives. See our redirect chain guide for how to find and fix chains.
- Slow hosting: shared hosting under load, underpowered VPS, or poorly-configured serverless cold starts
Fixes in priority order:
- Add a CDN (Cloudflare, Vercel Edge, Fastly) with aggressive caching for static and semi-static pages
- Audit slow database queries — most high TTFB on dynamic pages traces to one or two slow queries
- Collapse redirect chains so the first request goes directly to the final URL
- Enable HTTP/2 (multiplexing) and TLS 1.3 at your CDN or origin
Compression — The Low-Effort TTFB Multiplier
Before discussing render metrics, it's worth calling out compression separately. Gzip and Brotli compress HTML, CSS, and JavaScript before sending them over the wire. A 200KB HTML file compressed to 40KB downloads 5× faster — improving everything from download time to rendering start.
What to check: the Content-Encoding response header. Values: gzip, br (Brotli), zstd. No Content-Encoding header means the response is uncompressed — a common misconfiguration on origin servers behind CDNs.
What good looks like: Brotli is preferred (15–20% better compression than gzip). Any compression is far better than none.
FCP — First Contentful Paint
FCP (First Contentful Paint) measures when the browser renders the first piece of DOM content — any text, image, canvas, or non-white background. It's the first moment the user sees something happening on the page.
FCP is affected by everything before it — DNS, TCP, TLS, TTFB — plus render-blocking resources in the <head>. A script or stylesheet with no defer or async attribute blocks parsing and delays FCP even after all bytes have downloaded.
What good looks like: Under 1.8s on mobile (Google's "good" threshold). Over 3s is classified as "poor".
Top fixes:
- Mark non-critical scripts as
deferorasync— they'll be fetched without blocking parsing - Load non-critical stylesheets with
media="print" onload="this.media='all'"pattern - Inline critical CSS (the CSS needed to render above-fold content) directly in the
<head>so the browser doesn't wait for an external file
LCP — Largest Contentful Paint
LCP (Largest Contentful Paint) is the Core Web Vitals metric most correlated with perceived speed. It measures when the largest visible content element (usually the hero image or the main heading) finishes rendering. It's the moment the page feels "loaded".
| LCP range | Classification |
|---|---|
| Under 2.5s | Good |
| 2.5–4.0s | Needs improvement |
| Over 4.0s | Poor |
What causes poor LCP:
- Hero image not preloaded: the browser discovers the LCP image only after parsing the HTML, then has to fetch it. A
<link rel="preload" as="image">tells the browser to fetch it before parsing — the most impactful single change for most sites with image-based LCP elements - Hero image lacking dimensions: without explicit
widthandheightattributes, the browser can't reserve space. It paints layout, then shifts when the image loads — this triggers CLS and delays LCP - LCP image lazy-loaded: images with
loading="lazy"are intentionally deferred — if the LCP element is lazy-loaded, it won't start fetching until the page is partially rendered, guaranteeing a poor LCP - Large uncompressed image: a 2MB hero image takes far longer to download than a 200KB WebP equivalent
CrUX vs lab data: Lighthouse shows lab LCP (synthetic test from a single location). CrUX (Chrome User Experience Report) shows real-user LCP from actual Chrome users visiting your site. Lab data is useful for development; CrUX is what Google uses for rankings. The Page Speed Inspector shows both.
CLS — Cumulative Layout Shift
CLS measures how much the page layout shifts during load — elements moving as images, ads, or fonts render. A page that shifts text under your cursor as you're about to click is a high CLS page. Google's threshold for "good" CLS is 0.1 (a score, not milliseconds).
Most common causes of high CLS:
- Images without
widthandheightattributes — the browser can't reserve space until the image loads, then reflows layout - Ads or embeds with no reserved height — inserting a banner ad into the page pushes content down
- Web fonts causing FOUT (Flash of Unstyled Text) — adding
font-display: swapeliminates the invisible text phase but can cause a shift when the custom font loads and has different metrics than the fallback
HTML-Level Checks That Affect All Speed Metrics
Beyond the network and server phases, the HTML structure of the page has a major impact on rendering speed. These are the checks the Page Speed Inspector surfaces:
- Render-blocking scripts: any
<script>in the<head>withoutdeferorasyncblocks parsing until the script downloads and executes - Render-blocking stylesheets: synchronous
<link rel="stylesheet">blocks rendering until the stylesheet fully downloads - Images missing dimensions: images without
width/heightattributes cause layout reflow (contributing to CLS) - Below-fold images without lazy loading: images not visible on initial load should use
loading="lazy"— they don't need to be fetched immediately and their download competes with above-fold resources - Missing preconnect hints: third-party domains (fonts, analytics) require DNS + TCP + TLS before they can send the first byte. A
<link rel="preconnect">starts this process as soon as the browser parses the<head>— before it even encounters the actual resource request - Fonts missing font-display: without
font-display: swaporfont-display: optional, browsers show invisible text while the font loads (FOIT — Flash of Invisible Text), a visible content delay - Oversized DOM: a DOM with more than 1500 nodes is slow to parse, style, and reflow. DOM size above 800 elements starts to affect interactivity (INP)
Measure Your TTFB, LCP, and HTML Issues in One Pass
Free, no signup. The Page Speed Inspector gives you DNS, TCP, TLS, TTFB, compression, cache policy, HTTP version, DOM size, render-blocking scripts, missing dimensions, and CrUX real-user data — all from one URL.
Try the Page Speed Inspector Free →Affiliate disclosure: Some links on this page may be affiliate links. We only mention tools we've personally used and have an honest opinion about. Affiliate revenue helps keep ByteWaveNetwork's tools free and maintained. We are not paid by any of the tools compared in this article for favorable coverage.