Si vous avez déjà cliqué sur “Commander” et que rien ne se passe (ou pire, une erreur 500/403 apparaît), vous avez probablement un checkout WooCommerce cassé par du JavaScript, un endpoint AJAX bloqué, ou un cache mal configuré. Le piège, c’est que le checkout mélange page HTML, appels AJAX (wc-ajax), nonces et parfois REST API. Un seul maillon faible et tout s’écroule.

Le problème

Les messages exacts varient, mais ceux que je vois le plus souvent dans la console et les logs serveur ressemblent à ça :

Failed to load resource: the server responded with a status of 403 (Forbidden)
/?wc-ajax=checkout:1 403 (Forbidden)

Uncaught TypeError: Cannot read properties of undefined (reading 'checkout_url')
    at checkout.js:...
PHP Fatal error:  Uncaught Error: Call to undefined function wc() in /wp-content/themes/votre-theme/functions.php:123

PHP Fatal error:  Uncaught Error: Class "AutomatticWooCommerceBlocksPackage" not found in /wp-content/plugins/mon-plugin/mon-plugin.php:45
[error] [client ...] ModSecurity: Access denied with code 403 (phase 2). Matched "Operator `Rx' with parameter `(?i)(?:bselectb|bunionb|bdropb)'" ... uri "/?wc-ajax=checkout"

Où ça apparaît :

  • Front-end : page /checkout/, au clic sur “Commander”, lors du changement de méthode de livraison/paiement, ou au chargement (fragments).
  • Admin : parfois via les réglages WooCommerce (si des assets ne se chargent pas), mais le checkout se casse surtout côté visiteur.
  • API / AJAX : endpoints ?wc-ajax=checkout, ?wc-ajax=update_order_review, ?wc-ajax=get_refreshed_fragments, et parfois /wp-json/.

Circonstances typiques :

  • Juste après une mise à jour WordPress (ici 6.9.4), WooCommerce, un plugin de paiement, ou un plugin de cache/minification.
  • Après activation d’un WAF/CDN (Cloudflare, ModSecurity côté hébergeur) qui bloque wc-ajax.
  • Après modification de template checkout dans un thème enfant (ou via un builder) et perte d’un hook WooCommerce.

À qui s’adresse ce dépannage : si vous êtes à l’aise avec PHP/JS et l’écosystème WordPress, vous repartirez avec une méthode reproductible pour isoler la cause (JS, cache, endpoint, conflit) et une série de correctifs concrets (avec code avant/après) compatibles WordPress 6.9.4+ et PHP 8.1+.

Résumé rapide

  • Commencez par la console navigateur : une seule erreur JS peut empêcher le submit checkout.
  • Testez les endpoints : /?wc-ajax=update_order_review et /?wc-ajax=checkout doivent répondre (souvent en JSON/HTML partiel).
  • Désactivez temporairement la minification/différage JS : c’est la cause n°1 sur les sites avec cache agressif.
  • Excluez checkout/cart/my-account du cache et des optimisations (CDN, page cache, Rocket Loader, etc.).
  • Vérifiez WP_DEBUG + Query Monitor : une fatal PHP “silencieuse” côté AJAX ressemble à un checkout “qui ne fait rien”.
  • Conflits plugins : test rapide avec Health Check (mode dépannage) sans impacter les visiteurs.

Les symptômes

Voici les symptômes visibles que vous pouvez rencontrer, souvent combinés :

  • Le bouton “Commander” ne fait rien (pas de redirection, pas de message d’erreur).
  • Spinner infini après sélection d’un moyen de paiement ou recalcul des frais de port.
  • Erreur 500 sur /?wc-ajax=checkout ou /?wc-ajax=update_order_review.
  • Erreur 403/406 (WAF/ModSecurity) sur les endpoints Woo.
  • Erreur 400 “Bad Request” ou message “Invalid nonce” (souvent causé par cache).
  • Console JS : Uncaught TypeError, jQuery is not defined, ou erreurs sur checkout.js.
  • REST API : /wp-json/ renvoie 401/403, ce qui peut impacter certains moyens de paiement ou blocs.
  • Ça marche en local mais pas en production : CDN, cache serveur, règles de sécurité, ou différences PHP/extensions.
  • Shortcode / bloc checkout non rendu après personnalisation (builder) : page vide ou contenu partiel.

