Stripe webhooks on Fastify: parse the body as a buffer for signature checks
Fastify's default JSON parser hands your handler a parsed object, which fails Stripe signature verification. This skill shows the addContentTypeParser override that keeps the raw bytes.
Stripe webhooks on Fastify
Fastify parses JSON request bodies into objects before your handler runs. stripe.webhooks.constructEvent needs the raw bytes. Override the content type parser so the body arrives as a Buffer.
The setup
import Fastify from 'fastify';
import Stripe from 'stripe';
const fastify = Fastify();
const stripe = new Stripe(process.env.STRIPE_API_KEY_VALUE);
// Keep the raw bytes for Stripe's signature check
fastify.addContentTypeParser(
'application/json',
{ parseAs: 'buffer' },
function (req, body, done) {
done(null, body);
}
);
fastify.post('/webhooks/stripe', async (req, reply) => {
const sig = req.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(
req.body.toString('utf8'),
sig,
process.env.STRIPE_WEBHOOK_SIGNING_VALUE
);
} catch (err) {
return reply.code(400).send('bad signature');
}
// switch on event.type ...
return reply.code(200).send({ received: true });
});Scope it if you can
Overriding the parser globally changes body handling for every route. If the app has other JSON routes, register the webhook in a scoped plugin with its own parser, or check the URL inside the parser and only buffer the webhook path.
Checklist
parseAs: 'buffer'is the documented Fastify option for raw access.- Convert the Buffer to a UTF-8 string before passing it to constructEvent.
- Stripe sends webhooks as
application/json, so match that content type exactly.
Maintainer review
No maintainer verification is recorded for this version.
This records the version a maintainer checked. It does not assert that the version is the latest upstream release.