Aller au contenu

Caisse WooCommerce lente : trouver l'étape en cause et la corriger

Apprenez à trouver pourquoi la caisse WooCommerce fait attendre un seul client de 5 à 15 secondes dans une boutique tranquille. Ce guide vous montre comment chronométrer chaque requête de la caisse, repérer les services externes lents et l'encombrement de la base de données, puis corriger l'étape vraiment lente.

Ce guide aide les propriétaires de boutiques WooCommerce, et les gens qui gèrent leurs serveurs, à trouver pourquoi la caisse est lente pour un seul client, même quand la boutique est calme. Le symptôme est connu : la page de caisse s’affiche, le total de la commande tourne plusieurs secondes, puis le clic sur « Commander » en prend encore cinq ou plus. La cause tient presque toujours à une seule étape, le plus souvent un service externe comme un calcul de tarifs d’expédition ou de taxes que WooCommerce attend. Vous allez chronométrer chaque requête de la caisse séparément, trouver la pièce lente et la corriger, plutôt que de payer pour un plus gros serveur.

Ce que vous allez accomplir

  • Savoir laquelle des trois requêtes de la caisse est lente : le chargement de la page, le recalcul du total ou l’envoi de la commande.
  • Obtenir le temps de chaque appel HTTP externe fait par la caisse, avec tout ce qui dépasse 1 seconde signalé.
  • Savoir si les options autochargées, les vieilles sessions de clients ou les réglages de stockage des commandes ajoutent du travail à la base de données.
  • État final : la page de caisse se charge en moins de 1 seconde, le total se recalcule en moins de 1 seconde et « Commander » aboutit en moins de 3 secondes, passerelle de paiement comprise.

Prérequis

  • WooCommerce 8.x ou plus récent sur WordPress 6.4 ou plus récent, avec un accès shell et WP-CLI (l’outil de ligne de commande de WordPress) lancé avec l’utilisateur du site, depuis le dossier WordPress.
  • Une copie de préproduction avec les vraies extensions d’expédition, de taxes et de paiement en mode test. Ne rejouez jamais un vrai envoi de commande contre la production.
  • Chrome, Edge ou Firefox avec les outils de développement, et curl sur votre poste.
  • L’extension Query Monitor, installée en préproduction. Elle affiche les requêtes SQL et les appels HTTP externes de chaque page vue.
  • Sur les serveurs cPanel, un accès root si vous voulez le journal des requêtes lentes de PHP-FPM (le gestionnaire de processus PHP qu’utilisent beaucoup de serveurs cPanel). Les serveurs LiteSpeed avec LSAPI, son propre gestionnaire PHP, n’ont pas de journal équivalent ; l’étape 8 propose une solution de rechange.
  • Le SQL ci-dessous suppose le préfixe de tables par défaut wp_. Lancez wp db prefix pour voir le vôtre et remplacez-le au besoin.

Cet article porte sur la lenteur avec un seul client. Si la caisse ne flanche que lorsque beaucoup de gens achètent en même temps, lisez plutôt Caisse WooCommerce en 504 sous la charge.

Comment ça fonctionne

Une caisse est une courte suite de requêtes, et chacune peut être lente pour une raison différente.

Avec la caisse classique par code court (la page [woocommerce_checkout]), le navigateur charge d’abord /checkout/. Il envoie ensuite une requête à ?wc-ajax=update_order_review pour calculer l’expédition, les taxes et le total. Cet appel se répète chaque fois que le client change son adresse ou son mode d’expédition. Le clic sur « Commander » envoie tout le formulaire à ?wc-ajax=checkout.

La caisse en blocs fait le même travail par la Store API, l’interface REST de WooCommerce pour la vitrine. Les changements d’adresse passent par /wp-json/wc/store/v1/batch ou /wp-json/wc/store/v1/cart/update-customer, et la commande est envoyée à /wp-json/wc/store/v1/checkout.

Cascade des trois requêtes de la caisse, où PHP et la base de données prennent 1,5 seconde et les services externes d'expédition, de taxes, de fraude et de paiement ajoutent 7,5 secondes

C’est au recalcul du total que les services externes font le plus mal. Pour afficher les options d’expédition, une extension de tarifs en direct appelle l’API du transporteur. Un service de taxes peut aussi être appelé pour le même panier. WooCommerce garde les tarifs d’expédition en cache dans la session du client et les réutilise tant que le panier et l’adresse ne changent pas. Quand ce cache est contourné, chaque recalcul paie un nouvel appel au transporteur. L’envoi de la commande ajoute ensuite le contrôle antifraude et la passerelle de paiement, puis écrit la commande dans la base de données.