Tableau de diagnostic rapide

Symptôme Cause probable Vérification Solution
Bouton “Commander” inactif Erreur JS / minification / defer Console navigateur + désactiver optimisation JS Solution 1 + exclure scripts Woo
Spinner infini sur livraison/paiement Endpoint update_order_review en erreur Onglet Réseau: status 500/403 Solution 1 ou 3 selon code
403 sur ?wc-ajax=checkout WAF/ModSecurity/CDN Logs serveur + en-têtes WAF Solution 3 (whitelist règle)
“Invalid nonce” / session perdue Page cache sur checkout Comparer HTML caché vs non caché Solution 2 (exclusions cache)
500 sur endpoint AJAX Fatal PHP dans un plugin/thème WP_DEBUG_LOG + Query Monitor Corriger fatal + hook approprié

Pourquoi ça arrive

Version simple : le checkout WooCommerce n’est pas “une page”. C’est une page qui déclenche des appels AJAX, manipule la session, et valide des nonces. Si un plugin modifie le chargement JS, si le cache sert une version figée, ou si le serveur bloque l’endpoint, le processus d’achat se bloque.

Version technique : WooCommerce s’appuie sur :

  • Assets JS/CSS (notamment wc-checkout, woocommerce, jquery-blockui selon contexte), et sur des variables localisées (ex: URL AJAX, nonces, paramètres).
  • Endpoints AJAX via admin-ajax.php pour certains flux, mais surtout ?wc-ajax=... (plus léger) pour update_order_review, checkout, fragments, etc.
  • Cookies/session WooCommerce (panier, session client), sensibles aux caches et aux proxies.
  • Hooks côté thème/plugins qui peuvent injecter des champs, valider des données, ou casser le flux (ex: validation trop stricte, erreur PHP).

Causes probables (du plus fréquent au plus rare) :

  1. Optimisation front (minify/combine/defer/delay JS) qui casse l’ordre d’exécution ou supprime une dépendance.
  2. Cache page / CDN sur checkout/cart/my-account (nonces périmés, fragments incohérents).
  3. WAF / ModSecurity qui bloque ?wc-ajax=checkout (faux positif sur payload).
  4. Fatal PHP dans un plugin de paiement/livraison, ou snippet qui appelle Woo trop tôt.
  5. Templates checkout surchargés (thème enfant / builder) obsolètes ou hooks manquants.
  6. Permaliens / réécriture ou règles serveur qui redirigent mal (HTTP↔HTTPS, trailing slash, langues).
  7. Problèmes “edge” : objet cache persistant mal configuré, headers cookies cassés, limite mémoire, timeouts.

Prérequis avant de commencer

  • Sauvegarde (fichiers + base) et idéalement un staging. Ne testez pas des snippets au hasard en production.
  • Versions : WordPress 6.9.4 (votre contexte), PHP 8.1+ (recommandé), WooCommerce à jour.
  • Activer les logs côté WordPress :
/** Activer le debug en environnement de test */
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false ); // Évitez d'afficher les erreurs aux clients
define( 'SCRIPT_DEBUG', true ); // Charge les versions non minifiées quand dispo
  • Outils :
    • Query Monitor (voir requêtes, hooks, erreurs PHP, appels AJAX).
    • Health Check & Troubleshooting (mode dépannage sans impacter les visiteurs).
    • Accès aux logs PHP/serveur (error_log, nginx/apache) et à la console navigateur.

Sources officielles utiles pendant le diagnostic :

Solution 1 : Corriger un checkout bloqué par une erreur JavaScript/AJAX (wc-ajax)

Quand le checkout “ne fait rien”, je commence presque toujours par l’onglet Console puis Réseau. Si l’appel ?wc-ajax=checkout ne part pas, ou part mais revient en 500/403, vous avez votre piste.

Diagnostic concret (2 minutes)

  1. Ouvrez la page checkout en navigation privée.
  2. F12 → Console : notez la première erreur (pas la dixième).
  3. F12 → Réseau → filtrez “wc-ajax”.
  4. Cliquez “Commander” et observez :
    • statut HTTP (200/302/400/403/500)
    • réponse (JSON d’erreur, HTML, page de firewall)

Cas typique : un script personnalisé “trop tôt” casse WooCommerce

