Durée de la requête du TTFB : réduire le temps de traitement serveur
La part serveur du TTFB : comment l'appellent DevTools et web-vitals, comment Server-Timing montre l'étape lente et pourquoi le cache de page passe en premier.
- Bon ≤ 0,8 s
- À améliorer ≤ 1,8 s
- Mauvais > 1,8 s
Partager
Durée de la requête en bref
La durée de la requête est la dernière des cinq parties du Time to First Byte (TTFB) : elle va de l'envoi de la requête HTML jusqu'à l'arrivée de son premier octet. C'est le temps de génération de la page par le serveur, plus un aller-retour sur le réseau. Sur la plupart des sites, c'est la plus grande part du TTFB : réduisez-la en premier, en commençant par un cache de page.
Qu'est-ce que la durée de la requête, et correspond-elle à « Waiting (TTFB) » dans DevTools ?
La durée de la requête correspond à ce que Chrome DevTools affiche dans l'onglet Timing du panneau Network sous « Waiting for server response », et dans les anciennes versions « Waiting (TTFB) » : un aller-retour de latence, plus le temps mis par le serveur pour préparer la réponse. La bibliothèque web-vitals rapporte cette même part sous requestDuration, CoreDash sous Request dans sa répartition par phases, et Lighthouse sous l'intitulé server response time.
Elle commence dès que la connexion au serveur est établie et se termine quand le premier octet du HTML atteint le navigateur. Avant elle interviennent les quatre autres parties du Time to First Byte : attente, cache, DNS et connexion. Cette phase d'attente correspond surtout aux redirections, avant la requête ; ce n'est pas le « Waiting » de DevTools.
La requête constituait la part la plus importante du TTFB au 75e percentile sur 82 des 86 sites CoreDash comptant au moins 1 000 chargements de page sur mobile au cours des 28 jours précédant le 9 novembre 2026.
Que fait le serveur pendant que le navigateur attend ?
La requête part vers le serveur et le premier octet revient ; entre les deux, le serveur génère la page :
- Routage : le serveur web, comme nginx ou Apache, transmet la requête à l'application.
- Démarrage : l'application charge son code ; WordPress charge son cœur, chaque plugin actif et le thème pour chaque page générée.
- Données : l'application interroge sa base de données et appelle d'autres services, comme une API de stock ou de prix.
- HTML : le template transforme ces données en page.

Une première visite sur une page WordPress non mise en cache, mesurée dans Chrome. Sur les 875 ms de TTFB, la requête a pris 657 ms.
Lors de ce chargement, la connexion a pris 20 ms pour deux allers-retours (TCP et TLS) ; un aller-retour durait donc environ 10 ms. Sur les 657 ms, environ 645 ms ont servi au serveur pour générer la page. Près du serveur, la durée de la requête correspond presque uniquement au temps serveur ; un visiteur éloigné subit l'aller-retour en plus.
Rien ne peut s'afficher avant l'arrivée du premier octet du HTML : la durée de la requête s'ajoute donc aussi au Largest Contentful Paint.
Quelle étape est lente sur le serveur ? Server-Timing le montre
L'en-tête Server-Timing transmet les temps de traitement propres au serveur avec le HTML, à raison d'une entrée par étape : la base de données, le template, une API externe, un HIT ou un MISS de cache. Chrome DevTools affiche ces entrées dans l'onglet Timing de la requête HTML, sous « Waiting for server response », et un outil de RUM (real-user monitoring) les collecte auprès de vos visiteurs.
Server-Timing: db;dur=53.2;desc="Database", tpl;dur=18.7;desc="Template", cache;desc="MISS"