PHP lui-même est rarement en cause dans une boutique calme. Quand il l’est, on trouve d’habitude un gros bloc d’options autochargées, soit les réglages que WordPress charge à chaque requête. Les autres causes courantes sont une table de sessions gonflée et un stockage des commandes qui écrit chaque commande en double.

Arbre de décision qui part de la requête lente dans l'onglet Réseau du navigateur et mène aux vérifications et correctifs pour le chargement de la page, la mise à jour de la commande et l'envoi de la commande

Étapes

1. Trouvez la requête lente dans le navigateur. Ouvrez la caisse en préproduction avec un produit dans le panier. Ouvrez les outils de développement, allez dans l’onglet Réseau et tapez wc-ajax dans le filtre (ou store/v1 pour la caisse en blocs). Changez le code postal, puis passez une commande test.

Résultat attendu : vous voyez update_order_review (ou batch) après chaque changement d’adresse, et checkout après l’envoi de la commande. Cliquez sur chacune et ouvrez l’onglet Timing. La ligne « Waiting for server response » donne le temps passé côté serveur. Notez les trois chiffres. Toute requête au-dessus de 1 seconde est celle à creuser. Si le document de la page lui-même est lent, notez-le aussi.

2. Chronométrez la requête lente en ligne de commande. Dans l’onglet Réseau, faites un clic droit sur la requête update_order_review lente et choisissez Copy as cURL. Collez-la dans un terminal et ajoutez les options de chronométrage ci-dessous à la fin. La commande copiée contient votre témoin de session et votre jeton de sécurité, elle rejoue donc la même requête.

# collez la commande curl copiée, puis ajoutez :
  -o /dev/null -s -w 'connect=%{time_connect}s tls=%{time_appconnect}s ttfb=%{time_starttransfer}s total=%{time_total}s\n'
connect=0.012s tls=0.041s ttfb=4.793s total=4.801s

ttfb (le temps jusqu’au premier octet) moins tls donne le temps de travail de votre serveur. Ici, c’est environ 4,75 secondes, donc le délai vient du serveur et non du réseau. Lancez-la trois fois. Si la première est lente et les deux suivantes rapides, un cache se réchauffe, et le vrai problème est que ce cache se vide trop souvent. Ne rejouez que update_order_review. Rejouer la requête checkout crée une vraie commande.

3. Journalisez chaque appel HTTP externe. WordPress fait passer la plupart des requêtes sortantes par son API HTTP (wp_remote_get, wp_remote_post et les fonctions apparentées), et deux crochets permettent de les chronométrer. Créez cette extension obligatoire (mu-plugin) en préproduction.

<?php
// wp-content/mu-plugins/sh-http-timing.php
add_filter( 'pre_http_request', function ( $pre, $args, $url ) {
    $GLOBALS['sh_http_start'][ $url ] = microtime( true );
    return $pre;
}, 10, 3 );

add_action( 'http_api_debug', function ( $response, $context, $class, $args, $url ) {
    $start = $GLOBALS['sh_http_start'][ $url ] ?? null;
    if ( null === $start ) {
        return;
    }
    $ms   = (int) round( ( microtime( true ) - $start ) * 1000 );
    $code = is_wp_error( $response ) ? $response->get_error_code() : wp_remote_retrieve_response_code( $response );
    error_log( sprintf( 'sh-http %dms %s %s %s', $ms, $args['method'], $code, wp_parse_url( $url, PHP_URL_HOST ) ) );
}, 10, 5 );

Refaites le changement de code postal et une commande test, puis lisez le journal. Avec WP_DEBUG_LOG activé, les lignes vont dans wp-content/debug.log. Sinon, elles vont dans le journal d’erreurs PHP, qui sur cPanel est habituellement le fichier error_log du dossier du site.

grep 'sh-http' wp-content/debug.log | tail -n 20
[16-Sep-2026 14:02:11 UTC] sh-http 3112ms POST 200 api.shipping-carrier.example
[16-Sep-2026 14:02:12 UTC] sh-http 1287ms POST 200 tax.provider.example
[16-Sep-2026 14:02:40 UTC] sh-http 1204ms POST 200 fraud.provider.example
[16-Sep-2026 14:02:42 UTC] sh-http 1893ms POST 200 api.payment-gateway.example

L’extension n’enregistre que la méthode, le code de statut et le nom d’hôte, jamais de données de carte ni de contenu de requête. Toute ligne au-dessus de 1000 ms est une constatation à traiter. Un code http_request_failed avec un temps proche de 5000 ou 10000 ms signifie qu’un service tombe en délai d’expiration, et la caisse l’attend jusqu’au bout. Pour le chargement de la page, le panneau HTTP API Calls de Query Monitor affiche les mêmes détails dans la barre d’administration. Supprimez le mu-plugin une fois terminé.