J’ai souvent vu un snippet qui manipule le checkout sans déclarer les dépendances JS, ce qui casse l’ordre de chargement quand un plugin de perf “diffère” des scripts.

AVANT (cassé) : enqueue sans dépendances + exécution globale

add_action( 'wp_enqueue_scripts', function () {
	// Mauvaise pratique : pas de dépendances, pas de condition checkout
	wp_enqueue_script(
		'mon-checkout',
		get_stylesheet_directory_uri() . '/assets/checkout.js',
		[],
		'1.0.0',
		true
	);
} );
// Mauvaise pratique : suppose que jQuery et wc_checkout_params existent toujours
jQuery(function ($) {
	$(document.body).on('click', '#place_order', function () {
		console.log(wc_checkout_params.checkout_url);
	});
});

Ce code “marche” parfois… jusqu’au jour où un plugin de cache diffère jQuery, ou où WooCommerce charge ses paramètres différemment selon le contexte. Résultat : jQuery is not defined ou wc_checkout_params is undefined, et le checkout peut ne plus soumettre.

APRÈS (corrigé) : dépendances + condition + garde-fous

add_action( 'wp_enqueue_scripts', function () {
	// Ne chargez ce script que sur le checkout pour limiter les conflits
	if ( function_exists( 'is_checkout' ) && is_checkout() ) {
		wp_enqueue_script(
			'mon-checkout',
			get_stylesheet_directory_uri() . '/assets/checkout.js',
			[ 'jquery', 'wc-checkout' ], // Dépendances explicites
			'1.0.1',
			true
		);
	}
}, 20 );
jQuery(function ($) {
	// Garde-fou : si WooCommerce n'a pas initialisé ses paramètres, on n'exécute pas
	if (typeof window.wc_checkout_params === 'undefined') {
		console.warn('wc_checkout_params absent : un plugin de cache/minification a peut-être modifié l’ordre de chargement.');
		return;
	}

	// Exemple : écoute d’événement WooCommerce plutôt que de surcharger le submit
	$(document.body).on('updated_checkout', function () {
		// Ici votre logique, sans bloquer le flux natif
	});
});

Pourquoi ça corrige

  • Dépendances : wc-checkout est chargé avant votre script, donc les variables et handlers existent.
  • Condition : vous évitez d’impacter d’autres pages (où Woo ne charge pas les mêmes assets).
  • Garde-fou : en cas d’optimisation agressive, vous loggez un indice au lieu de casser le submit.

Variante : l’appel AJAX renvoie 500 (fatal PHP)

Si /?wc-ajax=checkout renvoie 500, cherchez une fatal PHP dans wp-content/debug.log ou dans les logs PHP-FPM. Une erreur fréquente : un snippet appelle WooCommerce trop tôt.

AVANT (cassé) : appel à wc() avant que Woo soit chargé

add_action( 'init', function () {
	// Mauvais : sur certains contextes (AJAX tôt / ordre de chargement), wc() peut ne pas être dispo
	$currency = wc()->currency->get_currency();
	error_log( 'Devise: ' . $currency );
} );

APRÈS (corrigé) : attendre le bon moment + vérifier la présence

add_action( 'woocommerce_init', function () {
	// Correct : WooCommerce est initialisé
	if ( function_exists( 'wc' ) && wc() && isset( wc()->currency ) ) {
		$currency = wc()->currency->get_currency();
		error_log( 'Devise: ' . $currency );
	}
} );

Pourquoi : quand WooCommerce traite un endpoint wc-ajax, l’ordre d’initialisation peut exposer les snippets “trop tôt”. En vous accrochant à woocommerce_init, vous réduisez drastiquement les fatals intermittentes.

Compatibilité Divi 5 / Elementor / Avada

  • Divi 5 : évitez d’injecter du JS inline via un module Code sur la page checkout. J’ai vu des erreurs de scope et d’ordre de chargement. Préférez un enqueue propre (ci-dessus).
  • Elementor : si vous utilisez le template Checkout (WooCommerce), désactivez les optimisations “Improved Asset Loading”/expériences qui retardent jQuery sur checkout, ou excluez cette page.
  • Avada : Fusion Builder + options perf peuvent combiner/différer. Excluez checkout des optimisations JS si vous voyez des erreurs wc_checkout_params.

