Comparateur indépendant · sans classement payant
Accueil / Blog / Technique / Webhooks fiables : signer, rejouer et dédupliquer les événements

Webhooks fiables : signer, rejouer et dédupliquer les événements

Stripe renvoie un paiement, votre endpoint répond 500, ils rejouent — et vous double-créditez le client. Un webhook fiable signe, idempotent et observable.

Rédaction Hébergeurs.eu 5 min

Un webhook paiement arrive à 02:14 pendant un deploy. L'app répond 503. Le provider réessaie trois fois. Le matin, le client a trois abonnements actifs et une seule facture. Personne n'avait implémenté l'idempotence — seulement un INSERT naïf.

Les webhooks ne sont pas des « callbacks HTTP sympas ». Ce sont des livraisons at-least-once avec retries, ordre non garanti, et parfois des minutes de décalage.

Modèle de livraison

Assumez at-least-once : doublons normaux. Parfois ordering non-FIFO entre types d'événements. Votre handler doit être idempotent et tolérer le réordonnancement logique (ex. cancelled avant paid rare mais possible).

Signature et horodatage

Vérifiez HMAC-SHA256 avec secret rotatable. Rejetez si timestamp > 5 min (replay). Constant-time compare pour la signature.

Logguez les échecs de signature séparément des 500 applicatifs — ne confondez pas attaque et bug.

Idempotence en pratique

ApprocheAvantageLimite
event_id en baseSimpleTTL cleanup
Idempotency-Key headerStandard APISupport provider variable
État métier (paid_at)Robuste métierRace conditions

Combinez clé provider + contrainte unique SQL.

Réponses HTTP et async

Traitement < 2 s : 200 sync. Sinon 202 + queue (Redis, SQS, Rabbit). Le worker reprend event_id. Jamais de travail lourd avant d'acquitter sans idempotence.

Dead-letter queue pour échecs métier après N tentatives.

Observabilité

Métriques : latence handler, taux 2xx/4xx/5xx, lag queue, doublons ignorés. Trace correlée event_id → job → facture. Runbook : « replay manuel sans double effet » documenté.

Checklist endpoint production

  • [ ] Signature HMAC vérifiée constant-time
  • [ ] Timestamp anti-replay (<5 min)
  • [ ] Table event_id contrainte unique
  • [ ] Réponse 2xx/202 < timeout provider
  • [ ] Queue async pour traitement >2 s
  • [ ] Dead-letter après N échecs métier
  • [ ] Logs structurés event_id + latency
  • [ ] Idempotence testée (3 POST identiques)
  • [ ] Firewall IP provider documenté
  • [ ] Runbook replay manuel sans double effet
  • [ ] Rotation secret webhook planifiée
  • [ ] RGPD : rétention payload limitée

Cocher cette liste avant d'exposer l'URL publique — un webhook « presque bon » coûte plus qu'un retard release de deux jours.

Scénarios de replay et incident

Imaginez un deploy qui redémarre PHP-FPM pendant trente secondes : le provider paiement envoie cinq retries espacés. Sans table d'idempotence, vous créditez cinq fois le même paiement. Le support client découvre le bug avant vos logs applicatifs parce que le dashboard compta agrège les doublons plus vite que votre monitoring technique.

Un replay légitime diffère d'un replay malveillant : le premier vient du provider après timeout, le second d'un attaquant qui capture une URL webhook non signée (si vous avez oublié HMAC). La signature lie le corps au secret ; sans elle, rejouer un POST capturé suffit.

Pour les tests, utilisez l'outil de replay du provider (Stripe CLI, GitHub redelivery) en staging avec base jetable. Vérifiez que le troisième retry identique ne change rien en base. Automatisez ce test en CI si l'endpoint est critique.

Côté hébergement mutualisé, vérifiez max_execution_time PHP et timeout nginx proxy — un 504 gateway génère retries provider même si votre handler aurait fini deux secondes plus tard. Alignez timeouts : nginx > PHP > provider webhook timeout, avec marge.

Les providers diffèrent sur signature header (Stripe-Signature, X-Hub-Signature-256, etc.) — encapsulez la vérification par adapter, pas copy-paste doc une fois.

Versionnez le secret webhook comme password : rotation avec double secret actif pendant 24 h si provider le supporte.

Charge test : simuler 10× burst webhooks — queue depth et worker count dimensionnés avant Black Friday.

Tenez runbook daté, métriques avant/après, revue post-incident — la discipline cumulative évite la panique du vendredi soir.

Tenez runbook daté, métriques avant/après, revue post-incident — la discipline cumulative évite la panique du vendredi soir.

Timeouts mutualisé

Alignez nginx > PHP > timeout provider — 504 déclenche retries.

Décider et avancer sans angle mort

  1. Vérifier la signature HMAC en constant-time avec horodatage anti-replay inférieur à cinq minutes.
  2. Dédupliquer par event_id — contrainte unique en base, idempotence testée avec trois POST identiques.
  3. Répondre 2xx/202 rapidement — queue async si le traitement dépasse deux secondes ; dead-letter après N échecs métier.
  4. Documenter les IP provider — firewall, rotation secret webhook, runbook replay manuel sans double effet.
  5. Limiter la rétention payload — conformité RGPD sur les corps stockés.

Pour choisir un hébergement API fiable (latence, SLA), utilisez le comparateur, l'annuaire et nos guides observabilité.

Questions fréquentes

Pourquoi signer les webhooks ?

La signature HMAC prouve que le corps vient du provider et n'a pas été altéré en transit. Sans elle, n'importe qui peut POST sur votre endpoint.

Quel code HTTP pour les retries ?

2xx acquitte. 4xx non retryable si erreur permanente (signature invalide). 5xx ou timeout déclenchent retries — d'où l'idempotence obligatoire.

Comment dédupliquer ?

Stockez event_id (ou hash stable) avec contrainte unique ; ignorez les doublons déjà traités même si le corps diffère légèrement.

Timeout côté émetteur ?

Souvent 5–30 s. Répondez vite (202 + job async) si le traitement est long, mais garantissez alors l'idempotence côté worker.


Avant d'ouvrir l'endpoint en prod, simulez trois POST identiques — si la base change trois fois, vous n'êtes pas prêts.

Comparez les hébergeurs européens

Filtrez par conformité, localisation et usage — puis ouvrez les fiches pour vérifier le périmètre réel.

Voir l'annuaire
Blog

À lire aussi

Tous les articles →