4. Vérifiez que le cache des tarifs d’expédition est actif. WooCommerce offre un mode débogage d’expédition qui contourne le cache des tarifs, si bien que chaque recalcul rappelle le transporteur. On l’oublie facilement après des tests.

wp option get woocommerce_shipping_debug_mode
no

Si vous voyez yes, désactivez-le dans WooCommerce → Réglages → Expédition → Options d’expédition, ou lancez wp option update woocommerce_shipping_debug_mode no. Vérifiez ensuite les réglages de votre extension de tarifs en direct. Beaucoup d’extensions de transporteurs ont leur propre cache de tarifs. Activez-le et fixez sa durée à au moins une heure.

5. Mesurez les options autochargées. WordPress charge chaque option autochargée à chaque requête, y compris chaque appel de la caisse.

wp db query "SELECT COUNT(*) AS options, ROUND(SUM(LENGTH(option_value))/1024) AS kb FROM wp_options WHERE autoload IN ('yes','on','auto-on','auto');"
+---------+------+
| options | kb   |
+---------+------+
|    1184 | 3620 |
+---------+------+

Sous 800 Ko, c’est sain, et c’est aussi le seuil où l’outil Santé du site de WordPress commence à avertir. Au-delà, listez les plus grosses entrées.

wp db query "SELECT option_name, ROUND(LENGTH(option_value)/1024) AS kb FROM wp_options WHERE autoload IN ('yes','on','auto-on','auto') ORDER BY LENGTH(option_value) DESC LIMIT 10;"

On y trouve surtout des restes d’extensions supprimées et de grosses listes mises en cache. Désactivez l’autochargement d’une entrée confirmée avec wp option set-autoload <name> off. Le guide d’audit de l’autochargement vous aide à décider ce qui peut être changé sans risque.

6. Vérifiez la table des sessions. WooCommerce garde le panier de chaque client dans wp_woocommerce_sessions. Une tâche planifiée supprime les lignes expirées deux fois par jour. Quand WP-Cron (le planificateur de tâches intégré à WordPress) est brisé, la table grossit sans limite.

wp db query "SELECT COUNT(*) AS total, SUM(session_expiry < UNIX_TIMESTAMP()) AS expired, ROUND(SUM(LENGTH(session_value))/1024/1024,1) AS mb FROM wp_woocommerce_sessions;"
+--------+---------+-------+
| total  | expired | mb    |
+--------+---------+-------+
| 412880 |  398511 | 911.4 |
+--------+---------+-------+

Une boutique en santé a peu de lignes expirées. Des dizaines de milliers veulent dire que le nettoyage ne roule pas. Vérifiez l’événement planifié, puis lancez-le une fois à la main.

wp cron event list --hook=woocommerce_cleanup_sessions --fields=hook,next_run_relative
wp cron event run woocommerce_cleanup_sessions
+------------------------------+--------------------+
| hook                         | next_run_relative  |
+------------------------------+--------------------+
| woocommerce_cleanup_sessions | 3 hours 12 minutes |
+------------------------------+--------------------+
Executed the cron event 'woocommerce_cleanup_sessions' in 41.207s.
Success: Executed a total of 1 cron event.

Cette commande ne supprime que les sessions expirées. Le bouton WooCommerce → État → Outils → Effacer les sessions clients les supprime toutes, paniers actifs compris ; utilisez-le seulement dans une heure creuse. Si l’événement est absent ou que sa prochaine exécution est dans le passé, réparez WP-Cron avant toute chose.

7. Vérifiez la synchronisation du stockage des commandes. Le stockage haute performance des commandes (HPOS) garde les commandes dans des tables dédiées. Son mode de compatibilité tient aussi à jour les anciennes tables d’articles, ce qui ajoute des écritures à chaque création de commande.

wp option get woocommerce_custom_orders_table_enabled
wp option get woocommerce_custom_orders_table_data_sync_enabled
yes
no

yes puis no, c’est la configuration légère. Si la deuxième valeur est yes, le mode de compatibilité est actif. Désactivez-le dans WooCommerce → Réglages → Avancé → Fonctionnalités seulement quand chaque extension active est marquée compatible sur cet écran et que wp wc hpos count_unmigrated affiche 0. Le guide de migration HPOS couvre ce changement.

8. Attrapez le code PHP lent avec le journal des requêtes lentes. Quand les étapes 3 à 7 ne trouvent rien, enregistrez la pile d’appels de ce que PHP fait après 3 secondes. Sur un serveur cPanel avec PHP-FPM, ajoutez ces deux lignes à /var/cpanel/userdata/<user>/<domain>.php-fpm.yaml, pour que cPanel les conserve quand il régénère le pool.

request_slowlog_timeout: 3s
slowlog: /home/<user>/logs/php-fpm-slow.log
/usr/local/cpanel/scripts/php_fpm_config --rebuild
/scripts/restartsrv_apache_php_fpm