Solution 2 : Éliminer les problèmes de cache/CDN qui cassent les nonces et les fragments

Un checkout mis en cache, c’est souvent un checkout mort. Le symptôme typique : “Invalid nonce”, panier qui se vide, ou paiement qui échoue uniquement pour certains visiteurs (selon POP CDN / cookies).

Ce qui se passe en coulisses

Le checkout contient des nonces et des champs dynamiques. Si un cache page sert une version HTML générée pour un autre utilisateur (ou trop ancienne), WooCommerce reçoit un nonce périmé et refuse l’action. Même chose si votre CDN ignore certains cookies WooCommerce.

Diagnostic

  • Testez checkout avec cache bypass :
    • désactivez temporairement le cache page (plugin + cache serveur si possible)
    • désactivez “Rocket Loader”/optimisations JS côté CDN
  • Comparez les en-têtes :
    • Cache-Control, CF-Cache-Status, X-Cache sur /checkout/
  • Vérifiez si /checkout/ est servi depuis le cache malgré un panier non vide.

Correctif : envoyer des headers no-cache sur checkout/cart/my-account

WooCommerce gère déjà beaucoup de cas, mais sur certains stacks (reverse proxy agressif, règles custom), forcer des headers aide. Attention : ce n’est pas une excuse pour laisser un plugin de cache mettre en cache checkout. C’est un filet de sécurité.

AVANT (cassé) : règles de cache globales

add_action( 'send_headers', function () {
	// Mauvais : forcer un cache public partout, y compris checkout
	header( 'Cache-Control: public, max-age=3600' );
} );

APRÈS (corrigé) : no-cache ciblé WooCommerce

add_action( 'send_headers', function () {
	if ( is_admin() ) {
		return;
	}

	// Sur les pages sensibles WooCommerce, on empêche le cache navigateur/proxy
	if ( function_exists( 'is_checkout' ) && ( is_checkout() || is_cart() || is_account_page() ) ) {
		header( 'Cache-Control: no-store, no-cache, must-revalidate, max-age=0' );
		header( 'Pragma: no-cache' );
		header( 'Expires: Wed, 11 Jan 1984 05:00:00 GMT' );
	}
}, 20 );

Pourquoi : vous réduisez les cas où un proxy/CDN “s’entête” à mettre en cache une page dynamique.

Correctif : exclure checkout des optimisations JS (pratique)

La plupart des plugins de perf permettent des exclusions par URL. Ciblez au minimum :

  • /checkout/
  • /cart/
  • /my-account/
  • ?wc-ajax=* (si votre outil touche aux requêtes)

Dans mon expérience, le combo le plus destructeur est : “Delay JS execution” + “Combine JS” + “Remove jQuery Migrate” (selon site) appliqué au checkout. Vous gagnez 100 ms et vous perdez des commandes.

Note sécurité

Un cache mal configuré peut exposer des données (adresse, email) si une page compte est mise en cache. Vérifiez vos règles. Si vous suspectez une fuite, purgez les caches et auditez immédiatement.

Quand la console montre des 403/404 sur ?wc-ajax=... ou /wp-json/, le problème n’est plus WooCommerce “en lui-même”. C’est votre serveur (WAF, règles, permaliens, redirections) qui bloque.

Test rapide des endpoints

Depuis votre navigateur (connecté/déconnecté), testez :

  • https://votre-site.tld/?wc-ajax=get_refreshed_fragments
  • https://votre-site.tld/?wc-ajax=update_order_review (peut exiger POST en situation réelle)
  • https://votre-site.tld/wp-json/

Et via WP-CLI (utile pour vérifier la santé globale WordPress) :

wp core version
wp plugin list --status=active
wp option get permalink_structure

Cas 1 : permaliens cassés / redirections incohérentes

Un classique après migration : redirection HTTP→HTTPS ou ajout/retrait de “www” via plusieurs couches (WordPress + plugin + serveur + CDN). Les endpoints AJAX se retrouvent redirigés (302) vers une page HTML, et WooCommerce n’arrive plus à parser la réponse.

Vérification :

  • Onglet Réseau : l’appel ?wc-ajax=checkout reçoit un 302 vers une page de login, une page 404, ou une page “consentement cookies”.
  • La réponse contient du HTML au lieu du JSON attendu.