Un chargement sur une installation locale de test WordPress, ralenti par l'ouverture de DevTools : les deux entrées Server-Timing, 27,8 et 27,1 ms, s'inscrivent bien dans les 59 ms passées par le navigateur à attendre le serveur.
Quiconque ouvre DevTools peut lire cet en-tête. N'envoyez que des noms d'étapes et des durées, jamais de requête, de chemin de fichier ou de nom d'hôte interne ; sinon, réservez l'en-tête aux collaborateurs connectés (MDN).
Server-Timing en PHP
Mesurez chaque étape avec hrtime(true) et envoyez l'en-tête avant la page :
$t = hrtime(true);
$products = $pdo->query('SELECT * FROM products WHERE active = 1')->fetchAll();
$db = (hrtime(true) - $t) / 1e6;
$t = hrtime(true);
$html = renderTemplate('product-list', ['products' => $products]);
$tpl = (hrtime(true) - $t) / 1e6;
header(sprintf('Server-Timing: db;dur=%.1f;desc="Database", tpl;dur=%.1f;desc="Template"', $db, $tpl));
echo $html;
PHP envoie ses en-têtes dès que le premier contenu quitte le serveur. Tout appel à header() après cela est ignoré avec un avertissement « headers already sent ». Un template qui écrit au fil de l'eau nécessite donc de l'output buffering.
Server-Timing avec Node.js et Express
Dans Express, définissez l'en-tête avant res.send() :
app.get('/products', async (req, res) => {
const t0 = performance.now();
const products = await db.query('SELECT * FROM products WHERE active = 1');
const t1 = performance.now();
const html = renderTemplate('product-list', { products });
const t2 = performance.now();
res.set('Server-Timing',
`db;dur=${(t1 - t0).toFixed(1)};desc="Database", tpl;dur=${(t2 - t1).toFixed(1)};desc="Template"`);
res.send(html);
});
Node envoie les en-têtes dès le premier res.write(). Sur une route en streaming, définissez Server-Timing avant la première écriture : plus tard, Node lèvera une erreur ERR_HTTP_HEADERS_SENT.
Comment mesurer la durée de la requête ?
Pour vos visiteurs, la durée de la requête s'obtient via le RUM (real-user monitoring) : CoreDash affiche Request dans la répartition par phases de sa page TTFB, par page, et la bibliothèque web-vitals rapporte requestDuration dans son attribution du TTFB.
Google ne fixe aucun seuil pour la durée de la requête ; l'avertissement Document request latency de Lighthouse signale une réponse serveur supérieure à 600 ms. Sur l'ensemble des chargements de page sur mobile des 133 sites CoreDash disposant de données mobiles au cours des 28 jours précédant le 9 novembre 2026, la phase de requête atteignait 404 ms au 75e percentile.
Dans votre propre code, la bibliothèque web-vitals donne aussi les entrées Server-Timing reçues avec la page :
import { onTTFB } from 'web-vitals/attribution';
onTTFB(({ value, attribution }) => {
console.log('TTFB', Math.round(value), 'ms');
console.log('Request duration', Math.round(attribution.requestDuration), 'ms');
const nav = attribution.navigationEntry;
if (nav) {
if (nav.finalResponseHeadersStart) { // Chrome 133 and later
console.log('Final response headers at', Math.round(nav.finalResponseHeadersStart), 'ms');
}
console.table((nav.serverTiming || []).map(({ name, duration }) => ({ name, duration })));
}
});
Ces deux valeurs proviennent de l'API Navigation Timing : le TTFB correspond au responseStart de l'entrée de navigation, et web-vitals calcule requestDuration en retranchant connectEnd au TTFB. La liste serverTiming de l'entrée contient les éléments de l'en-tête sous forme d'objets PerformanceServerTiming (l'API Server-Timing dans le navigateur), ce qui permet à un script de RUM de les remonter à chaque page vue.

