TTFB Optimization skill for Codex

Split server response time, then cut the big slice. Codex splits TTFB into redirects, DNS, connection, TLS, queueing and server phases using curl timings and Server-Timing headers, then fixes the dominant slice: redirect chains, sequential data fetching, whole-page SSR that streams nothing, cold starts, uncached anonymous HTML or an origin far from its database. It separates cache hits from misses and judges the result by LCP as well as TTFB.

  • Performance
  • $ttfb-optimization
  • 135 lines
  • 2 files
  • ~1,600 tokens
  • MIT
  • Updated

Install

curl -fsSL codexskills.dev/install.sh \  | sh -s -- ttfb-optimization

Installs to ~/.agents/skills/ttfb-optimization/ for every project on this machine.

104-line POSIX script. No sudo, no telemetry. Read install.sh

Use it in Codex

Start a prompt with $ttfb-optimization, or pick it from /skills. You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.

What Codex reads before loading it

Reduces Time to First Byte for HTML and API responses by breaking it into redirects, DNS, connection, TLS and server time, instrumenting the server with Server-Timing, then fixing the dominant part - edge caching of HTML, streaming SSR with early flush, parallelised data fetching, connection reuse, cold starts and region placement, plus 103 Early Hints. Use when TTFB is above 800 ms, LCP is slow because the document arrives late, SSR pages feel sluggish, PageSpeed flags document latency, or a serverless or edge deployment responds slowly.

Use it when

  • Field TTFB p75 is above 800 ms or document latency is flagged
  • LCP is slow because the HTML itself arrives late
  • SSR, serverless or edge-rendered pages respond slowly

Use something else when

How to check the result

Median curl timings and Server-Timing phases for the same URLs drop, cache hits and misses are reported separately, and field p75 TTFB and LCP improve after release.

The mistake it prevents

Shrinking TTFB by shipping an empty shell and fetching data client-side, which looks good on TTFB and makes LCP worse.

~/.agents/skills/ttfb-optimization/SKILL.md

--- name: ttfb-optimization description: Reduces Time to First Byte for HTML and API responses by breaking it into redirects, DNS, connection, TLS and server time, instrumenting the server… license: MIT ---

Done well means: you know which slice of TTFB dominates for real users (network, queueing or server work), the fix targets that slice, and p75 TTFB for the affected pages drops into the good range (≤ 800 ms field p75, aim for ≤ 200 ms server time on cache misses and near-zero on edge hits).

Break TTFB down

TTFB = redirects + service worker startup + DNS + TCP + TLS + request queueing + server processing + first byte transit.

curl -o /dev/null -s -w 'dns=%{time_namelookup} connect=%{time_connect} tls=%{time_appconnect} ttfb=%{time_starttransfer} total=%{time_total} redirects=%{num_redirects}\n' -L https://example.com/products/1

Run it 5+ times and from a region near real users (network needed). In the browser, DevTools Network > the document request > Timing tab shows the same split. Field data: Navigation Timing (responseStart - startTime) via web-vitals onTTFB attribution ($rum-monitoring).

Instrument server time with Server-Timing, which DevTools displays per request:

// Express-style middleware: add named phases as you measure them
res.setHeader('Server-Timing', [
  `auth;dur=${t.auth.toFixed(1)}`,
  `db;dur=${t.db.toFixed(1)};desc="orders query"`,
  `render;dur=${t.render.toFixed(1)}`,
  `cache;desc="${cacheStatus}"`,
].join(', '));

Expose only coarse timings publicly (they can leak internals); gate detailed ones behind an internal header or environment.

Workflow

- [ ] 1. Field p75 TTFB per page type and region; lab breakdown with curl / DevTools
- [ ] 2. Classify: redirect, connection, queueing, server work, or cache miss rate
- [ ] 3. Add Server-Timing (or use traces) to split server time into phases
- [ ] 4. Fix the largest slice (table below); one change at a time
- [ ] 5. Re-measure the same URLs from the same location; check cache hit ratio
- [ ] 6. Confirm field p75 after release

Fix by slice