Correctif : unifier l’URL du site et réduire les redirections. Vérifiez :

  • Réglages > Général : “Adresse web de WordPress” et “Adresse web du site”
  • Règles serveur (nginx/apache) et CDN

Si vous avez un code de redirection custom, évitez de toucher aux requêtes AJAX WooCommerce.

AVANT (cassé) : redirection globale naïve

add_action( 'template_redirect', function () {
	// Mauvais : redirige aussi les endpoints wc-ajax et casse les réponses
	if ( ! is_ssl() ) {
		wp_safe_redirect( 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'], 301 );
		exit;
	}
} );

APRÈS (corrigé) : exclure wc-ajax et admin-ajax

add_action( 'template_redirect', function () {
	// On ne redirige pas dans l'admin
	if ( is_admin() ) {
		return;
	}

	// On évite de casser les endpoints AJAX WooCommerce
	if ( isset( $_GET['wc-ajax'] ) || ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) {
		return;
	}

	if ( ! is_ssl() ) {
		$host = isset( $_SERVER['HTTP_HOST'] ) ? $_SERVER['HTTP_HOST'] : '';
		$uri  = isset( $_SERVER['REQUEST_URI'] ) ? $_SERVER['REQUEST_URI'] : '/';

		wp_safe_redirect( 'https://' . $host . $uri, 301 );
		exit;
	}
}, 1 );

Pourquoi : WooCommerce attend une réponse précise sur wc-ajax. Une redirection 301/302 transforme l’appel en autre chose, et le checkout se bloque.

Cas 2 : WAF/ModSecurity bloque ?wc-ajax=checkout

Si vous voyez un 403 uniquement sur wc-ajax, cherchez une signature ModSecurity dans les logs. J’ai vu des faux positifs sur des champs adresse (ex: “Rue de l’Union”) ou des notes de commande.

Correctifs possibles (du plus propre au plus “bricolage”) :

  • Whitelister l’URL ou la règle ModSecurity côté hébergeur (recommandé).
  • Configurer votre WAF/CDN pour ne pas inspecter agressivement /checkout/ et ?wc-ajax=checkout.
  • Si vous contrôlez nginx/apache, ajouter une exception ModSecurity sur cette route (dépend de votre stack).

Je ne vous donne pas une règle serveur “universelle” ici, parce que ModSecurity varie selon hébergeur. Mais le diagnostic est stable : 403 + log ModSecurity + endpoint Woo = faux positif à lever.

Cas 3 : REST API bloquée (plugins sécurité)

Certaines extensions de sécurité bloquent /wp-json/ aux visiteurs non connectés. Ça peut casser des moyens de paiement, des blocs WooCommerce, ou des intégrations.

Vérification : /wp-json/ doit répondre 200 (ou au moins ne pas être 403) publiquement. WordPress documente la REST API ici : REST API Handbook.

Correctif : ajustez le plugin de sécurité pour autoriser les routes nécessaires. Si vous avez du code custom qui filtre rest_authentication_errors, testez-le avec Query Monitor et retirez les blocages globaux.

Vérifications après correction

  • Test fonctionnel :
    • Produit simple → ajout panier → checkout → paiement (idéalement un moyen “test”)
    • Produit avec livraison → changement d’adresse → recalcul OK
  • Console : zéro erreur JS sur checkout.
  • Réseau :
    • ?wc-ajax=update_order_review répond 200
    • ?wc-ajax=checkout répond 200 et retourne une réponse attendue (pas une page HTML de firewall)
  • Logs :
    • plus de fatal dans wp-content/debug.log
    • plus de 403 WAF sur les endpoints
  • Cache : vérifiez que checkout/cart/my-account ne sont pas mis en cache (en-têtes + outil cache).

Si ça ne marche toujours pas

Voici une procédure que j’applique quand le bug est “glissant” (ça marche parfois, parfois non) :

1) Isoler conflit plugin/thème sans casser la prod

  • Installez Health Check & Troubleshooting.
  • Activez le mode dépannage pour votre session admin.
  • Désactivez tout sauf WooCommerce + le plugin de paiement concerné.
  • Testez checkout en navigation privée.

Si ça remarche : réactivez les plugins un par un. Les coupables fréquents : cache/optimisation, sécurité, traduction, champs checkout, anti-spam agressif.