Confirmez que le pool a pris le réglage avec grep slowlog /opt/cpanel/ea-php*/root/etc/php-fpm.d/<domain>.conf, qui devrait afficher les deux lignes. Si rien ne s’affiche, votre version de cPanel n’a pas repris ces clés dans le pool : consultez la documentation de cPanel sur les pools PHP-FPM par utilisateur pour connaître les noms de clés acceptés. Passez une commande test, puis lisez le journal. La première ligne de trace montre ce que PHP attendait.

[16-Sep-2026 14:05:31]  [pool example_com] pid 48211
script_filename = /home/example/public_html/index.php
[0x00007f3a2c813f10] curl_exec() /home/example/public_html/wp-includes/Requests/src/Transport/Curl.php:239

Une ligne curl_exec() signale un appel externe, et les lignes suivantes nomment l’extension qui l’a fait. LiteSpeed LSAPI n’a pas de journal équivalent. Sur ces serveurs, utilisez CloudLinux X-Ray si votre hébergeur l’offre, ou un profileur PHP comme New Relic.

9. Confirmez que la caisse n’est pas mise en cache. Une caisse en cache affiche des totaux périmés et des jetons de sécurité expirés, ce qui cause des recalculs en échec et des nouvelles tentatives.

curl -sI https://store.example.com/checkout/ | grep -i -E 'cache-control|x-litespeed-cache|cf-cache-status|x-cache'
cache-control: no-cache, must-revalidate, max-age=0, no-store, private
cf-cache-status: DYNAMIC

Tout HIT sur cette page est un problème. Ajoutez /checkout/, /cart/ et /my-account/ à la liste d’exclusions de votre extension de cache, puis videz le cache.

Valider le résultat

Refaites les étapes 1 et 2 après chaque correctif et comparez avec vos premiers chiffres.

# collez la commande curl update_order_review copiée, puis ajoutez :
  -o /dev/null -s -w 'ttfb=%{time_starttransfer}s total=%{time_total}s\n'
ttfb=0.382s total=0.389s

Vous avez terminé quand tout ceci est vrai en préproduction :

  • La page de caisse se charge avec un temps serveur sous 1 seconde.
  • update_order_review ou le recalcul par la Store API reste sous 1 seconde, y compris au deuxième changement d’adresse.
  • Une commande test dans le bac à sable de la passerelle aboutit en moins de 3 secondes.
  • Le journal sh-http de l’étape 3 n’a aucune ligne au-dessus de 1000 ms pendant le recalcul.

Appliquez ensuite les mêmes changements en production et passez une vraie commande de faible valeur pour confirmer.

Dépannage

Le mu-plugin n’enregistre rien, mais la caisse attend clairement. Certaines trousses de développement (SDK) de paiement ou de taxes utilisent leur propre client HTTP et contournent l’API HTTP de WordPress. Le journal de l’étape 8 ou un profileur les montrera quand même.

Le journal des requêtes lentes reste vide ou signale des erreurs ptrace. PHP-FPM a besoin du droit d’inspecter ses propres processus. Certaines configurations CloudLinux CageFS ou certains conteneurs le bloquent. Utilisez plutôt un profileur.

Le total se recalcule plusieurs fois par adresse. Les extensions d’autocomplétion d’adresse peuvent déclencher le recalcul à chaque champ qu’elles remplissent. Chaque recalcul rappelle le transporteur si les tarifs ne sont pas en cache, d’où l’importance double de l’étape 4.

Des clients ont perdu leur panier après le nettoyage. L’outil Effacer les sessions clients a été utilisé au lieu du nettoyage planifié. Les paniers ne se récupèrent pas ; utilisez wp cron event run woocommerce_cleanup_sessions la prochaine fois.

Seule la première commande du matin est lente. Cela pointe vers un cache d’opcodes PHP ou un cache d’objets vide après un redémarrage, et non vers la caisse. Regardez ce qui redémarre PHP pendant la nuit.

Quand cette approche ne convient pas

Si la caisse est rapide pour un client seul et ne flanche que pendant les soldes, c’est un problème de capacité, que couvre le guide sur les 504 sous la charge. Si toutes les pages de la boutique sont lentes, commencez par la performance générale du serveur et du catalogue avant de regarder la caisse. Si le délai survient sur la page hébergée du fournisseur de paiement après la redirection, il vient de son côté ; sa page d’état et son soutien technique sont la bonne suite. Pour le format des requêtes de la caisse, la documentation de la Store API fait référence.

Un WordPress qui rapporte vraiment mérite un opérateur — Stack Harbor opère cette pile sous WordPress Entreprise ; réservez une consultation d’infrastructure.