remotion-dev/remotion · error · Error

Signatures do not match

Error message

Signatures do not match

What it means

Thrown by validateWebhookSignature() when the incoming signatureHeader does not equal the HMAC computed over the body and the shared secret. A mismatch means the request was not authentic, was tampered with, or used a different secret.

Source

Thrown at packages/lambda-client/src/validate-webhook-signature.ts:40

			"No 'body' was provided to validateWebhookSignature().",
		);
	}

	if (typeof require === 'undefined') {
		throw new Error('validateWebhookSignature can only be called from Node.JS');
	}

	const Crypto = require('crypto');

	const hmac = Crypto.createHmac('sha512', secret);
	const signature = `sha512=${hmac.update(JSON.stringify(body)).digest('hex')}`;

	if (!signatureHeader || signatureHeader === 'NO_SECRET_PROVIDED') {
		throw new Error('No webhook signature was provided');
	}

	if (signatureHeader !== signature) {
		throw new Error('Signatures do not match');
	}
};

View on GitHub (pinned to 78fe4bb3fd)

Solutions

  1. Capture and HMAC the RAW request body (the exact bytes Remotion signed), not a re-serialized object — use express.json({verify}) or a raw-body middleware.
  2. Confirm the secret string matches byte-for-byte what was passed to renderMediaOnLambda() (no trailing newline, same encoding).
  3. Reject the request with 401 on signature mismatch and log nothing sensitive.

Example fix

// before (re-serialized body, drifts from signed bytes)
app.post('/webhook', express.json(), (req, res) => {
  validateWebhookSignature({secret, body: req.body, signatureHeader: req.headers['x-remotion-signature']});
});

// after (capture raw body)
app.post('/webhook', express.json({verify: (req, _res, buf) => { req.rawBody = buf; }}), (req, res) => {
  validateWebhookSignature({secret, body: JSON.parse(req.rawBody), signatureHeader: req.headers['x-remotion-signature']});
});
Defensive patterns

Strategy: try-catch

Validate before calling

// Capture the RAW request body — the exact bytes Remotion signed.
app.post('/webhook', express.json({verify: (req, _res, buf) => { req.rawBody = buf; }}), (req, res) => {
  try {
    validateWebhookSignature({secret, body: JSON.parse(req.rawBody), signatureHeader: req.headers['x-remotion-signature']});
    res.status(200).send('ok');
  } catch {
    res.status(401).send('unauthorized');
  }
});

Try / catch

try {
  validateWebhookSignature({secret, body, signatureHeader});
} catch (err) {
  // Do NOT distinguish 'Signatures do not match' from other failures in the response.
  return res.status(401).send('unauthorized');
}

Prevention

When it happens

Trigger: The body the receiver parsed differs from the raw body Remotion signed (most common), the secret on the receiver differs from the one passed to renderMediaOnLambda(), or the request was replayed/tampered.

Common situations: Express re-serializing req.body with different key order / whitespace than the raw JSON Remotion used to compute the HMAC; a typo in the secret; checking the header before body-parser has run; an attacker actually tampering.

Related errors


AI-assisted analysis of remotion-dev/remotion@78fe4bb3fd (2026-08-12). Data as JSON: /api/errors/1882e284ebf3ca92. Report an issue: GitHub.