2) Vérifier mémoire, timeouts, et fatals silencieuses

  • Regardez debug.log et logs PHP-FPM.
  • Avec Query Monitor, inspectez l’onglet “PHP Errors”.
  • Si vous voyez des erreurs “Allowed memory size exhausted”, augmentez la mémoire côté PHP et côté WP (WP_MEMORY_LIMIT), puis retestez.

3) Vérifier la console + sourcemaps (SCRIPT_DEBUG)

Avec SCRIPT_DEBUG, vous obtenez souvent des fichiers non minifiés, donc des stack traces exploitables. Ça accélère énormément l’identification d’un script tiers qui pollue le scope global.

4) Vérifier les règles de réécriture

  • Admin → Réglages → Permaliens → Enregistrer (sans changer) pour régénérer.
  • WP-CLI :
wp rewrite flush --hard

Si vous avez un reverse proxy, assurez-vous qu’il ne réécrit pas ? ou ne supprime pas des query strings (ça casse wc-ajax).

5) Vérifier les permissions fichiers (cas rares mais réels)

Si des fichiers JS/CSS WooCommerce ne sont pas servis (403/404), le checkout casse. Vérifiez que votre serveur sert bien /wp-content/plugins/woocommerce/assets/. Sur certains hébergements durcis, des règles bloquent .map ou certains types MIME.

6) Tester sans builder

Divi/Elementor/Avada peuvent surcharger la page checkout via templates. Pour trancher :

  • Créez une page “Checkout test” avec uniquement le bloc/shortcode WooCommerce natif (sans mise en page complexe).
  • Assignez-la temporairement comme page de commande (WooCommerce → Réglages → Avancé).
  • Retestez.

Pièges et erreurs courantes

Symptôme Cause probable Solution recommandée
Le checkout marche admin connecté, pas pour les clients Cache/CDN varie selon cookies Solution 2 : exclusions cache + vérifier cookies Woo
“Uncaught TypeError … wc_checkout_params” Script chargé avant wc-checkout ou minification/delay Solution 1 : dépendances + exclure optimisation
500 sur ?wc-ajax=checkout Fatal PHP dans plugin/snippet WP_DEBUG_LOG + corriger hook (woocommerce_init)
403 ModSecurity WAF faux positif Solution 3 : whitelist règle/URL
Checkout cassé après “copier-coller” d’un snippet Code collé au mauvais endroit / point-virgule manquant Revenir en arrière, valider syntaxe, utiliser un plugin de snippets fiable
Plus de frais de port / total incorrect AJAX update_order_review bloqué Console + réseau, désactiver conflit, vérifier 200
Le CSS/JS Woo ne se charge pas Mauvais enqueue, blocage serveur, concat agressive Vérifier 200 sur assets, exclure Woo des optimisations

Erreurs que je vois souvent chez des utilisateurs intermédiaires

  • Copier le code dans le mauvais fichier (ex: dans un template WooCommerce au lieu de functions.php ou d’un plugin mu).
  • Oublier une parenthèse et provoquer une fatal qui n’apparaît pas à l’écran (car WP_DEBUG_DISPLAY est à false).
  • Utiliser un hook inadapté (init au lieu de woocommerce_init) et déclencher des bugs intermittents sur AJAX.
  • Confondre action et filtre : retourner une valeur dans une action ne sert à rien, et vous pensez avoir “bloqué” un comportement.
  • Tester sur production sans staging : vous découvrez le bug quand les clients payent.
  • Oublier de vider le cache navigateur/CDN après correction : vous retestez sur une version cassée.

Variante / alternative

Méthode sans code (plugins/outils)

  • Health Check pour isoler le conflit sans impacter les visiteurs.
  • Query Monitor pour voir les erreurs PHP, requêtes lentes, et appels AJAX.
  • Si vous utilisez un plugin de cache : appliquez la configuration “WooCommerce safe” (exclusions checkout/cart/account, désactivation delay JS sur checkout).

Méthode plus avancée (développeurs) : journaliser précisément les appels checkout

Quand le problème est rare (1 commande sur 30), je journalise l’état du contexte sur les endpoints WooCommerce. Le but : savoir si c’est un 403 WAF, une fatal, ou une validation qui bloque.

