Tutorials

Verify Upmonora Webhook Signatures in PHP and Node.js

Verify the X-Upmonora-Signature HMAC-SHA256 header on incoming webhooks in PHP and Node.js, with constant-time comparison.

Upmonora teamSeptember 10, 20262 min read

Overview - Verify Upmonora Webhook Signatures in PHP and Node.js

Each webhook delivery includes X-Upmonora-Event, X-Upmonora-Delivery and X-Upmonora-Signature: sha256=<hex>. The signature is an HMAC-SHA256 of the raw request body using your channel's signing secret (shown once when you create the webhook).

PHP

$body = file_get_contents('php://input');
$expected = 'sha256=' . hash_hmac('sha256', $body, getenv('UPMONORA_WEBHOOK_SECRET'));
$given = $_SERVER['HTTP_X_UPMONORA_SIGNATURE'] ?? '';

if (!hash_equals($expected, $given)) {
    http_response_code(401);
    exit('Invalid signature');
}
$event = json_decode($body, true);
// $event['event'] === 'monitor.down', $event['monitor'], $event['incident'] …
http_response_code(204);

Node.js (Express)

import crypto from 'node:crypto';
import express from 'express';

const app = express();
app.post('/webhooks/upmonora', express.raw({ type: 'application/json' }), (req, res) => {
  const expected = 'sha256=' + crypto.createHmac('sha256', process.env.UPMONORA_WEBHOOK_SECRET)
    .update(req.body).digest('hex');
  const given = req.get('X-Upmonora-Signature') || '';
  const ok = given.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(given), Buffer.from(expected));
  if (!ok) return res.status(401).send('Invalid signature');
  const event = JSON.parse(req.body);
  console.log(event.event, event.monitor);
  res.sendStatus(204);
});

Tips

  • Always verify against the raw body, before JSON parsing.
  • Return a 2xx quickly; non-2xx responses are retried with back-off.
  • Use X-Upmonora-Delivery to de-duplicate retries.