CoreDash, le TTFB sur mobile découpé en cinq parties sur acme-pagespeed.com : repérez la barre la plus longue, ici Request.
Les 103 Early Hints font paraître la durée de la requête très courte. Chrome compte le statut 103 comme premier octet, si bien que le TTFB et la durée de la requête se terminent dès l'arrivée du hint, pendant que le serveur est encore en train de générer la page. Shopify les envoie, tout comme Cloudflare avec l'option Early Hints active. La propriété finalResponseHeadersStart de l'entrée de navigation ou une entrée Server-Timing indique le vrai temps serveur ; 103 Early Hints détaille la configuration.
Un APM, ou la métrique http.server.request.duration d'OpenTelemetry, mesure l'activité à l'intérieur de l'application, sans le réseau ni les files d'attente en amont : le RUM affiche donc une durée de requête plus élevée. Un chargement dans DevTools (« Waiting for server response ») ne représente qu'un seul cache HIT ou MISS depuis votre propre machine ; les field data intègrent chaque MISS et chaque visiteur éloigné. Comment identifier et corriger un TTFB lent montre comment repérer les pages et les visiteurs dont la phase de requête est lente.
Pourquoi le serveur est-il lent à envoyer le premier octet ?
Sur la plupart des sites, un TTFB lent signifie que le serveur régénère la page pour chaque visiteur : il n'y a pas de cache de page, ou le visiteur le contourne. Les pages impossibles à mettre en cache sont lentes en raison des opérations qu'elles effectuent : une requête lente en base, l'attente d'une API externe, un manque de CPU.
| Ce que vous observez | Cause fréquente | Où regarder |
|---|---|---|
| Lenteur sur chaque page, sans être connecté | Pas de cache de page, ou échec du cache (MISS) | L'en-tête de cache : HIT ou MISS |
| Lenteur connecté ou avec un panier | La page ne peut pas être mise en cache | Server-Timing |
| Lenteur sur les liens d'annonces et d'e-mails | Paramètres d'URL absents du cache | L'URL avec et sans ?gclid= |
| Lenteur sur la recherche et les pages de compte | Requête lente ou appel externe | Server-Timing, Query Monitor |
| Rapide à proximité, lent à distance | L'aller-retour jusqu'au serveur | Le RUM par pays |
| Ralentissement du jour au lendemain | Un déploiement ou un cache cassé | L'évolution de la requête dans le RUM |
| Lenteur après une heure de calme | Un cache expiré ou un cold start | Le premier chargement comparé au second |
Comment réduire la durée de la requête ?
Commencez par un cache de page. Une copie stockée de la page évite le travail du serveur ; or, sur la plupart des sites, ce travail représente la plus grande part d'un Time to First Byte (TTFB) lent. Pour les pages qui ne peuvent pas être mises en cache, c'est le serveur lui-même qui doit devenir plus rapide.
WordPress et WooCommerce
Activez le cache de page dans WP Rocket, LiteSpeed Cache ou chez votre hébergeur. Mon plugin, WP Core Web Vitals, envoie la page mise en cache avant même le chargement de WordPress et des autres plugins, soit environ cinq fois plus vite que si la page était servie depuis WordPress.
Vérifiez le résultat sans être connecté, dans une fenêtre privée : les utilisateurs connectés, un cookie de panier et des paramètres d'URL inconnus du cache contournent le cache.
Sur une installation locale de test WordPress avec un thème par défaut, le cache de page a fait passer la phase de requête de la page d'accueil de 31 ms à environ 1 ms (médiane sur 15 chargements chacun). Un site réel met plus de temps à générer une page ; sa version en cache revient presque aussi vite.

