I want to start with a number that should make you uncomfortable. The 75th percentile TTFB on the median website is 920 milliseconds. That means before your hero image starts loading, before your CSS is parsed, before a single pixel renders, you've already burned almost a full second of your 2.5 second LCP budget. On a fast connection, that's bad. On a real phone in real conditions, you can already kiss your "good" Core Web Vitals goodbye.
And yet TTFB is the most invisible number in the performance world. Nobody opens DevTools and brags about their TTFB. Nobody screenshots it. But it is the foundation everything else stacks on top of. Fix TTFB and your LCP, FCP, and even your INP can drop without you touching a single line of frontend code.
What TTFB actually measures
TTFB is Time to First Byte. It's the gap between the browser sending the request and the first byte of the response showing up. That gap covers a bunch of stages:
- DNS lookup to find your server
- TCP handshake to establish connection
- TLS negotiation to set up HTTPS
- Any redirects (yes, every redirect adds a full round trip)
- Server processing time, which includes whatever your backend is doing
If any of those steps drag, TTFB drags. If all of them drag, you're toast.
What's a good TTFB?
Good: 800 ms or less at the 75th percentile
Needs improvement: 800 ms to 1800 ms
Poor: Over 1800 ms
Top ranking sites land at a median of 180 ms. Server only processing should be under 200 ms. If your dynamic page is taking 600 ms just on the server before sending bytes, that's a problem you can fix today.
Why TTFB beats up your LCP
Here's the thing. LCP cannot start before HTML arrives. HTML cannot arrive before TTFB clears. So if your TTFB is 1.5 seconds, your absolute best possible LCP, even with a perfect hero image and zero render blocking, is 1.5 seconds plus however long it takes to download the HTML, parse it, fetch the hero, decode it, and paint it. Realistically you're looking at 2.5 to 3 seconds in ideal conditions, which is right at the edge of "needs improvement."
I have audited sites where every frontend optimization was perfect. WebP heroes, fetchpriority high, preload hints, the works. They still failed Core Web Vitals because TTFB sat at 1.4 seconds and there was no way to claw it back.
Step 1: Measure where the time actually goes
Before you optimize, you need to know which part of TTFB is slow. Run this curl command:
curl -w "@curl-format.txt" -o /dev/null -s "https://yoursite.com"
Where curl-format.txt looks like:
time_namelookup: %{time_namelookup}s
time_connect: %{time_connect}s
time_appconnect: %{time_appconnect}s
time_pretransfer: %{time_pretransfer}s
time_redirect: %{time_redirect}s
time_starttransfer: %{time_starttransfer}s
----------
time_total: %{time_total}s
That breakdown tells you exactly where the time goes. If time_namelookup is high, your DNS is slow. If time_connect is high, your network or TLS is slow. If time_starttransfer is way bigger than time_pretransfer, your application code is slow. Different problems, different fixes.
Step 2: Cache aggressively at the edge
The single biggest TTFB win for most sites is putting a CDN in front. Cloudflare, Fastly, Bunny, AWS CloudFront, take your pick. The CDN serves cached HTML and static assets from a server geographically near the user. So instead of going to your origin server in Frankfurt, a user in Tokyo gets the response from a Tokyo edge node in 30 milliseconds.
For static or semi static pages this is an instant 70 to 90 percent TTFB reduction. Like, free. Set up Cloudflare in front of your domain, configure cache rules, done.
Cache-Control: public, max-age=300, s-maxage=3600, stale-while-revalidate=86400
Translation. Browser caches for 5 minutes. Edge caches for 1 hour. If the cache is stale but newer than 1 day, serve the stale version while revalidating in the background. So users always get instant responses, and the cache stays fresh.
Step 3: Slow database queries are usually the villain
If your TTFB is over 500 ms on a dynamic page, the smart money is on a slow database query. Open your slow query log. Find queries taking more than 50 ms. Add indexes. Most of the time that's it.
EXPLAIN ANALYZE SELECT * FROM posts WHERE author_id = 42 ORDER BY created_at DESC;
Run that in PostgreSQL or MySQL. If you see "Seq Scan" anywhere, you're missing an index. Add one:
CREATE INDEX idx_posts_author_created ON posts (author_id, created_at DESC);
Query goes from 200 ms to 2 ms. TTFB drops with it. This is the most boring, most reliable performance trick in software, and people skip it constantly.
Step 4: Cache rendered HTML
Even with fast queries, regenerating HTML on every request is wasteful. If your homepage doesn't change every five seconds, cache the rendered HTML for a few minutes.
Easy options. Redis page cache for dynamic frameworks. Static site generation if your stack supports it. Or just Cache-Control: s-maxage at the CDN level.
The pattern in Express:
app.get('/blog/:slug', async (req, res) => {
const cached = await redis.get(`page:${req.params.slug}`);
if (cached) return res.set('X-Cache', 'HIT').type('html').send(cached);
const html = await renderBlogPage(req.params.slug);
await redis.setex(`page:${req.params.slug}`, 300, html);
res.set('X-Cache', 'MISS').type('html').send(html);
});
First request: 600 ms. Every cached request after: 12 ms. Worth it.
Step 5: Move to HTTP/3
HTTP/2 already brought multiplexed requests, header compression, and server push. HTTP/3 takes it further by replacing TCP with QUIC. The big win is no more head of line blocking on packet loss. On flaky mobile networks that's a measurable 20 to 50 ms TTFB improvement.
Cloudflare and Fastly already enable HTTP/3 by default. Self hosted nginx setup:
listen 443 quic reuseport;
listen 443 ssl;
http3 on;
add_header Alt-Svc 'h3=":443"; ma=86400';
ssl_protocols TLSv1.3;
Browsers that support HTTP/3 will use it automatically once they see the Alt-Svc header. Browsers that don't will fall back to HTTP/2. Either way you win.
Step 6: Kill redirects
Every redirect adds one full round trip. DNS lookup, TCP, TLS, request, response, then the actual page request starts. On mobile that's 100 to 400 ms wasted. Common culprits:
- HTTP to HTTPS redirect (necessary, but should be a single hop, not two)
- www to non www redirect (or vice versa)
- Trailing slash normalization
- Old URLs that nobody updated
Audit with redirect-checker tools or just curl -L -o /dev/null -w "%{num_redirects}" your homepage. Anything above 1 is wasteful. Fix the canonical URL to point directly to the final destination so users land on it in one hop.
Step 7: Edge functions for dynamic stuff
Some things can't be cached. Personalized dashboards, account pages, search results. For those, run the dynamic logic at the edge instead of your origin.
Cloudflare Workers, Vercel Edge Functions, Deno Deploy, all run JavaScript or TypeScript at the network edge. The user's request never has to travel all the way to your origin server. Cold start under 50 ms, scales infinitely, no servers to manage.
export default {
async fetch(req, env) {
const user = await getUser(req, env);
return new Response(renderDashboard(user), {
headers: { 'Content-Type': 'text/html' }
});
}
};
Edge functions have limits. Tight CPU budgets, no native modules, no big dependencies. They are not for every workload. But for thin "look up something, render HTML" handlers they're miles faster than going back to origin.
Step 8: SSR vs SSG vs ISR, the boring answer
This is where modern frameworks confuse the issue. Server side rendering, static site generation, incremental static regeneration. Three acronyms, three different TTFB profiles.
| Strategy | TTFB | Best for |
|---|---|---|
| Static (SSG) | 50 to 150 ms | Marketing sites, blog posts, docs |
| ISR | 50 to 150 ms warm, 200 to 500 ms cold | Content sites with fresh data |
| SSR with cache | 100 to 400 ms | Personalized pages with shared layout |
| SSR uncached | 400 to 1500 ms | Honestly, almost never the right call for a public page |
If you're using Next.js, Nuxt, SvelteKit, or Remix and your TTFB is over 500 ms, the first question is "is this page rendering on every request?" If yes, can you cache it? If you cannot cache it because the content is per user, can you split it into a static shell plus a small dynamic island? Almost always yes.
The TTFB optimization checklist
Print this. Use it.
- Run the curl breakdown to see which stage is slow.
- Put a CDN in front of your origin. Configure edge caching with sensible TTLs.
- Audit redirects. One hop, never two.
- Profile slow database queries. Add indexes. Cache repeated queries in Redis.
- Cache rendered HTML when possible. SSG for static pages. SSR with cache for the rest.
- Enable HTTP/3 on your origin or rely on your CDN.
- Move dynamic logic to edge functions when latency matters.
- Measure with real users. CrUX field data is what Google ranks on, not your local Lighthouse run.
How TTFB shows up in Core Web Vitals reporting
Google does not have a "TTFB Core Web Vital." Officially, TTFB is a "diagnostic metric." That sounds like it doesn't matter. It absolutely matters. Bad TTFB makes good LCP impossible. Google's own data shows that sites with passing CWV almost always have median TTFB under 600 ms. Sites failing CWV almost always have TTFB over 1.2 seconds.
So when you see PageSpeed Insights flagging "Reduce server response times," that's TTFB. When you see "Avoid multiple page redirects," that's TTFB. When CrUX shows your LCP at 3.8 seconds, half of that is probably TTFB.
Fix the foundation first. Then chase the visual stuff.
WordPress specific TTFB tips
If you're on WordPress, your TTFB story is mostly about plugins. Every active plugin runs PHP on every uncached page load. Forty active plugins on a small VPS means TTFB north of a second easily. Recommendations:
- Use a real page cache plugin like WP Rocket or LiteSpeed Cache. Hit rates above 90 percent.
- Use a host with built in object caching. Kinsta, Pressable, Cloudways with Redis turned on.
- Audit plugins quarterly. Deactivate the ones you don't use.
- Move analytics out of WP and into a tag manager that loads after page render.
We have a deeper WordPress CWV guide if your stack is WP heavy.
The TTFB myths I keep busting
"My TTFB is fine in Lighthouse"
Lighthouse runs from your local machine on a fast connection. Real users on real phones in real cities will hit edges, mobile networks, and crappy hotel WiFi. Their TTFB is way higher than yours. Trust CrUX field data, not lab tests.
"I'll add a CDN later"
If your origin is in one country and your audience is global, "later" means months of bad TTFB for everyone outside your origin's region. CDN setup is usually a one hour Cloudflare onboarding. Just do it.
"My server is fast enough"
Your server is fast for the request that just hit it. The next request might queue behind a slow query, or hit cold cache, or trigger a heavy template render. Look at p75 and p95 latency, not p50.
"TTFB does not affect rankings"
Directly, no. Indirectly, yes, because TTFB shapes LCP, and LCP is a ranking signal. Bad TTFB equals bad LCP equals worse ranking on competitive queries.
TTFB benchmarks for popular hosts in 2026 (what we actually measure)
Numbers without context are useless. So here's what we see across hundreds of audited sites running on different hosts. Take this with a grain of salt because traffic patterns and configs vary, but the patterns are real.
| Host | Cached TTFB | Uncached TTFB | Notes |
|---|---|---|---|
| Cloudflare Pages + Workers | 40 to 90 ms | 120 to 250 ms | Globally fastest in our tests. Hard to beat for static plus light dynamic. |
| Vercel | 60 to 150 ms | 200 to 500 ms | Excellent for Next.js. Cold start hits more than steady state. |
| Netlify | 80 to 180 ms | 250 to 600 ms | Solid for static sites. Function cold starts are real. |
| Shared WordPress hosting | 200 to 500 ms | 800 to 2500 ms | Highly variable. Plugin count is the dominant factor. |
| Managed WordPress (Kinsta, Pressable) | 80 to 200 ms | 400 to 900 ms | Object cache and edge caching make a huge difference. |
| Self hosted VPS, well configured | 50 to 150 ms | 200 to 600 ms | Nginx + Redis + tuned PHP-FPM hits these numbers comfortably. |
| Self hosted VPS, default install | 200 to 600 ms | 800 to 3000 ms | Out of the box stacks have plenty of room. |
So if you are on shared WordPress and seeing 1.5 second TTFB, that is not unusual but it is fixable. Adding Cloudflare in front, enabling object cache, and pruning plugins typically cuts that in half.
The cheap stack that hits good TTFB everywhere
For most teams I work with, this is the smallest possible stack that still gets you good TTFB:
- Cloudflare in front of everything (free tier is enough)
- Edge caching with the s-maxage trick at one hour for static, five minutes for dynamic
- Origin server with Redis or Memcached for object cache
- Database with proper indexes on hot columns
- HTTP/3 enabled (free if you're on Cloudflare)
That setup typically lands a sub 200 ms global TTFB. Total cost: nothing if you stay on Cloudflare's free tier and a small VPS. You get most of the benefit of expensive enterprise CDNs without paying enterprise prices.
The "my site is slow only sometimes" mystery
If your TTFB looks fine in lab tests but field data shows occasional 3 second spikes, you have a tail latency problem, not an average problem. Usually traces back to one of three things:
- A slow query that only fires on certain pages or for certain users
- A queue or lock contention that backs up under load
- A third party API that your backend calls synchronously, that occasionally takes 5 seconds
p99 is what catches these. p50 hides them. So when you report TTFB to your team, report p75 (what Google uses) and p95 (what your worst case users see). Median makes everyone feel good and explains nothing.
Frequently asked questions
What's the best way to measure TTFB?
Real user data via CrUX or any RUM tool (PerformanceObserver in vanilla JS works too). Lab tests like WebPageTest are useful for debugging but the field number is what matters for Core Web Vitals.
How much can a CDN really help my TTFB?
For static or cacheable content, a CDN typically cuts TTFB by 70 to 90 percent. For uncacheable dynamic content, the win is smaller, around 20 to 40 percent, mostly from TLS termination at the edge and connection reuse.
Is server side rendering bad for TTFB?
SSR by itself is not bad. SSR without caching is bad. Cached SSR is roughly the same TTFB as static. Just put the cache in front and stop regenerating identical HTML on every request.
What's the cheapest way to fix bad TTFB?
Free Cloudflare account, point your DNS to it, turn on Always Online and Auto Minify. That alone often cuts TTFB by half on a small site. Costs zero dollars.
How does TTFB connect to INP?
It does not directly. INP measures interaction latency after the page is loaded. But long server round trips during interactions, like form submissions calling slow APIs, do show up in INP. Fast API responses help. Read the INP guide for the full breakdown.
Should I worry about HTTP/3 in 2026?
Browser support is at 95 percent. If you're on Cloudflare or any major CDN, you already have it. If you self host, enable it. The wins are real on mobile networks where packet loss is common.
Find Your TTFB Bottleneck in 30 Seconds
Run your site through VitalsFixer. We measure your TTFB by region, flag slow database queries, and tell you exactly where your server response time is hiding.
Analyze My Site Free →Server is the problem? We fix that too
Our engineers profile your stack, optimize queries, set up caching, and configure the CDN. Real humans, 48 hour turnaround, money back if scores don't improve.
View Expert Fix Service →