SafeWebhook/ Guides
Launch Workbench
Express.jsNode.js / JavaScript

Express.js Webhook Handler Guide: Fix body-parser & Verify Signatures

Learn how to configure Express.js with `express.raw({ type: "application/json" })` for webhook signature verification without breaking standard JSON routes.

Direct Answer / Quick Implementation Guide

In Express.js, apply `express.raw({ type: "application/json" })` specifically to your `/api/webhook` route BEFORE applying global `app.use(express.json())`. This ensures the raw `Buffer` is available on `req.body` for cryptographic HMAC verification.

Essential Implementation Takeaways

  • Mount `express.raw({ type: "application/json" })` before global `express.json()`.
  • Use `crypto.timingSafeEqual` to compare computed hashes against headers.
  • Convert raw buffer to JSON after signature validation via `JSON.parse(req.body.toString())`.
  • Respond with `res.status(200).send({ received: true })`.

Common Gotchas in Express.js & How to Fix Them

Placing `app.use(express.json())` at top of file
Fix:Move `app.post("/webhook", express.raw(...))` above all global body parsing middleware.

Complete Production Boilerplate

Node.js / JavaScript
// server.js (Express.js Webhook Receiver)
const express = require('express');
const crypto = require('crypto');
const app = express();

// 1. Webhook route with raw body buffer
app.post('/api/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-hub-signature-256'];
  const secret = process.env.WEBHOOK_SECRET;

  if (!signature) {
    return res.status(400).send('Missing signature');
  }

  // Compute HMAC digest using raw buffer
  const hmac = crypto.createHmac('sha256', secret);
  const digest = 'sha256=' + hmac.update(req.body).digest('hex');

  const isValid = crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(digest));
  if (!isValid) {
    return res.status(401).send('Signature mismatch');
  }

  // Parse JSON now that signature is verified
  const event = JSON.parse(req.body.toString('utf8'));
  console.log('Received valid event:', event);

  res.status(200).json({ received: true });
});

// 2. Global JSON parser for rest of your API
app.use(express.json());

app.listen(3000, () => console.log('Server running on port 3000'));

Step-by-Step Setup Guide

1
Define the webhook route before registering global middleware.
2
Attach `express.raw({ type: "application/json" })` to the webhook route.
3
Perform timing-safe HMAC validation with Node’s built-in `crypto` module.
4
Parse the verified buffer with `JSON.parse(req.body.toString())`.
5
Acknowledge with HTTP 200.

How to Test Locally on Your Machine

  • Run `node server.js` locally on port 3000.
  • In SafeWebhook, copy the cURL export command for any captured webhook.
  • Execute the cURL command targeting `http://localhost:3000/api/webhook`.
  • Verify the event logs in your terminal.

Frequently Asked Questions: Express.js Webhooks

Can I capture the raw body inside express.json() verify callback?

Yes, you can use `express.json({ verify: (req, res, buf) => { req.rawBody = buf; } })` to attach the buffer to all incoming requests.

Test Your Express.js Webhook Handler Live

Capture payloads, simulate errors, and replay directly to your localhost server.

Launch Webhook Workbench →

Explore Other Webhook Framework Guides