[{"data":1,"prerenderedAt":1273},["ShallowReactive",2],{"content:\u002Fnetwork-protocol-optimization\u002Ftime-to-first-byte-optimization":3,"surroundings:\u002Fnetwork-protocol-optimization\u002Ftime-to-first-byte-optimization":1264},{"id":4,"title":5,"body":6,"description":1244,"extension":1245,"meta":1246,"navigation":1257,"path":1258,"seo":1259,"stem":1262,"__hash__":1263},"content\u002Fnetwork-protocol-optimization\u002Ftime-to-first-byte-optimization\u002Findex.md","Time to First Byte (TTFB) Optimization",{"type":7,"value":8,"toc":1222},"minimark",[9,14,24,27,30,132,137,140,156,160,176,180,459,463,466,470,489,493,496,577,700,704,711,724,731,735,741,747,753,759,765,769,772,824,828,839,843,846,910,914,921,925,928,946,950,972,979,983,994,998,1022,1026,1058,1062,1074,1083,1099,1108,1117,1126,1135,1144,1153,1157,1180,1184,1207,1212,1215,1218],[10,11,13],"h1",{"id":12},"time-to-first-byte-ttfb-optimization-getting-the-response-started","Time to First Byte (TTFB) Optimization: Getting the Response Started",[15,16,17,18,23],"p",{},"This topic is part of ",[19,20,22],"a",{"href":21},"\u002Fnetwork-protocol-optimization\u002F","Network & Server Response Optimization",". Time to First Byte measures the time from the start of a navigation until the first byte of the HTML response arrives. It includes everything that happens before the browser has anything to work with: redirects, DNS lookup, connection setup, TLS negotiation, the request's trip to the server, all the work the server does to build the response, and the first bytes' trip back. Nothing visible can happen before it, so TTFB is a floor under First Contentful Paint and Largest Contentful Paint.",[15,25,26],{},"Google treats a TTFB of 800ms or less at the 75th percentile as good. That threshold is generous: on a mobile connection, if TTFB is 800ms, only 1.7 seconds remain for CSS, fonts, the LCP image and rendering before LCP crosses 2.5 seconds. Fast sites usually achieve 200–500ms at p75, mostly by serving HTML from a CDN cache close to users.",[15,28,29],{},"TTFB problems are rarely one thing. A typical slow TTFB is a stack: a tracking redirect (250ms), a connection to a distant origin without a CDN (300ms), and server time of 600ms because three backend calls run sequentially. Fixing it means measuring each component and attacking the biggest — which this topic organises into a repeatable workflow.",[15,31,32],{},[33,34,40,41,40,48,40,52,40,55,40,64,40,70,40,77,40,84,40,89,40,93,40,97,40,100,40,103,40,107,40,110,40,115,40,119,40,122,40,125,40,129,40],"svg",{"viewBox":35,"width":36,"role":37,"ariaLabel":38,"style":39},"0 0 760 318","100%","img","The parts that add up to Time to First Byte, from redirects to server processing.","height:auto;max-width:760px;display:block;margin:1.75rem auto;font-family:inherit;color:var(--fp-svg-ink)"," ",[42,43],"rect",{"className":44,"x":46,"y":46,"width":36,"height":36,"fill":47},[45],"svg-canvas","0","#ffffff",[49,50,51],"title",{},"Components of TTFB",[53,54,38],"desc",{},[42,56],{"x":57,"y":57,"width":58,"height":59,"rx":60,"fill":61,"stroke":62,"style":63},"1","758","316","10","none","currentColor","stroke-opacity:0.18",[65,66,51],"text",{"x":67,"y":68,"fill":62,"style":69},"28.0","34.0","font-size:16px;font-weight:700",[42,71],{"x":67,"y":72,"width":73,"height":74,"rx":75,"fill":61,"stroke":62,"style":76},"56.0","704.0","40.0","6","stroke-opacity:0.35",[65,78,83],{"x":79,"y":80,"fill":62,"style":81,"textAnchor":82},"42.0","80.5","font-size:13px;font-weight:700","start","Redirects",[65,85,88],{"x":86,"y":80,"fill":62,"style":87,"textAnchor":82},"207.2","font-size:12px","Each hop is a full round trip, often a new connection",[42,90],{"x":67,"y":91,"width":73,"height":74,"rx":75,"fill":62,"stroke":62,"style":92},"106.0","fill-opacity:0.06;stroke-opacity:0.4",[65,94,96],{"x":79,"y":95,"fill":62,"style":81,"textAnchor":82},"130.5","DNS lookup",[65,98,99],{"x":86,"y":95,"fill":62,"style":87,"textAnchor":82},"Cached after first visit; slow resolvers add time",[42,101],{"x":67,"y":102,"width":73,"height":74,"rx":75,"fill":62,"stroke":62,"style":92},"156.0",[65,104,106],{"x":79,"y":105,"fill":62,"style":81,"textAnchor":82},"180.5","TCP + TLS handshakes",[65,108,109],{"x":86,"y":105,"fill":62,"style":87,"textAnchor":82},"1-3 round trips; fewer with TLS 1.3 and HTTP\u002F3",[42,111],{"x":67,"y":112,"width":73,"height":74,"rx":75,"fill":113,"stroke":113,"style":114},"206.0","#0466c8","fill-opacity:0.14;stroke-opacity:0.9",[65,116,118],{"x":79,"y":117,"fill":62,"style":81,"textAnchor":82},"230.5","Request to server",[65,120,121],{"x":86,"y":117,"fill":62,"style":87,"textAnchor":82},"Network latency to the origin or CDN edge",[42,123],{"x":67,"y":124,"width":73,"height":74,"rx":75,"fill":61,"stroke":62,"style":76},"256.0",[65,126,128],{"x":79,"y":127,"fill":62,"style":81,"textAnchor":82},"280.5","Server time",[65,130,131],{"x":86,"y":127,"fill":62,"style":87,"textAnchor":82},"Routing, auth, data fetching, rendering",[133,134,136],"h2",{"id":135},"the-metric-degradation-this-topic-addresses","The Metric Degradation This Topic Addresses",[15,138,139],{},"High TTFB delays everything downstream: FCP, LCP, and indirectly INP (since hydration starts later). It shows up in the LCP breakdown as a large first sub-part. In field data, TTFB is unusually variable by geography: users far from a single-region origin see much higher values. It is also sensitive to cache state: a page served from CDN cache might have a 100ms TTFB, while a cache miss on the same page takes 1.5 seconds.",[15,141,142,143,147,148,151,152,155],{},"Two field patterns are common. A ",[144,145,146],"strong",{},"uniformly high TTFB"," across regions points at server time (slow backend, no caching). A ",[144,149,150],{},"TTFB that grows with distance"," from the origin points at missing edge caching or connection costs. A ",[144,153,154],{},"bimodal distribution"," (many fast, many slow) usually reflects cache hit versus miss.",[133,157,159],{"id":158},"prerequisites","Prerequisites",[161,162,163,167,170,173],"ul",{},[164,165,166],"li",{},"Field TTFB at p75 by page type, country and device, from RUM or CrUX.",[164,168,169],{},"Navigation Timing sub-parts from RUM (redirect, DNS, connect, TLS, request-to-response).",[164,171,172],{},"Access to server logs or tracing for backend time.",[164,174,175],{},"CDN analytics showing cache hit ratio for HTML.",[133,177,179],{"id":178},"_1-environment-setup-collect-ttfb-sub-parts","1. Environment Setup: Collect TTFB Sub-Parts",[181,182,187],"pre",{"className":183,"code":184,"language":185,"meta":186,"style":186},"language-javascript shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","\u002F\u002F RUM: break TTFB into components from Navigation Timing.\nconst [nav] = performance.getEntriesByType('navigation');\nconst parts = {\n  redirect: nav.redirectEnd - nav.redirectStart,\n  dns: nav.domainLookupEnd - nav.domainLookupStart,\n  connect: nav.connectEnd - nav.connectStart,          \u002F\u002F includes TLS\n  tls: nav.secureConnectionStart ? nav.connectEnd - nav.secureConnectionStart : 0,\n  request: nav.responseStart - nav.requestStart,       \u002F\u002F server time + latency\n  ttfb: nav.responseStart,\n  protocol: nav.nextHopProtocol,\n  serverTiming: nav.serverTiming?.map((s) => `${s.name}:${Math.round(s.duration)}`).join(','),\n};\nnavigator.sendBeacon('\u002Frum', JSON.stringify(parts));\n\u002F\u002F trade-off: cross-origin redirects hide redirect timing (it reports 0); the gap\n\u002F\u002F between startTime and fetchStart still reveals that time was spent.\n","javascript","",[188,189,190,199,237,251,263,274,288,314,328,334,340,413,419,447,453],"code",{"__ignoreMap":186},[191,192,195],"span",{"class":193,"line":194},"line",1,[191,196,198],{"class":197},"sjfSM","\u002F\u002F RUM: break TTFB into components from Navigation Timing.\n",[191,200,202,206,210,214,217,220,223,227,230,234],{"class":193,"line":201},2,[191,203,205],{"class":204},"sPARh","const",[191,207,209],{"class":208},"saISM"," [",[191,211,213],{"class":212},"sPXB4","nav",[191,215,216],{"class":208},"] ",[191,218,219],{"class":204},"=",[191,221,222],{"class":208}," performance.",[191,224,226],{"class":225},"smZ65","getEntriesByType",[191,228,229],{"class":208},"(",[191,231,233],{"class":232},"sZ8jY","'navigation'",[191,235,236],{"class":208},");\n",[191,238,240,242,245,248],{"class":193,"line":239},3,[191,241,205],{"class":204},[191,243,244],{"class":212}," parts",[191,246,247],{"class":204}," =",[191,249,250],{"class":208}," {\n",[191,252,254,257,260],{"class":193,"line":253},4,[191,255,256],{"class":208},"  redirect: nav.redirectEnd ",[191,258,259],{"class":204},"-",[191,261,262],{"class":208}," nav.redirectStart,\n",[191,264,266,269,271],{"class":193,"line":265},5,[191,267,268],{"class":208},"  dns: nav.domainLookupEnd ",[191,270,259],{"class":204},[191,272,273],{"class":208}," nav.domainLookupStart,\n",[191,275,277,280,282,285],{"class":193,"line":276},6,[191,278,279],{"class":208},"  connect: nav.connectEnd ",[191,281,259],{"class":204},[191,283,284],{"class":208}," nav.connectStart,          ",[191,286,287],{"class":197},"\u002F\u002F includes TLS\n",[191,289,291,294,297,300,302,305,308,311],{"class":193,"line":290},7,[191,292,293],{"class":208},"  tls: nav.secureConnectionStart ",[191,295,296],{"class":204},"?",[191,298,299],{"class":208}," nav.connectEnd ",[191,301,259],{"class":204},[191,303,304],{"class":208}," nav.secureConnectionStart ",[191,306,307],{"class":204},":",[191,309,310],{"class":212}," 0",[191,312,313],{"class":208},",\n",[191,315,317,320,322,325],{"class":193,"line":316},8,[191,318,319],{"class":208},"  request: nav.responseStart ",[191,321,259],{"class":204},[191,323,324],{"class":208}," nav.requestStart,       ",[191,326,327],{"class":197},"\u002F\u002F server time + latency\n",[191,329,331],{"class":193,"line":330},9,[191,332,333],{"class":208},"  ttfb: nav.responseStart,\n",[191,335,337],{"class":193,"line":336},10,[191,338,339],{"class":208},"  protocol: nav.nextHopProtocol,\n",[191,341,343,346,349,352,356,359,362,365,367,370,373,376,379,381,384,386,388,390,393,396,399,402,405,407,410],{"class":193,"line":342},11,[191,344,345],{"class":208},"  serverTiming: nav.serverTiming?.",[191,347,348],{"class":225},"map",[191,350,351],{"class":208},"((",[191,353,355],{"class":354},"sQw3B","s",[191,357,358],{"class":208},") ",[191,360,361],{"class":204},"=>",[191,363,364],{"class":232}," `${",[191,366,355],{"class":208},[191,368,369],{"class":232},".",[191,371,372],{"class":208},"name",[191,374,375],{"class":232},"}:${",[191,377,378],{"class":208},"Math",[191,380,369],{"class":232},[191,382,383],{"class":225},"round",[191,385,229],{"class":232},[191,387,355],{"class":208},[191,389,369],{"class":232},[191,391,392],{"class":208},"duration",[191,394,395],{"class":232},")",[191,397,398],{"class":232},"}`",[191,400,401],{"class":208},").",[191,403,404],{"class":225},"join",[191,406,229],{"class":208},[191,408,409],{"class":232},"','",[191,411,412],{"class":208},"),\n",[191,414,416],{"class":193,"line":415},12,[191,417,418],{"class":208},"};\n",[191,420,422,425,428,430,433,436,439,441,444],{"class":193,"line":421},13,[191,423,424],{"class":208},"navigator.",[191,426,427],{"class":225},"sendBeacon",[191,429,229],{"class":208},[191,431,432],{"class":232},"'\u002Frum'",[191,434,435],{"class":208},", ",[191,437,438],{"class":212},"JSON",[191,440,369],{"class":208},[191,442,443],{"class":225},"stringify",[191,445,446],{"class":208},"(parts));\n",[191,448,450],{"class":193,"line":449},14,[191,451,452],{"class":197},"\u002F\u002F trade-off: cross-origin redirects hide redirect timing (it reports 0); the gap\n",[191,454,456],{"class":193,"line":455},15,[191,457,458],{"class":197},"\u002F\u002F between startTime and fetchStart still reveals that time was spent.\n",[133,460,462],{"id":461},"_2-capture-a-baseline","2. Capture a Baseline",[15,464,465],{},"Compute p75 for each sub-part by page type and region. The largest sub-part at p75 is where to start. Record CDN cache hit ratio for HTML and the share of navigations with redirects.",[133,467,469],{"id":468},"_3-isolate-the-dominant-component","3. Isolate the Dominant Component",[15,471,472,473,476,477,480,481,484,485,488],{},"If ",[144,474,475],{},"redirects"," are large, find the redirecting URLs (logs, campaign links, internal links). If ",[144,478,479],{},"connect\u002FTLS"," is large, check CDN coverage, TLS version, HTTP\u002F3 and certificate chain size. If ",[144,482,483],{},"request"," (server time) dominates, add ",[188,486,487],{},"Server-Timing"," to see what the server does and look at cache hit ratios.",[133,490,492],{"id":491},"_4-apply-the-matching-fix","4. Apply the Matching Fix",[15,494,495],{},"Remove redirects at their source. Put a CDN in front of HTML and cache it where possible. Use TLS 1.3 and enable HTTP\u002F3. Parallelise and cache backend calls. Stream or early-flush responses that cannot be cached. Address cold starts on serverless platforms.",[181,497,499],{"className":183,"code":498,"language":185,"meta":186,"style":186},"\u002F\u002F Parallelise independent backend calls instead of awaiting them one by one.\nconst [product, reviews, stock] = await Promise.all([\n  getProduct(id), getReviews(id), getStock(id),\n]);\n\u002F\u002F trade-off: Promise.all fails fast if one call rejects; use Promise.allSettled\n\u002F\u002F when partial pages are acceptable, rendering fallbacks for failed sections.\n",[188,500,501,506,543,562,567,572],{"__ignoreMap":186},[191,502,503],{"class":193,"line":194},[191,504,505],{"class":197},"\u002F\u002F Parallelise independent backend calls instead of awaiting them one by one.\n",[191,507,508,510,512,515,517,520,522,525,527,529,532,535,537,540],{"class":193,"line":201},[191,509,205],{"class":204},[191,511,209],{"class":208},[191,513,514],{"class":212},"product",[191,516,435],{"class":208},[191,518,519],{"class":212},"reviews",[191,521,435],{"class":208},[191,523,524],{"class":212},"stock",[191,526,216],{"class":208},[191,528,219],{"class":204},[191,530,531],{"class":204}," await",[191,533,534],{"class":212}," Promise",[191,536,369],{"class":208},[191,538,539],{"class":225},"all",[191,541,542],{"class":208},"([\n",[191,544,545,548,551,554,556,559],{"class":193,"line":239},[191,546,547],{"class":225},"  getProduct",[191,549,550],{"class":208},"(id), ",[191,552,553],{"class":225},"getReviews",[191,555,550],{"class":208},[191,557,558],{"class":225},"getStock",[191,560,561],{"class":208},"(id),\n",[191,563,564],{"class":193,"line":253},[191,565,566],{"class":208},"]);\n",[191,568,569],{"class":193,"line":265},[191,570,571],{"class":197},"\u002F\u002F trade-off: Promise.all fails fast if one call rejects; use Promise.allSettled\n",[191,573,574],{"class":193,"line":276},[191,575,576],{"class":197},"\u002F\u002F when partial pages are acceptable, rendering fallbacks for failed sections.\n",[15,578,579],{},[33,580,40,583,40,586,40,589,40,591,40,608,40,611,40,613,40,617,40,623,40,627,40,631,40,634,40,638,40,641,40,644,40,650,40,654,40,657,40,660,40,664,40,667,40,670,40,673,40,677,40,680,40,688,40,692,40,696,40],{"viewBox":581,"width":36,"role":37,"ariaLabel":582,"style":39},"0 0 760 149","Workflow from collecting TTFB sub-parts to applying the fix that matches the dominant component.",[42,584],{"className":585,"x":46,"y":46,"width":36,"height":36,"fill":47},[45],[49,587,588],{},"TTFB optimization workflow",[53,590,582],{},[592,593,594],"defs",{},[595,596,603],"marker",{"id":597,"viewBox":598,"refX":599,"refY":600,"markerWidth":601,"markerHeight":601,"orient":602},"fa21d94cc3","0 0 10 10","9","5","7","auto-start-reverse",[604,605],"path",{"d":606,"fill":62,"style":607},"M0 0 L10 5 L0 10 z","fill-opacity:0.7",[42,609],{"x":57,"y":57,"width":58,"height":610,"rx":60,"fill":61,"stroke":62,"style":63},"147",[65,612,588],{"x":67,"y":68,"fill":62,"style":69},[42,614],{"x":67,"y":72,"width":615,"height":616,"rx":75,"fill":113,"stroke":113,"style":114},"108.8","71.0",[65,618,622],{"x":619,"y":620,"fill":62,"style":81,"textAnchor":621},"82.4","78.0","middle","Collect",[65,624,626],{"x":619,"y":625,"fill":62,"style":87,"textAnchor":621},"96.0","Navigation Timing",[65,628,630],{"x":619,"y":629,"fill":62,"style":87,"textAnchor":621},"112.0","in RUM",[42,632],{"x":633,"y":72,"width":615,"height":616,"rx":75,"fill":113,"stroke":113,"style":114},"176.8",[65,635,637],{"x":636,"y":620,"fill":62,"style":81,"textAnchor":621},"231.2","Baseline",[65,639,640],{"x":636,"y":625,"fill":62,"style":87,"textAnchor":621},"p75 by part and",[65,642,643],{"x":636,"y":629,"fill":62,"style":87,"textAnchor":621},"region",[42,645],{"x":646,"y":72,"width":615,"height":616,"rx":75,"fill":647,"stroke":648,"style":649},"325.6","#ffc300","#b8860b","fill-opacity:0.24;stroke-opacity:0.9",[65,651,653],{"x":652,"y":620,"fill":62,"style":81,"textAnchor":621},"380.0","Isolate",[65,655,656],{"x":652,"y":625,"fill":62,"style":87,"textAnchor":621},"largest sub-part",[42,658],{"x":659,"y":72,"width":615,"height":616,"rx":75,"fill":113,"stroke":113,"style":114},"474.4",[65,661,663],{"x":662,"y":620,"fill":62,"style":81,"textAnchor":621},"528.8","Fix",[65,665,666],{"x":662,"y":625,"fill":62,"style":87,"textAnchor":621},"redirects, edge,",[65,668,669],{"x":662,"y":629,"fill":62,"style":87,"textAnchor":621},"backend",[42,671],{"x":672,"y":72,"width":615,"height":616,"rx":75,"fill":113,"stroke":113,"style":114},"623.2",[65,674,676],{"x":675,"y":620,"fill":62,"style":81,"textAnchor":621},"677.6","Verify",[65,678,679],{"x":675,"y":625,"fill":62,"style":87,"textAnchor":621},"field p75 by region",[193,681],{"x1":682,"y1":683,"x2":684,"y2":683,"stroke":62,"strokeWidth":685,"style":686,"markerEnd":687},"136.8","91.5","174.8","1.5","stroke-opacity:0.6","url(#fa21d94cc3)",[193,689],{"x1":690,"y1":683,"x2":691,"y2":683,"stroke":62,"strokeWidth":685,"style":686,"markerEnd":687},"285.6","323.6",[193,693],{"x1":694,"y1":683,"x2":695,"y2":683,"stroke":62,"strokeWidth":685,"style":686,"markerEnd":687},"434.4","472.4",[193,697],{"x1":698,"y1":683,"x2":699,"y2":683,"stroke":62,"strokeWidth":685,"style":686,"markerEnd":687},"583.2","621.2",[133,701,703],{"id":702},"deconstructing-server-time","Deconstructing Server Time",[15,705,706,707,710],{},"Server time is usually the largest and most variable component. It includes request routing (load balancers, API gateways), middleware (authentication, sessions, A\u002FB assignment, logging), data fetching (databases, internal APIs, third-party APIs), rendering (templating or SSR), and response compression. Each is a candidate for optimization, but the pattern that matters most is ",[144,708,709],{},"sequential dependencies",": three 150ms calls in sequence take 450ms; in parallel they take 150ms.",[15,712,713,715,716,719,720,723],{},[188,714,487],{}," makes this visible. The server sends a header listing named durations (",[188,717,718],{},"db;dur=45, api-reviews;dur=210, render;dur=60","), which appear in DevTools' Timing tab and are available to RUM via ",[188,721,722],{},"PerformanceServerTiming",". With RUM collection, you see the p75 of each phase in the field — often revealing that one backend dependency dominates.",[15,725,726,727,730],{},"Caching attacks server time at different levels: full-page caching at the CDN (eliminates server time for cache hits), application-level caching of rendered fragments or data, and database query caching. Full-page caching gives the largest gains; even short TTLs (60 seconds) with ",[188,728,729],{},"stale-while-revalidate"," can serve most requests from the edge for popular pages.",[133,732,734],{"id":733},"advanced-diagnostics-and-edge-cases","Advanced Diagnostics and Edge Cases",[15,736,737,740],{},[144,738,739],{},"Personalised pages."," Logged-in or personalised pages often bypass CDN caches. Separate the shared shell from personal data — cache the shell, fetch personal parts client-side or assemble at the edge.",[15,742,743,746],{},[144,744,745],{},"Cookie-dependent caching."," CDNs often bypass cache when requests carry cookies. Configure the cache key to ignore cookies that do not affect the response.",[15,748,749,752],{},[144,750,751],{},"Geographic latency."," For global audiences, a single-region origin adds 100–300ms of network latency for distant users on every uncached request. Edge caching or multi-region deployment addresses it.",[15,754,755,758],{},[144,756,757],{},"Cold caches after deploys."," Purging all HTML on deploy causes a burst of slow TTFBs. Use versioned assets so HTML can be revalidated gradually instead of purged.",[15,760,761,764],{},[144,762,763],{},"Bot traffic."," Crawlers hitting uncached low-traffic pages can load origins and slow everyone. Watch origin load and cache hit ratio together.",[133,766,768],{"id":767},"validation-and-budgeting","Validation and Budgeting",[15,770,771],{},"Validate in the field: TTFB p75 per template and region, before and after each change. In the lab, use WebPageTest from several locations to see connection and server components. Set budgets for TTFB p75 per key template and alert on regressions, plus a budget for HTML cache hit ratio.",[773,774,775,788],"table",{},[776,777,778],"thead",{},[779,780,781,785],"tr",{},[782,783,784],"th",{},"Budget",[782,786,787],{},"Target",[789,790,791,800,808,816],"tbody",{},[779,792,793,797],{},[794,795,796],"td",{},"TTFB p75 (mobile, all regions)",[794,798,799],{},"\u003C 600 ms",[779,801,802,805],{},[794,803,804],{},"HTML cache hit ratio (cacheable templates)",[794,806,807],{},"> 85%",[779,809,810,813],{},[794,811,812],{},"Navigations with redirects (entry pages)",[794,814,815],{},"\u003C 5%",[779,817,818,821],{},[794,819,820],{},"Server time p75 (uncached)",[794,822,823],{},"\u003C 300 ms",[133,825,827],{"id":826},"worked-example-a-regional-news-site","Worked Example: A Regional News Site",[15,829,830,831,834,835,838],{},"A regional news site hosted on a single origin in one region, with no CDN caching for HTML (the CDN only served assets). Mobile TTFB p75 was 1.4 seconds; LCP p75 was 3.6 seconds. Navigation Timing showed: redirects 180ms (social links pointed at ",[188,832,833],{},"http:\u002F\u002F"," URLs and a tracking domain), connect 260ms, server time 820ms (article rendering made sequential calls to a CMS API, a related-articles service and a comments count). The team updated social link templates to canonical HTTPS URLs, enabled CDN caching for article HTML with ",[188,836,837],{},"s-maxage=120, stale-while-revalidate=3600",", and parallelised the three backend calls for cache misses. TTFB p75 fell to 380ms, LCP p75 to 2.3 seconds. Cache hit ratio for article HTML settled at 93%.",[133,840,842],{"id":841},"edge-rendering-and-regional-origins","Edge Rendering and Regional Origins",[15,844,845],{},"For pages that cannot be cached, moving rendering closer to users reduces the network part of TTFB. Edge functions run near users, but if they must call a database in one region, each query pays the long round trip — sometimes making TTFB worse than rendering at the origin. The rule: compute should be close to data. Edge rendering helps when data is cached at the edge or replicated globally; otherwise, keep rendering near the data and cache what you can at the edge. Multi-region deployments with read replicas are the heavy-duty option for global, dynamic applications.",[15,847,848],{},[33,849,40,852,40,855,40,858,40,860,40,863,40,865,40,872,40,879,40,884,40,888,40,893,40,897,40,901,40,906,40],{"viewBox":850,"width":36,"role":37,"ariaLabel":851,"style":39},"0 0 760 163","Bar chart comparing TTFB for a user far from the database when rendering at the origin, at the edge with remote data, and at the edge with cached data.",[42,853],{"className":854,"x":46,"y":46,"width":36,"height":36,"fill":47},[45],[49,856,857],{},"TTFB for a personalised page by rendering location",[53,859,851],{},[42,861],{"x":57,"y":57,"width":58,"height":862,"rx":60,"fill":61,"stroke":62,"style":63},"161",[65,864,857],{"x":67,"y":68,"fill":62,"style":69},[65,866,871],{"x":867,"y":868,"fill":62,"style":869,"textAnchor":870},"222.6","70.0","font-size:13px","end","Origin render (near DB)",[42,873],{"x":874,"y":72,"width":875,"height":876,"rx":877,"fill":113,"stroke":113,"style":878},"234.6","253.2","19","3","fill-opacity:0.55;stroke-opacity:0.9",[65,880,883],{"x":881,"y":868,"fill":62,"style":882},"493.8","font-size:12px;font-weight:600","520ms",[65,885,887],{"x":867,"y":886,"fill":62,"style":869,"textAnchor":870},"101.0","Edge render, 3 remote DB queries",[42,889],{"x":874,"y":890,"width":891,"height":876,"rx":877,"fill":61,"stroke":62,"style":892},"87.0","433.4","fill-opacity:0.7;stroke-opacity:0.35",[65,894,896],{"x":895,"y":886,"fill":62,"style":882},"674.0","890ms",[65,898,900],{"x":867,"y":899,"fill":62,"style":869,"textAnchor":870},"132.0","Edge render, edge-cached data",[42,902],{"x":874,"y":903,"width":904,"height":876,"rx":877,"fill":647,"stroke":648,"style":905},"118.0","68.2","fill-opacity:0.7;stroke-opacity:0.9",[65,907,909],{"x":908,"y":899,"fill":62,"style":882},"308.8","140ms",[133,911,913],{"id":912},"measuring-ttfb-correctly","Measuring TTFB Correctly",[15,915,916,917,920],{},"Several measurement traps make TTFB look better or worse than users experience. Lab tools often run on fast connections from data centres near your origin. Synthetic tests may hit warm caches. Some RUM libraries measure TTFB from ",[188,918,919],{},"requestStart"," rather than navigation start, excluding redirects and connection setup. The web-vitals library reports TTFB from the start of navigation, which matches what users wait for. For prerendered or back\u002Fforward-cached pages, TTFB is effectively zero and should be segmented separately, otherwise improvements in speculation can mask regressions in server time.",[133,922,924],{"id":923},"connection-setup-the-overlooked-share","Connection Setup: The Overlooked Share",[15,926,927],{},"For first-time visitors on mobile, connection setup can rival server time. A DNS lookup through a slow resolver, a TCP handshake, and a TLS 1.2 handshake with a long certificate chain can take 400–600ms before the request is even sent. Several changes reduce it. TLS 1.3 removes a round trip from the handshake, and session resumption makes repeat connections cheaper still. HTTP\u002F3 combines the transport and TLS handshakes over QUIC, saving another round trip and coping better with packet loss. A CDN terminates connections close to the user, so each round trip is short even if the origin is far away. Shorter certificate chains and OCSP stapling reduce the bytes and checks in the handshake.",[15,929,930,931,934,935,937,938,941,942,945],{},"Check what your users actually negotiate: ",[188,932,933],{},"nextHopProtocol"," in Navigation Timing reports ",[188,936,133],{}," or ",[188,939,940],{},"h3",", and the connect and TLS sub-parts show how long setup took. If a large share of navigations still use HTTP\u002F2 when HTTP\u002F3 is enabled, the ",[188,943,944],{},"alt-svc"," advertisement may be missing or blocked, or users may be returning before the browser has learned that HTTP\u002F3 is available.",[133,947,949],{"id":948},"caching-strategies-for-different-page-types","Caching Strategies for Different Page Types",[15,951,952,953,956,957,959,960,963,964,967,968,971],{},"Not every page can be cached the same way, but most can be cached somehow. ",[144,954,955],{},"Static content pages"," (articles, docs, marketing) can be cached at the CDN for minutes to hours, with ",[188,958,729],{}," so updates propagate without slow requests. ",[144,961,962],{},"Listing pages"," (categories, search results for common queries) can be cached briefly, keyed by the query parameters that change the result. ",[144,965,966],{},"Product pages"," with frequently changing prices or stock can cache the page shell and fetch volatile data client-side or at the edge. ",[144,969,970],{},"Logged-in pages"," can often cache a shared shell and personalise a small part. Only truly unique, per-request responses — checkout steps, account settings — need full server rendering on every request, and those are usually a small share of traffic.",[15,973,974,975,978],{},"Measuring cache hit ratio per template makes this concrete. A template with 40% hit ratio usually has a cache key problem (cookies, query parameters, ",[188,976,977],{},"Vary"," headers) rather than truly uncacheable content.",[133,980,982],{"id":981},"ttfb-and-backforward-cache","TTFB and Back\u002FForward Cache",[15,984,985,986,989,990,993],{},"Navigations restored from the back\u002Fforward cache (bfcache) have no network request at all: the page is restored from memory instantly. Making pages eligible for bfcache (avoiding ",[188,987,988],{},"unload"," handlers, ",[188,991,992],{},"Cache-Control: no-store"," on HTML where not needed, and open connections that block it) turns a large share of back and forward navigations into zero-TTFB events. In field data, segment bfcache restores separately so they do not hide regressions in real server response times.",[133,995,997],{"id":996},"a-rollout-plan","A Rollout Plan",[999,1000,1001,1004,1007,1010,1013,1016,1019],"ol",{},[164,1002,1003],{},"Collect Navigation Timing sub-parts and Server-Timing in RUM.",[164,1005,1006],{},"Remove redirects from high-traffic entry URLs.",[164,1008,1009],{},"Enable CDN caching for HTML on cacheable templates, with stale-while-revalidate.",[164,1011,1012],{},"Turn on TLS 1.3 and HTTP\u002F3 at the CDN.",[164,1014,1015],{},"Add Server-Timing and fix the slowest backend dependencies; parallelise calls and add timeouts with fallbacks.",[164,1017,1018],{},"Stream or early-flush responses that must be rendered per request, so the head arrives first.",[164,1020,1021],{},"Set budgets per template and region, and alert on regressions.",[133,1023,1025],{"id":1024},"common-pitfalls","Common Pitfalls",[161,1027,1028,1034,1040,1046,1052],{},[164,1029,1030,1033],{},[144,1031,1032],{},"Optimising server code while ignoring redirects and connection time."," Often the bigger share for mobile users.",[164,1035,1036,1039],{},[144,1037,1038],{},"Never caching HTML."," Leaves the largest TTFB win unused.",[164,1041,1042,1045],{},[144,1043,1044],{},"Sequential backend calls."," Turns several fast calls into one slow response.",[164,1047,1048,1051],{},[144,1049,1050],{},"Edge rendering far from data."," Adds round trips instead of removing them.",[164,1053,1054,1057],{},[144,1055,1056],{},"Measuring TTFB only in the lab."," Misses geography, cache misses and real networks.",[133,1059,1061],{"id":1060},"faq","FAQ",[1063,1064,1067,1071],"details",{"className":1065},[1066],"faq-item",[1068,1069,1070],"summary",{},"What is a good TTFB?",[15,1072,1073],{},"800ms or less at p75 is considered good. Aim for under 600ms on mobile for a comfortable LCP budget; fast sites achieve 200–500ms.",[1063,1075,1077,1080],{"className":1076},[1066],[1068,1078,1079],{},"Does TTFB include redirects?",[15,1081,1082],{},"In the web-vitals definition, yes — TTFB is measured from the start of navigation, so redirect time is included.",[1063,1084,1086,1089],{"className":1085},[1066],[1068,1087,1088],{},"How do I cache HTML safely?",[15,1090,1091,1092,1095,1096,1098],{},"Cache only responses that are the same for all users (or vary by a small set of keys), set ",[188,1093,1094],{},"s-maxage"," with ",[188,1097,729],{},", and make sure cookies or personalisation do not leak into cached pages.",[1063,1100,1102,1105],{"className":1101},[1066],[1068,1103,1104],{},"What is Server-Timing?",[15,1106,1107],{},"A response header in which the server reports named durations for its work. Browsers show it in DevTools and expose it to RUM scripts.",[1063,1109,1111,1114],{"className":1110},[1066],[1068,1112,1113],{},"Does a CDN help TTFB for uncached pages?",[15,1115,1116],{},"Somewhat: CDNs terminate connections near users and often keep warm connections to the origin, which reduces connection setup time. The large gains come from caching.",[1063,1118,1120,1123],{"className":1119},[1066],[1068,1121,1122],{},"Why is TTFB slower on mobile?",[15,1124,1125],{},"Higher network latency multiplies the cost of every round trip in redirects and connection setup. Server time is usually the same.",[1063,1127,1129,1132],{"className":1128},[1066],[1068,1130,1131],{},"Can prerendering hide slow TTFB?",[15,1133,1134],{},"For prerendered navigations, users do not wait for TTFB. But entry pages and non-predicted navigations still pay it, so fix the underlying server time too.",[1063,1136,1138,1141],{"className":1137},[1066],[1068,1139,1140],{},"Does HTML size affect TTFB?",[15,1142,1143],{},"Not directly — TTFB is the arrival of the first byte. But large HTML takes longer to transfer after that, delaying discovery of resources near the end of the document. Compression and streaming help with both.",[1063,1145,1147,1150],{"className":1146},[1066],[1068,1148,1149],{},"Should I measure TTFB for API requests too?",[15,1151,1152],{},"Yes, for client-rendered pages that fetch data before rendering. Their response times behave like TTFB for the content users wait for, and Server-Timing works on API responses as well.",[133,1154,1156],{"id":1155},"guides-in-this-topic","Guides in This Topic",[161,1158,1159,1166,1173],{},[164,1160,1161,1165],{},[19,1162,1164],{"href":1163},"\u002Fnetwork-protocol-optimization\u002Ftime-to-first-byte-optimization\u002Fdiagnosing-slow-ttfb-with-server-timing\u002F","Diagnosing slow TTFB with Server-Timing"," — see where server time goes.",[164,1167,1168,1172],{},[19,1169,1171],{"href":1170},"\u002Fnetwork-protocol-optimization\u002Ftime-to-first-byte-optimization\u002Ffixing-ttfb-from-serverless-cold-starts\u002F","Fixing TTFB from serverless cold starts"," — initialisation costs on serverless platforms.",[164,1174,1175,1179],{},[19,1176,1178],{"href":1177},"\u002Fnetwork-protocol-optimization\u002Ftime-to-first-byte-optimization\u002Feliminating-redirect-chains\u002F","Eliminating redirect chains"," — remove round trips before the first byte.",[133,1181,1183],{"id":1182},"related","Related",[161,1185,1186,1193,1200],{},[164,1187,1188,1192],{},[19,1189,1191],{"href":1190},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fedge-compute-and-dynamic-caching\u002F","Edge compute & dynamic caching"," — caching pages that look uncacheable.",[164,1194,1195,1199],{},[19,1196,1198],{"href":1197},"\u002Fnetwork-protocol-optimization\u002Fstreaming-ssr-and-early-flush\u002F","Streaming SSR & early flush"," — sending bytes before the server finishes.",[164,1201,1202,1206],{},[19,1203,1205],{"href":1204},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fhttp-cache-control-headers-explained\u002Fcache-control-for-html-documents\u002F","Cache-Control for HTML documents"," — header settings for cached HTML.",[1208,1209,1211],"script",{"type":1210},"application\u002Fld+json","\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"HowTo\",\n  \"name\": \"Time to First Byte (TTFB) Optimization: Getting the Response Started\",\n  \"description\": \"How to measure and reduce Time to First Byte by attacking each of its components — redirects, DNS, TLS, server work and caching — with field data as the guide.\",\n  \"step\": [\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 1,\n      \"name\": \"Environment Setup: Collect TTFB Sub-Parts\",\n      \"text\": \"Environment Setup: Collect TTFB Sub-Parts\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 2,\n      \"name\": \"Capture a Baseline\",\n      \"text\": \"Compute p75 for each sub-part by page type and region.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 3,\n      \"name\": \"Isolate the Dominant Component\",\n      \"text\": \"If redirects are large, find the redirecting URLs (logs, campaign links, internal links).\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 4,\n      \"name\": \"Apply the Matching Fix\",\n      \"text\": \"Remove redirects at their source.\"\n    }\n  ]\n}\n",[1208,1213,1214],{"type":1210},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"TechArticle\",\n  \"headline\": \"Time to First Byte (TTFB) Optimization: Getting the Response Started\",\n  \"description\": \"How to measure and reduce Time to First Byte by attacking each of its components — redirects, DNS, TLS, server work and caching — with field data as the guide.\",\n  \"datePublished\": \"2026-10-06\",\n  \"dateModified\": \"2026-10-06\",\n  \"author\": {\n    \"@type\": \"Organization\",\n    \"name\": \"frontend-performance.com\"\n  },\n  \"publisher\": {\n    \"@type\": \"Organization\",\n    \"name\": \"frontend-performance.com\"\n  },\n  \"mainEntityOfPage\": {\n    \"@type\": \"WebPage\",\n    \"@id\": \"https:\u002F\u002Ffrontend-performance.com\u002Fnetwork-protocol-optimization\u002Ftime-to-first-byte-optimization\u002F\"\n  }\n}\n",[1208,1216,1217],{"type":1210},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"BreadcrumbList\",\n  \"itemListElement\": [\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 1,\n      \"name\": \"Home\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 2,\n      \"name\": \"Network & Server Response Optimization\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fnetwork-protocol-optimization\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 3,\n      \"name\": \"Time to First Byte (TTFB) Optimization\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fnetwork-protocol-optimization\u002Ftime-to-first-byte-optimization\u002F\"\n    }\n  ]\n}\n",[1219,1220,1221],"style",{},"html pre.shiki code .sjfSM, html code.shiki .sjfSM{--shiki-default:#66707B;--shiki-dark:#BDC4CC;--shiki-light:#66707B}html pre.shiki code .sPARh, html code.shiki .sPARh{--shiki-default:#A0111F;--shiki-dark:#FF9492;--shiki-light:#A0111F}html pre.shiki code .saISM, html code.shiki .saISM{--shiki-default:#0E1116;--shiki-dark:#F0F3F6;--shiki-light:#0E1116}html pre.shiki code .sPXB4, html code.shiki .sPXB4{--shiki-default:#023B95;--shiki-dark:#91CBFF;--shiki-light:#023B95}html pre.shiki code .smZ65, html code.shiki .smZ65{--shiki-default:#622CBC;--shiki-dark:#DBB7FF;--shiki-light:#622CBC}html pre.shiki code .sZ8jY, html code.shiki .sZ8jY{--shiki-default:#032563;--shiki-dark:#ADDCFF;--shiki-light:#032563}html pre.shiki code .sQw3B, html code.shiki .sQw3B{--shiki-default:#702C00;--shiki-dark:#FFB757;--shiki-light:#702C00}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}",{"title":186,"searchDepth":201,"depth":201,"links":1223},[1224,1225,1226,1227,1228,1229,1230,1231,1232,1233,1234,1235,1236,1237,1238,1239,1240,1241,1242,1243],{"id":135,"depth":201,"text":136},{"id":158,"depth":201,"text":159},{"id":178,"depth":201,"text":179},{"id":461,"depth":201,"text":462},{"id":468,"depth":201,"text":469},{"id":491,"depth":201,"text":492},{"id":702,"depth":201,"text":703},{"id":733,"depth":201,"text":734},{"id":767,"depth":201,"text":768},{"id":826,"depth":201,"text":827},{"id":841,"depth":201,"text":842},{"id":912,"depth":201,"text":913},{"id":923,"depth":201,"text":924},{"id":948,"depth":201,"text":949},{"id":981,"depth":201,"text":982},{"id":996,"depth":201,"text":997},{"id":1024,"depth":201,"text":1025},{"id":1060,"depth":201,"text":1061},{"id":1155,"depth":201,"text":1156},{"id":1182,"depth":201,"text":1183},"How to measure and reduce Time to First Byte by attacking each of its components — redirects, DNS, TLS, server work and caching — with field data as the guide.","md",{"slug":1247,"type":1248,"breadcrumb":1249,"datePublished":1256,"dateModified":1256},"time-to-first-byte-optimization","topic",[1250,1253,1254],{"name":1251,"url":1252},"Home","\u002F",{"name":22,"url":21},{"name":5,"url":1255},"\u002Fnetwork-protocol-optimization\u002Ftime-to-first-byte-optimization\u002F","2026-10-06",true,"\u002Fnetwork-protocol-optimization\u002Ftime-to-first-byte-optimization",{"title":1260,"description":1261},"Time to First Byte (TTFB) Optimization Guide","Break TTFB into redirects, connection setup and server time, then fix each: CDN-cached HTML, Server-Timing, backend parallelism, cold starts and redirect removal.","network-protocol-optimization\u002Ftime-to-first-byte-optimization\u002Findex","HyQfANBqJbINS7AogVcs304nJPxDBmZ0nbRR2Epopjw",[1265,1269],{"title":1266,"path":1267,"stem":1268,"children":-1},"Streaming SSR with React Suspense","\u002Fnetwork-protocol-optimization\u002Fstreaming-ssr-and-early-flush\u002Fstreaming-ssr-with-react-suspense","network-protocol-optimization\u002Fstreaming-ssr-and-early-flush\u002Fstreaming-ssr-with-react-suspense\u002Findex",{"title":1270,"path":1271,"stem":1272,"children":-1},"Diagnosing Slow TTFB with Server-Timing","\u002Fnetwork-protocol-optimization\u002Ftime-to-first-byte-optimization\u002Fdiagnosing-slow-ttfb-with-server-timing","network-protocol-optimization\u002Ftime-to-first-byte-optimization\u002Fdiagnosing-slow-ttfb-with-server-timing\u002Findex",1791308069638]