Dominant sliceTypical causeFix
Redirectshttp→https, apex→www, trailing slash, locale redirect chainsLink to final URLs; HSTS preload; at most one redirect
DNS / connect / TLSFar origin, no CDN, TLS 1.2 without resumption, new connection per requestCDN in front, TLS 1.3, HTTP/2 or HTTP/3, keep-alive to origin
QueueingServer saturated, too few workers, event loop blockedProfile ($node-profiling, $python-profiling), scale workers, fix blocking code
Server: data fetchingSequential queries/API calls, slow queries, N+1Parallelise independent calls, fix queries ($database-performance-triage), cache results
Server: renderingHeavy SSR of the whole page before sending anythingStream HTML; render shell first; move non-critical sections behind Suspense
Cold startsServerless function init, large bundles, VPC attachSmaller bundles, provisioned/min instances, lazy imports of rarely used SDKs
Cache missHTML not cached anywhere; personalised pages blocking cachingEdge-cache anonymous HTML with s-maxage + stale-while-revalidate; personalise client-side or with edge includes
RegionOrigin and database far apart, or far from usersCo-locate app and DB; regional or edge rendering for read-mostly pages

Parallelise data fetching

// Before: three sequential round trips (~90 + 120 + 70 ms)
const user = await getUser(id);
const orders = await getOrders(id);
const recs = await getRecommendations(id);

// After: independent calls in parallel (~120 ms)
const [user, orders, recs] = await Promise.all([getUser(id), getOrders(id), getRecommendations(id)]);

Watch for the same waterfall across layouts and components in React Server Components: each await in a parent before rendering children serialises fetches. Start promises early and pass them down, or use Suspense boundaries.

Stream the document

  • React: renderToPipeableStream (Node) / renderToReadableStream (Web streams), with slow sections in <Suspense>; Next.js App Router streams by default with loading.tsx and Suspense.
  • Send <head> with CSS and preload hints before slow data resolves, so the browser starts fetching critical resources while the server works.
  • Middleware that buffers the full response (some compression or logging layers, proxies with buffering on) defeats streaming; check that bytes arrive incrementally (curl -N and watch output timing).

103 Early Hints

When server time is unavoidable, send 103 Early Hints with Link: <...>; rel=preload or rel=preconnect for critical CSS, fonts and the LCP image. Supported by Chromium browsers and several CDNs (Cloudflare, Fastly, others) that can generate them from the final response’s Link headers. It does not reduce TTFB itself; it overlaps work with it.

Caching HTML

Anonymous HTML is the biggest TTFB win: an edge hit is tens of milliseconds. Use Cache-Control: public, max-age=0, s-maxage=60, stale-while-revalidate=600 (or the platform’s ISR/use cache features) and purge or tag-invalidate on content change. Header semantics are in $http-caching; CDN keys and purging in $cdn-caching; Next.js specifics in $nextjs-caching. Changing production CDN rules needs the user’s approval.

Hard rules

  • Measure from where users are, on the URLs they hit. A localhost TTFB says nothing about DNS, TLS or region.
  • Separate cache hits from misses in every measurement; an average mixes two populations.
  • Never cache responses that vary by session or cookie at a shared cache.
  • Lowering TTFB by sending an empty shell and fetching everything client-side often makes LCP worse. Judge by LCP, not TTFB alone.

Verification

  • curl breakdown (median of 5) and Server-Timing phases before vs after for the same URLs.
  • CDN hit ratio for HTML (if caching was added) and TTFB on hits vs misses.
  • Field p75 TTFB and LCP for the page group after release (RUM within days; CrUX over 28 days).

Report template

## TTFB: <page type> (<region>)
Field p75: TTFB 1.6 s, LCP 3.4 s (RUM, mobile, last 7 days)
Lab breakdown (median of 5, from <region>): dns 20 | connect 45 | tls 60 | server 1,310 ms
Server-Timing: auth 40 | db 890 (3 sequential queries) | render 310
Fixes: parallelised queries (db 890 -> 330), streamed product page shell, edge cache for anonymous traffic (s-maxage=60, swr=600)
After: server 420 ms on miss, 35 ms TTFB on edge hit, hit ratio 82%
Pending: field confirmation on <date>

What the zip contains

SKILL.md plus the interface file below. Nothing runs on install.

agents/openai.yamlgenerated from this page

Gives the Codex app a display name, a one-line description and a starter prompt for this skill.

interface:
  display_name: "TTFB Optimization"
  short_description: "Split server response time, then cut the big slice"
  default_prompt: "$ttfb-optimization Our product pages have a 1.8 second TTFB, find out where the time goes"

About this skill

Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.

esc
Just launched

Codex Skills Pro is live

79 premium skills for Codex, each with scripts, references and an evidence report that proves the result.

Bought one by one$882.21
All 79 skills$49
94% off

That's $0.62 a skill, paid once.

Get all 79 for $49Or pick single skills from $4.99 →

Secure checkout by Creem