Exemple minimaliste : loggez les requêtes wc-ajax côté WordPress (attention à ne pas logguer de données sensibles en clair).

add_action( 'init', function () {
	// Journalisation légère : ne stockez pas de données personnelles
	if ( isset( $_GET['wc-ajax'] ) ) {
		$action = sanitize_key( wp_unslash( $_GET['wc-ajax'] ) );

		// Évitez de logger des payloads complets (adresse, email, etc.)
		error_log( '[wc-ajax] action=' . $action . ' ip=' . ( $_SERVER['REMOTE_ADDR'] ?? 'unknown' ) );
	}
}, 1 );

Pourquoi : si vous voyez le log WordPress mais un 403 côté navigateur, c’est souvent un cache/CDN entre les deux. Si vous ne voyez même pas le log, la requête n’atteint pas WordPress (WAF/serveur).

Éviter ce problème à l’avenir

  • Excluez systématiquement checkout/cart/my-account de :
    • cache page
    • optimisations JS agressives (delay/defer/combine) sauf test strict
    • règles WAF trop strictes
  • Chargez vos scripts proprement :
    • dépendances explicites (jquery, wc-checkout)
    • conditions is_checkout()
  • Évitez les redirections “globales” en PHP qui touchent les endpoints AJAX. Si vous devez rediriger, excluez DOING_AJAX et wc-ajax.
  • Surveillez les logs après mise à jour WooCommerce/paiement. Une fatal sur AJAX peut passer inaperçue si vous ne regardez pas debug.log.
  • Gardez les surcharges de templates WooCommerce sous contrôle : si vous copiez des templates dans un thème enfant, ré-auditez-les après mises à jour WooCommerce. (C’est une source récurrente de checkout “bizarre”.)

Ressources

Questions fréquentes

Pourquoi le checkout fonctionne sur mon compte admin mais pas pour les visiteurs ?

Parce que le cache/CDN applique souvent des règles différentes selon cookies (utilisateur connecté vs non). Excluez checkout/cart/account du cache page et retestez en navigation privée.

J’ai un 500 sur /?wc-ajax=checkout, mais la page checkout s’affiche. C’est possible ?

Oui. La page HTML peut se charger, mais l’action “Commander” déclenche un endpoint AJAX qui plante sur une fatal PHP. Regardez wp-content/debug.log et les logs serveur.

Est-ce que je peux “forcer” WooCommerce à ne pas utiliser AJAX ?

Techniquement vous pouvez contourner certains comportements, mais c’est rarement une bonne idée : vous risquez de casser des moyens de paiement et la validation. Mieux vaut corriger la cause (JS/caching/WAF).

Un plugin de minification peut-il casser uniquement le checkout ?

Oui, parce que checkout dépend d’un ordre de scripts précis et de variables localisées. Une combinaison/différage peut changer l’ordre ou supprimer une dépendance, sans impacter les pages simples.

Que faire si ModSecurity bloque des commandes aléatoirement ?

Collectez l’heure, l’IP, l’URL et le code d’erreur, puis demandez à l’hébergeur la règle déclenchée. La solution propre est la whitelist ciblée (URL/règle), pas la désactivation globale du WAF.

Elementor/Divi/Avada sont-ils “responsables” du bug ?

Pas directement, mais leurs options de performance et leurs templates peuvent modifier le chargement d’assets ou la structure de la page checkout. Le test “page checkout minimale” permet de trancher rapidement.

Pourquoi j’obtiens “Invalid nonce” après une mise à jour ?

Souvent parce que votre cache sert une ancienne version du checkout. Purgez cache plugin + cache serveur + CDN, puis mettez en place des exclusions durables.

Dois-je activer WP_DEBUG en production pour diagnostiquer ?

Évitez. Faites-le sur staging. Si vous n’avez pas le choix, activez WP_DEBUG_LOG et gardez WP_DEBUG_DISPLAY à false pour ne pas exposer d’informations sensibles.

Le checkout renvoie du HTML au lieu de JSON dans la réponse AJAX : c’est grave ?

C’est un indicateur fort de redirection (HTTPS, cookies, page de sécurité) ou d’erreur interceptée par un proxy/WAF. Inspectez l’onglet Réseau et corrigez la couche qui injecte ce HTML.