Une installation locale n'implique aucun réseau : les deux barres correspondent uniquement au travail du serveur.
Un site à espace membre où tout le monde est connecté, ou une boutique où la majorité des visiteurs ont un panier, ne tire pas grand-chose d'un cache de page : chaque requête y mobilise PHP et la base de données. Un cache d'objets comme Redis conserve en mémoire les résultats de requêtes répétées ; chez beaucoup d'hébergeurs, il suffit d'une option à activer ou d'une demande au support. Query Monitor liste les requêtes en base lentes et le plugin dont elles proviennent.
Shopify
Le serveur, son cache et le CDN appartiennent à Shopify. Ce qui vous appartient, c'est le code Liquid du thème et ses blocs d'applications, que Shopify exécute sur ses serveurs. Shopify Theme Inspector pour Chrome montre quelle section ou quel snippet prend du temps : allégez-le, ou remplacez l'application qui l'ajoute.
Un cache de page en amont de l'application
Un reverse proxy, le CDN ou un cache qui répond avant le démarrage de votre framework évite le travail de l'application. Cet en-tête permet à un CDN ou à un proxy de conserver le HTML pendant dix minutes et de le servir une minute de plus pendant qu'il récupère une version fraîche, tout en obligeant le navigateur à redemander la page à chaque visite :
Cache-Control: public, max-age=0, s-maxage=600, stale-while-revalidate=60
Je n'ai pas conçu mon plugin WordPress pour servir le fichier en cache directement depuis nginx ou Apache : ce ne serait guère plus rapide, et chaque contrôle de cookies et de paramètres d'URL devrait être dupliqué dans la configuration du serveur.
Un Set-Cookie sur chaque réponse, pour une session ou un test A/B, empêche souvent la mise en cache sur le CDN. Vary: Cookie réclame une copie distincte par valeur de cookie, ce qui n'engendre presque aucun HIT avec des cookies d'analytique. Chaque nouvelle valeur de gclid ou d'utm_source issue d'une annonce représente une nouvelle URL, sauf si le cache ignore ces paramètres. Pour chronométrer le serveur hors cache, chargez la page avec un paramètre que vos caches n'ignorent pas, tel que ?test= avec une nouvelle valeur à chaque fois, et vérifiez que chaque en-tête de cache affiche MISS.
Un CDN qui met en cache le HTML
Un CDN qui met en cache le HTML répond depuis un serveur proche du visiteur : le Time to First Byte n'inclut plus le trajet jusqu'à votre serveur ni son travail. Un CDN qui ne met en cache que les images et les scripts transmet chaque requête HTML à votre serveur. Sur Cloudflare, le HTML n'est mis en cache qu'avec une Cache Rule, ou via APO pour WordPress : Configurer Cloudflare pour les Core Web Vitals donne les réglages. Le code exécuté à l'edge du CDN qui nécessite votre base de données doit toujours attendre l'aller-retour jusqu'à celle-ci.
Pages générées pour chaque visiteur
Les pages des utilisateurs connectés, le panier et les résultats de recherche ne peuvent pas provenir d'un cache de page ; Server-Timing révèle l'étape lente. Côté base de données : un index pour la requête lente, et une seule requête pour une liste au lieu d'une par élément (le problème N+1). Une page qui attend une base de données ou une API dans un autre datacenter ajoute cet aller-retour à chaque requête : mettez sa réponse en cache, et appelez en parallèle les services indépendants.
const [stock, prices] = await Promise.all([getStock(id), getPrices(id)]);
L'envoi d'e-mails, l'écriture de logs et la synchronisation des stocks peuvent attendre : traitez-les après la réponse ou dans une file d'attente.
Le serveur et l'hébergeur
Changer d'hébergeur a du sens quand Server-Timing et vos données de RUM montrent que le serveur lui-même est lent : les pages en cache sont rapides, et sur celles sans cache, chaque étape prend du temps. De nombreuses montées en gamme d'hébergement n'ajoutent que du stockage ; un serveur plus rapide a besoin de plus de CPU et de mémoire. Utilisez une version récente de PHP avec OPcache activé. Une fonction serverless attend un cold start après une période de calme : gardez une instance active, ou placez un cache de page en amont.
Frameworks JavaScript (Next.js, Nuxt, SvelteKit)
Un framework JavaScript qui génère chaque page sur le serveur à la demande produit le TTFB d'une page sans cache. En pratique, le contenu du CMS est récupéré en direct à chaque page avec un Cache-Control: private ou no-store, si bien que le CDN ne peut pas non plus conserver le HTML. Rendez les pages statiques ou mettez-les en cache dès que le contenu le permet (dans Next.js, use cache), mettez en cache les appels au CMS et laissez le CDN conserver le HTML. Streamez ce qui reste dynamique derrière <Suspense>, pour que le head et ses feuilles de style partent en premier. La lecture de cookies ou d'en-têtes dans un layout force le rendu à la demande de toutes les pages descendantes.
Le serveur est-il devenu plus rapide pour les vrais visiteurs ?
Vérifiez d'abord un chargement sans être connecté dans une fenêtre privée, puis avec ?gclid= et une nouvelle valeur. Les en-têtes de réponse doivent indiquer un cache HIT (cf-cache-status: HIT sur Cloudflare, X-CWV-Cache: HIT avec WP Core Web Vitals) et vos entrées Server-Timing ; un MISS sur le chargement avec ?gclid= signifie que les visiteurs provenant d'annonces attendent le serveur. Lors d'un déploiement, il faut aussi purger le HTML sur le CDN, sans quoi les visiteurs continueront de recevoir l'ancienne page.
Le lendemain, la part Request dans vos données de RUM montre le changement. PageSpeed Insights n'affiche que le TTFB global sur les 28 derniers jours, si bien qu'il évolue lentement.
Certaines requêtes lentes exigent plus qu'un cache ou un réglage : une boutique où chaque visiteur a un panier, ou un hébergeur ou un backend impossible à modifier. En une Core Web Vitals power hour, nous analysons vos entrées Server-Timing et vos pages les plus lentes, puis déterminons ce que le serveur doit cesser de faire en premier et qui s'en charge : votre hébergeur ou votre développeur.

Besoin d'un site plus rapide ?
Je suis Arjen et je répare des sites lents depuis 17 ans. Si vous rencontrez des problèmes que vous ne pouvez pas résoudre vous-même, envoyez-moi votre URL et parlons-en.
Envoyez-moi votre URL
Pourquoi notre fix ne fonctionne pas ?
Parfois le CDN sert encore l'ancien HTML, ou le fix a été appliqué sur le template desktop alors que le mobile en utilise un autre. Envoyez-moi votre URL et le problème, et je vous réponds avec quelques créneaux. J'analyse d'abord vos field data, puis nous passons une heure sur votre site, et sous 24 heures vous avez mes notes par écrit. 350 € HT.
Envoyez-moi votre URLPas de développeur pour réaliser le fix ? Je fais le travail moi-même, sur une copie de votre site.