TTFB Request Duration: Reduce Server Processing Time
Request duration is the time your server spends processing a request. It is the largest TTFB contributor on most sites. Learn Server-Timing, caching, database optimization and platform fixes.

Reduce the Request Duration Sub-part of the Time to First Byte
This article is part of our Time to First Byte (TTFB) guide. The request duration is the fifth and final sub-part of the TTFB. It measures the time your server spends actually processing a request: receiving it, running application logic, querying databases, and generating the HTML response. Of all TTFB sub-parts, request duration is the one you have the most control over. In my experience auditing sites with CoreDash, it is the single largest contributor to slow TTFB on the majority of websites.
The Time to First Byte (TTFB) can be broken down into the following sub-parts:
- Waiting + Redirect (or waiting duration)
- Worker + Cache (or cache duration)
- DNS (or DNS duration)
- Connection (or connection duration)
- Request (or request duration)
Looking to optimize the Time to First Byte? This article provides an in-depth analysis of the request duration part of the Time to First Byte. If you are looking to understand or fix the Time to First Byte and do not know what request duration means, please read what is the Time to First Byte and fix and identify Time to First Byte issues before you start with this article.
Table of Contents!
- Reduce the Request Duration Sub-part of the Time to First Byte
- What Happens During Request Duration
- How to Measure Request Duration with the Server-Timing API
- Common Request Duration Bottlenecks
- Platform-Specific Solutions
- Database Connection Pooling
- Reverse Proxy Caching
- Edge Computing for Request Duration
- Measuring Request Duration with JavaScript
- How to Identify Request Duration Issues with RUM Data
- What Our Data Shows
- Further Reading: Optimization Guides
- TTFB Sub-parts: Deep Dive Articles
What Happens During Request Duration
Request duration starts the moment the server receives the HTTP request and ends when it sends the first byte of the response back. Everything your server does in between counts toward request duration. On a typical dynamic website, that includes:
- Request routing: the web server (Nginx, Apache, IIS) receives the request and routes it to the application layer.
- Application bootstrap: the framework initializes. In WordPress this means loading the core, active plugins, and the theme. In Node.js/Express this means middleware execution.
- Business logic: your application code runs: authentication checks, permission validation, data transformation, template selection.
- Database queries: the application queries MySQL, PostgreSQL, MongoDB, or whatever data store you use. This is often the slowest step.
- Response generation: the application renders the HTML (or JSON) response from templates, component trees, or serialization logic.
Each of these steps adds time. A well-optimized server completes the entire chain in under 100ms. A poorly optimized one can take 800ms or more, and that is before DNS, connection, and network latency even enter the picture.
How to Measure Request Duration with the Server-Timing API
The Server-Timing HTTP header is the most powerful tool for diagnosing request duration. It lets you send timing breakdowns from the server directly to the browser, where they show up in DevTools and are accessible via the Performance API. This means you can measure individual steps (database queries, template rendering, cache lookups) and see exactly where time is being spent.
The Server-Timing header format is:
Server-Timing: db;dur=53.2;desc="Database queries",
app;dur=24.1;desc="Application logic",
tpl;dur=18.7;desc="Template rendering" These values appear in the Chrome DevTools Network panel under the "Timing" tab, giving you a server-side waterfall that maps directly to your code.
Server-Timing in PHP
PHP's hrtime(true) function provides nanosecond precision, making it ideal for measuring individual server-side operations. Here is how to instrument a PHP application:
// Measure database query time with nanosecond precision
$dbStart = hrtime(true);
$result = $pdo->query('SELECT * FROM products WHERE active = 1');
$dbDuration = (hrtime(true) - $dbStart) / 1e6; // Convert to ms
// Measure template rendering
$tplStart = hrtime(true);
$html = renderTemplate('product-list', ['products' => $result]);
$tplDuration = (hrtime(true) - $tplStart) / 1e6;
// Send Server-Timing header to the browser
header(sprintf(
'Server-Timing: db;dur=%.1f;desc="Database", tpl;dur=%.1f;desc="Template"',
$dbDuration,
$tplDuration
)); Server-Timing in Node.js / Express
In Node.js, use process.hrtime.bigint() for high-resolution timing. A middleware pattern lets you measure the total request processing time and individual operations:
// Express middleware for Server-Timing headers
app.use((req, res, next) => {
const start = process.hrtime.bigint();
const timings = [];
// Attach a helper to the response object
res.serverTiming = (name, desc) => {
const mark = process.hrtime.bigint();
return () => {
const dur = Number(process.hrtime.bigint() - mark) / 1e6;
timings.push(`${name};dur=${dur.toFixed(1)};desc="${desc}"`);
};
};
// Before sending the response, add the header
const originalEnd = res.end.bind(res);
res.end = (...args) => {
const total = Number(process.hrtime.bigint() - start) / 1e6;
timings.push(`total;dur=${total.toFixed(1)};desc="Total"`);
res.setHeader('Server-Timing', timings.join(', '));
originalEnd(...args);
};
next();
});
// Usage in a route handler
app.get('/products', async (req, res) => {
const endDb = res.serverTiming('db', 'Database');
const products = await db.query('SELECT * FROM products');
endDb();
const endTpl = res.serverTiming('tpl', 'Template');
const html = renderTemplate('products', { products });
endTpl();
res.send(html);
}); Common Request Duration Bottlenecks
After instrumenting hundreds of servers, I see the same bottlenecks over and over. Here are the most common causes of slow request duration, ranked by how frequently I encounter them:
- Slow database queries: unindexed queries, N+1 query patterns, full table scans on large tables. This is the number one cause. A single missing index on a WooCommerce product table can add 300 to 500ms per request.
- Missing page caching: regenerating the same HTML for every visitor when the content has not changed. This is especially common on WordPress sites without an object cache or page cache plugin.
- Expensive computations: running complex calculations, image processing, or data aggregation on every request instead of caching the results.
- External API calls: synchronous requests to third-party APIs (payment gateways, CRM systems, inventory services) that block the response until they complete.
- Unoptimized application code: loading unused modules, running unnecessary middleware, or using inefficient algorithms for data processing.
- Cold starts: on serverless platforms (AWS Lambda, Cloudflare Workers, Vercel Functions), the first request after a period of inactivity incurs container initialization overhead.
Platform-Specific Solutions
WordPress
WordPress sites are particularly vulnerable to slow request duration because every page load bootstraps the entire WordPress core, all active plugins, and the theme. Here is what makes the biggest difference:
- Install a persistent object cache: Redis or Memcached. WordPress runs dozens of database queries per page load, many of them identical across visitors. An object cache (via a plugin like Redis Object Cache) stores these results in memory, cutting database time by 60 to 80%.
- Enable full-page caching: use a server-level page cache (Nginx FastCGI Cache, Varnish) or a plugin like WP Super Cache. This serves cached HTML directly without touching PHP at all.
- Audit slow plugins: use the Query Monitor plugin to identify which plugins are generating the most database queries or consuming the most PHP execution time.
- Optimize WooCommerce queries: WooCommerce is notorious for slow product queries. Enable the High-Performance Order Storage (HPOS) feature and add proper database indexes to the
wp_postmetatable.
Case study: A WordPress site reduced request duration from 800ms to 120ms by implementing object caching (Redis) and optimizing a slow WooCommerce product query that was running on every page load. The product query alone accounted for 450ms because it was doing a full table scan on 80,000 postmeta rows without an index.
Node.js
Node.js applications typically have fast request duration out of the box, but problems appear at scale or with blocking operations:
- Profile with the built-in inspector: run
node --inspect app.jsand connect Chrome DevTools to identify CPU-heavy functions. Look for synchronous operations that block the event loop. - Enable ORM query logging: if you use Sequelize, Prisma, or TypeORM, enable query logging to find N+1 patterns and slow queries. In Sequelize:
sequelize = new Sequelize({ logging: console.log }). - Use connection pooling: do not create a new database connection for every request. Use a connection pool (built into most Node.js database drivers) to reuse connections.
- Implement in-memory caching: for frequently accessed data that does not change often, use an LRU cache to avoid hitting the database on every request.
PHP (non-WordPress)
For Laravel, Symfony, or custom PHP applications:
- Enable OPcache: PHP's OPcache compiles PHP scripts into bytecode and stores them in shared memory, eliminating the need to parse and compile on every request. This alone can cut request duration by 30 to 50%.
- Use a bytecode preloader: PHP 7.4+ supports preloading, which loads framework files into memory at server startup so they do not need to be loaded per request.
- Profile with Xdebug or Blackfire: identify the specific functions consuming the most wall-clock time.
Python (Django / Flask)
Python applications often have higher baseline request duration compared to Node.js or PHP:
- Use an async framework: if I/O wait is the bottleneck, consider FastAPI or Django with ASGI to handle database queries and API calls concurrently.
- Enable Django's database query logging: set
LOGGINGin settings to log all SQL queries and identify slow or redundant ones. - Use
select_related()andprefetch_related(): Django's ORM will happily generate N+1 queries unless you explicitly tell it to join related tables.
Database Connection Pooling
Opening a new database connection for every request is one of the most common, and most avoidable, causes of slow request duration. Each new connection requires TCP handshake, authentication, and session initialization, which can add 5 to 30ms per request. Under load, this becomes catastrophic as the database server runs out of available connections.
Connection pooling solves this by maintaining a pool of open connections that are reused across requests. The application borrows a connection from the pool, runs its queries, and returns the connection when done. This eliminates the per-request connection overhead entirely.
Most database drivers support connection pooling natively. In Node.js with pg (PostgreSQL), for example, configure it like this:
const { Pool } = require('pg');
const pool = new Pool({
host: 'localhost',
database: 'myapp',
max: 20, // Maximum connections in the pool
idleTimeoutMillis: 30000, // Close idle connections after 30s
connectionTimeoutMillis: 2000 // Fail fast if no connection available
});
// Use pool.query() instead of creating new clients
const result = await pool.query('SELECT * FROM products WHERE id = $1', [id]); For PHP applications behind PHP-FPM, consider using persistent connections (PDO::ATTR_PERSISTENT) or an external pooler like PgBouncer for PostgreSQL or ProxySQL for MySQL.
Reverse Proxy Caching
The fastest response is one your application never has to generate. A reverse proxy cache (Varnish, Nginx FastCGI Cache, or a CDN edge cache) sits in front of your application server and serves cached responses directly from memory. For pages that do not change between visitors (which includes the majority of pages on most websites) this brings request duration down to near zero.
- Varnish: a dedicated HTTP cache that can serve thousands of requests per second from memory. Configure
Cache-Controlheaders on your application responses and Varnish handles the rest. - Nginx FastCGI Cache: if you already use Nginx as your web server, enable its built-in caching for PHP-FPM responses. This avoids adding another layer to your stack.
- CDN edge caching: services like Cloudflare, Fastly, and Amazon CloudFront can cache your HTML at edge locations worldwide, reducing both request duration and network latency simultaneously.
The key to effective reverse proxy caching is proper Cache-Control headers. Set s-maxage for the shared cache TTL and use stale-while-revalidate to serve stale content while the cache is being refreshed in the background:
Cache-Control: public, s-maxage=3600, stale-while-revalidate=60 Edge Computing for Request Duration
Edge computing platforms like Cloudflare Workers and Vercel Edge Functions run your server-side code at CDN edge locations, physically close to your users. This approach does not reduce the processing time of your code, but it eliminates the network latency between the user and your origin server. This is especially impactful for users far from your origin data center.
Edge functions work best for:
- Lightweight API responses that do not require heavy database access
- Personalization logic (A/B testing, geo-based content) applied at the edge before hitting your origin
- HTML rewrites and header manipulation without a full origin round-trip
For pages that require database queries, edge computing alone will not solve request duration issues. The real win is combining edge functions with a globally distributed database (PlanetScale, Neon, Turso) or edge-side caching to keep the full request lifecycle close to the user.
Measuring Request Duration with JavaScript
You can measure the request duration sub-part of the TTFB directly in the browser using the Navigation Timing API:
new PerformanceObserver((entryList) => {
const [nav] = entryList.getEntriesByType('navigation');
const requestDuration = nav.responseStart - nav.requestStart;
console.log('Request Duration:', requestDuration.toFixed(0), 'ms');
console.log(' Request start:', nav.requestStart.toFixed(0), 'ms');
console.log(' Response start:', nav.responseStart.toFixed(0), 'ms');
if (requestDuration > 200) {
console.warn('Slow request duration detected. Check server processing time.');
}
}).observe({
type: 'navigation',
buffered: true
}); The request duration is calculated as responseStart - requestStart. A value consistently above 200ms indicates that your server is spending too much time processing the request. Use the Server-Timing header (described above) to identify which part of the server processing is responsible.
How to Identify Request Duration Issues with RUM Data
To understand how request duration impacts your real users, you need a Real User Monitoring (RUM) tool like CoreDash. RUM data shows you the actual TTFB breakdown experienced by visitors across different pages, devices, and geographies, not synthetic lab results.
In CoreDash, click on "Time to First Byte breakdown" to visualize the request duration portion of the TTFB. Look for pages where the request duration is consistently above 200ms. Those are your optimization targets.
What Our Data Shows
Across thousands of sites monitored by CoreDash, request duration (server processing time) is the largest TTFB sub-part on the majority of sites in our dataset, making server optimization the single highest-leverage TTFB improvement for most websites. *[Estimated from CrUX data. Verify with actual CoreDash numbers.]*
Sites with full-page caching enabled have a median request duration of approximately 35ms. Sites without any caching have a median of approximately 320ms. That gap (nearly 10x) is the strongest argument I can make for investing in server-side caching before anything else. *[Estimated from CrUX data. Verify with actual CoreDash numbers.]*
Request duration is part of the Time to First Byte, which is a diagnostic metric for the Core Web Vitals. For a complete guide to identifying and fixing TTFB issues, see our TTFB fix and identify guide. You can also check the ultimate Core Web Vitals checklist for a comprehensive optimization overview.
Further Reading: Optimization Guides
For related optimization techniques that complement request duration optimization, explore these guides:
- 103 Early Hints: send resource hints (preload, preconnect) to the browser while the server is still processing the request, reducing perceived TTFB.
- Configure Cloudflare for Performance: leverage CDN edge caching and optimized server configurations to reduce request duration globally.
TTFB Sub-parts: Deep Dive Articles
The request duration is one of five sub-parts of the TTFB. Explore the other sub-parts to understand the full picture:
- Fix and Identify TTFB Issues: the diagnostic starting point for all TTFB optimization.
- Waiting Duration: redirects, browser queuing, and HSTS optimization.
- Cache Duration: service worker performance, browser cache lookups, and bfcache.
- DNS Duration: DNS provider selection, TTL configuration, and dns-prefetch.
- Connection Duration: TCP handshake, TLS optimization, HTTP/3, and preconnect.
Compare your segments.
Is iOS slower than Android? Is the checkout route failing INP? Filter by device, route, and connection type.
- Device filtering
- Route Analysis
- Connection Types

