« 403 Forbidden ». C’est le seul message renvoyé par le service de paiement fractionné intégré au site d’un commerçant, chaque fois qu’il tentait d’envoyer une notification de mise à jour de statut de commande vers l’URL de webhook WordPress prévue à cet effet. Le service fonctionnait parfaitement en environnement de test, chez le prestataire lui-même, mais échouait systématiquement en production, sans qu’aucune erreur ne remonte côté WordPress : la requête n’atteignait tout simplement jamais le code PHP censé la traiter.
Symptôme : une requête qui semble ne jamais arriver
Le premier réflexe a été de vérifier le code de traitement du webhook lui-même, en ajoutant des journaux au tout début de la fonction. Aucun de ces journaux n’apparaissait, même lors d’un test manuel avec curl reproduisant fidèlement la requête du service tiers. Ce comportement, une absence totale de trace côté application malgré une requête bien envoyée, est le signe caractéristique d’un blocage intervenant en amont de WordPress, au niveau du serveur ou d’une couche de sécurité intermédiaire.
Diagnostic : consulter les journaux du pare-feu applicatif

Le site utilisait un pare-feu applicatif (WAF) mutualisé, fourni par l’hébergeur, avec un jeu de règles générique appliqué à tous les sites hébergés. Les journaux spécifiques du WAF, distincts des journaux d’accès classiques, ont montré la vraie cause :
[BLOCKED] rule_id=942100 (SQLi generic)
ip=54.x.x.x path=/wp-json/paiement/v1/webhook
reason: suspicious characters in POST body
La règle en question, une règle générique de détection d’injection SQL basée sur la présence de certains caractères spéciaux dans le corps d’une requête POST, se déclenchait sur le contenu JSON envoyé par le service de paiement, qui incluait légitimement des caractères comme ' ou -- dans certains champs de description de commande (un nom de produit contenant une apostrophe, par exemple).
Confirmer l’hypothèse avant de corriger
Pour valider ce diagnostic sans attendre une nouvelle notification réelle, une requête de test reproduisant exactement le corps JSON typique du service, envoyée directement en curl, a permis de confirmer que c’était bien ce contenu précis qui déclenchait le blocage, et non un problème d’adresse IP ou de méthode HTTP.
Correctif : une exception ciblée plutôt qu’une désactivation
La tentation la plus courante, face à un WAF trop strict, est de le désactiver purement et simplement sur le site concerné. C’est une mauvaise idée : cela retire toute protection contre les vraies tentatives d’injection SQL sur l’ensemble des autres routes du site. La bonne approche consiste à créer une exception précise, limitée à la route du webhook et à l’adresse IP source connue du service de paiement :
# Exception WAF ciblée
rule exception:
path = /wp-json/paiement/v1/webhook
source_ip = 54.x.x.x/32
disable_rule = 942100
Cette configuration, propre à la plupart des WAF en frontal (Cloudflare, Sucuri, ou les solutions fournies par l’hébergeur), laisse la règle générique active pour tout le reste du trafic, en ne créant une brèche que là où elle est justifiée.
Prévention : documenter les intégrations qui envoient du contenu inhabituel
Ce type de faux positif se reproduit à chaque nouvelle intégration qui envoie du contenu textuel varié dans un corps de requête (description de produit, commentaire client, adresse). Documenter, dès la mise en place d’un webhook, les caractères et formats que le service tiers est susceptible d’envoyer permet d’anticiper ce genre de blocage avant qu’il ne se produise en production, plutôt que de le découvrir au premier incident client.
Un WAF configuré en amont d’un site ne doit jamais rester une boîte noire : ses journaux, une fois consultés, expliquent presque toujours pourquoi une requête légitime a été rejetée.
Pour aller plus loin
La configuration initiale du WAF, ses règles par défaut et leur pertinence pour un site donné, sort du cadre de cet article. Ce qui compte ici : face à un 403 inattendu sur une intégration tierce, chercher d’abord du côté des couches intermédiaires avant de suspecter le code de l’application elle-même, et corriger par une exception ciblée plutôt que par une désactivation générale.