Introduction
Le webhook est au développement web ce que la notification push est au smartphone : au lieu de vérifier constamment si quelque chose s'est passé, vous êtes prévenu instantanément. En 2026, 62 % des entreprises utilisent des webhooks pour automatiser des workflows critiques (Zapier, 2024). Paiements Stripe, déploiements GitHub, synchronisation CRM — les webhooks sont partout.
Ce guide couvre l'intégralité du sujet : fonctionnement, implémentation Node.js/Express, sécurité (signatures HMAC), gestion des retries, idempotence et debugging. Avec des exemples de code prêts pour la production. Si vous cherchez un comparatif avec les approches traditionnelles, consultez notre article sur les API REST.

Comprendre les Webhooks : Communication Événementielle
Un webhook est un mécanisme de communication asynchrone : une application (l'émetteur) "pousse" des données vers une autre (le récepteur) dès qu'un événement se produit, via une requête HTTP POST.
Webhook vs API Polling
| Critère | API Polling | Webhook |
|---|---|---|
| Qui initie ? | Le client (requêtes répétées) | Le serveur (notification push) |
| Latence | Dépend de l'intervalle de polling | Quasi-instantané |
| Charge réseau | Élevée (requêtes inutiles à 95 %) | Minimale (uniquement si événement) |
| Complexité client | Faible (boucle de requêtes) | Moyenne (endpoint à exposer) |
| Fiabilité | Prévisible | Nécessite gestion des retries |
| Idéal pour | Données qui changent rarement | Événements en temps réel |
Tandis que les API REST ou GraphQL nécessitent une requête initiée par le client, les webhooks inversent ce flux — rendant les systèmes plus réactifs et économes en ressources.
Comment Fonctionne un Webhook ?
1. Enregistrement Vous configurez une URL sur votre serveur
→ https://votreapp.com/webhooks/stripe
2. Événement Un paiement est réussi sur Stripe
3. Notification Stripe envoie un POST vers votre URL
avec un payload JSON décrivant l'événement
4. Traitement Votre serveur parse le payload et exécute
la logique métier (marquer commande payée)
5. Confirmation Votre serveur répond 200 OK
(sinon Stripe retry automatiquement)
Cas d'Usage Concrets
Paiements et Abonnements (Stripe)
Le cas d'usage le plus critique. Quand un client paie via Stripe, votre application doit être notifiée instantanément. L'intégration Stripe repose entièrement sur les webhooks pour :
checkout.session.completed→ créer la commande en baseinvoice.payment_succeeded→ renouveler l'abonnementcustomer.subscription.deleted→ désactiver l'accès premiumcharge.refunded→ déclencher le remboursement côté comptabilité
CI/CD et Gestion de Code (GitHub)
Les plateformes Git utilisent intensément les webhooks pour déclencher les pipelines CI/CD :
push→ lancer les tests automatiséspull_request→ déclencher une review automatiquerelease→ déployer en production
Automatisation (n8n, Zapier)
Les webhooks sont le point d'entrée principal des plateformes d'automatisation. Un webhook entrant dans n8n peut déclencher un workflow complet : enrichissement CRM, notification Slack, génération de facture.
E-commerce (Shopify, WooCommerce)
order_created→ mettre à jour l'inventaireproduct_updated→ synchroniser le cataloguerefund_issued→ notifier le service client
Implémenter un Endpoint Webhook en Node.js
Endpoint de Base (Express)
import express from 'express';
const app = express();
// IMPORTANT : pour Stripe, le body doit être parsé en raw AVANT express.json()
app.post('/webhooks/stripe', express.raw({ type: 'application/json' }), (req, res) => {
const payload = req.body;
const sig = req.headers['stripe-signature'] as string;
let event;
try {
event = stripe.webhooks.constructEvent(payload, sig, process.env.STRIPE_WEBHOOK_SECRET!);
} catch (err) {
console.error('Webhook signature verification failed:', err.message);
return res.status(400).send(`Webhook Error: ${err.message}`);
}
// Traitement selon le type d'événement
switch (event.type) {
case 'checkout.session.completed':
await handleCheckoutCompleted(event.data.object);
break;
case 'invoice.payment_succeeded':
await handlePaymentSucceeded(event.data.object);
break;
case 'customer.subscription.deleted':
await handleSubscriptionCanceled(event.data.object);
break;
default:
console.log(`Unhandled event type: ${event.type}`);
}
res.status(200).json({ received: true });
});
Endpoint Webhook Générique
Pour recevoir des webhooks de n'importe quel service (pas seulement Stripe) :
app.post('/webhooks/:source', express.json(), async (req, res) => {
const source = req.params.source;
const payload = req.body;
const headers = req.headers;
// 1. Vérifier la signature (voir section sécurité)
if (!verifySignature(source, payload, headers)) {
return res.status(401).json({ error: 'Invalid signature' });
}
// 2. Répondre immédiatement (< 5 secondes)
res.status(200).json({ received: true });
// 3. Traiter en arrière-plan (via queue)
await webhookQueue.add('process-webhook', {
source,
payload,
receivedAt: new Date().toISOString()
});
});
Règle critique : répondez 200 OK avant de traiter le webhook. Si votre traitement prend plus de 5-10 secondes, le service émetteur considérera le webhook comme échoué et retentera. Utilisez une file d'attente Redis/BullMQ pour le traitement asynchrone.
Sécurité : Vérification des Signatures HMAC
Sans vérification de signature, n'importe qui peut envoyer un faux webhook à votre endpoint. La plupart des services signent leurs webhooks avec HMAC-SHA256.
Comment Fonctionne la Signature
1. Le service (Stripe) calcule un hash HMAC du body avec un secret partagé
2. Il envoie ce hash dans un header (ex: Stripe-Signature, X-Hub-Signature-256)
3. Votre serveur recalcule le hash avec le même secret
4. Si les deux hash correspondent, le webhook est authentique
Implémentation HMAC Générique
import crypto from 'crypto';
function verifyWebhookSignature(
payload: string | Buffer,
signature: string,
secret: string,
algorithm = 'sha256'
): boolean {
const expected = crypto
.createHmac(algorithm, secret)
.update(payload)
.digest('hex');
// Comparaison en temps constant pour éviter les timing attacks
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// Middleware de vérification
function requireSignature(secret: string, headerName: string) {
return (req: Request, res: Response, next: NextFunction) => {
const signature = req.headers[headerName] as string;
if (!signature || !verifyWebhookSignature(req.rawBody, signature, secret)) {
return res.status(401).json({ error: 'Invalid webhook signature' });
}
next();
};
}
La vérification de signature est un aspect critique de la sécurité des applications web — ne la sautez jamais en production.
Idempotence : Traiter un Webhook une Seule Fois
Les services émetteurs retentent les webhooks en cas d'échec (ou même parfois sans raison). Votre endpoint doit être idempotent — traiter le même événement deux fois ne doit pas créer de doublons.
Implémentation avec Deduplication
async function processWebhook(event: WebhookEvent) {
// Vérifier si cet événement a déjà été traité
const alreadyProcessed = await db.query(
'SELECT id FROM webhook_events WHERE event_id = $1',
[event.id]
);
if (alreadyProcessed.rows.length > 0) {
console.log(`Event ${event.id} already processed, skipping`);
return;
}
// Traiter l'événement dans une transaction
await db.transaction(async (tx) => {
// Enregistrer l'événement comme traité
await tx.query(
'INSERT INTO webhook_events (event_id, type, processed_at) VALUES ($1, $2, NOW())',
[event.id, event.type]
);
// Exécuter la logique métier
switch (event.type) {
case 'checkout.session.completed':
await createOrder(tx, event.data);
break;
// ...
}
});
}
La table webhook_events sert de registre de déduplication. Ajoutez un index unique sur event_id et un TTL pour nettoyer les anciennes entrées.
Gestion des Retries et Ordre des Événements
Politique de Retry des Principaux Services
| Service | Nombre de retries | Intervalle | Timeout |
|---|---|---|---|
| Stripe | 3 jours, ~15 tentatives | Backoff exponentiel | 20 secondes |
| GitHub | 3 tentatives | 10 secondes entre chaque | 10 secondes |
| Shopify | 48h, 19 tentatives | Backoff exponentiel | 5 secondes |
Ordre des Événements
Les webhooks ne garantissent pas l'ordre d'arrivée. Un invoice.paid peut arriver avant le checkout.completed correspondant. Deux stratégies :
1. Traitement tolérant à l'ordre — votre logique vérifie l'état actuel avant d'agir :
async function handlePaymentSucceeded(invoice: StripeInvoice) {
const order = await db.findOrder(invoice.metadata.orderId);
if (!order) {
// Commande pas encore créée — le webhook checkout arrivera après
// Stocker l'événement pour retraitement
await webhookQueue.add('retry-payment', invoice, { delay: 30_000 });
return;
}
await db.markOrderPaid(order.id);
}
2. Event sourcing — stocker tous les événements bruts et reconstruire l'état :
// Stocker l'événement brut
await db.query(
'INSERT INTO events (source, type, payload) VALUES ($1, $2, $3)',
['stripe', event.type, JSON.stringify(event.data)]
);
// Recalculer l'état depuis tous les événements
await rebuildOrderState(orderId);
Debugging des Webhooks
Outils de Développement Local
En développement local, votre serveur n'est pas accessible depuis internet. Utilisez un tunnel :
# ngrok — expose localhost sur une URL publique
ngrok http 3000
# → https://abc123.ngrok-free.app
# Configurer cette URL dans Stripe Dashboard
# Stripe CLI — plus simple pour Stripe spécifiquement
stripe listen --forward-to localhost:3000/webhooks/stripe
stripe trigger checkout.session.completed
Logging Structuré pour le Debug
app.post('/webhooks/:source', async (req, res) => {
const webhookId = crypto.randomUUID();
logger.info({
webhookId,
source: req.params.source,
eventType: req.body.type,
eventId: req.body.id,
headers: {
'content-type': req.headers['content-type'],
'user-agent': req.headers['user-agent']
}
});
try {
await processWebhook(req.body);
logger.info({ webhookId, status: 'processed' });
res.status(200).json({ received: true });
} catch (error) {
logger.error({ webhookId, error: error.message, stack: error.stack });
res.status(500).json({ error: 'Processing failed' });
}
});
Pour le monitoring en production, configurez des alertes sur les webhooks qui échouent de manière répétée — c'est souvent le signe d'un bug silencieux.
FAQ : Webhooks pour le Développement Web
FAQ - Questions fréquentes
Conclusion : Les Webhooks, Brique Essentielle de l'Architecture Moderne
Les webhooks ne sont pas un détail d'implémentation — c'est une brique architecturale fondamentale pour toute application web connectée. Maîtriser leur intégration (sécurité HMAC, idempotence, gestion des retries) est la différence entre un système fiable et un système qui perd des événements en silence.
Les règles à retenir : toujours vérifier les signatures, toujours répondre vite (200 OK avant traitement), toujours dédupliquer les événements, et toujours monitorer les échecs. Avec ces fondamentaux en place, vos webhooks deviennent un atout de fiabilité plutôt qu'une source de bugs.
Si vous concevez une application nécessitant des intégrations webhook robustes, contactez notre équipe. Nous construisons des applications web sur mesure et des SaaS avec des intégrations fiables et sécurisées.





