Verification

Check that a webhook delivery came from RankDebug and has not been replayed.

Every delivery is signed with the webhook's secret, so your endpoint can reject anything that did not come from RankDebug. Verify before you act on a delivery.

The signatures

Each delivery carries two signatures, both HMAC-SHA256 keyed with the webhook secret and written as sha256= followed by the hex digest:

  • X-RankDebug-Signature-256 signs the timestamp and the body together, as {timestamp}.{body}, where the timestamp is the X-RankDebug-Timestamp header. Use this one. Because the timestamp is signed, a captured delivery cannot be sent again later.
  • X-RankDebug-Signature signs the raw body alone. It is kept for receivers that already check it.

Always sign the raw request body exactly as received, before any JSON parsing.

Steps

  1. Read X-RankDebug-Timestamp and refuse the delivery if it is more than 300 seconds old.
  2. Compute HMAC-SHA256 of {timestamp}.{raw body} with your secret, hex-encoded.
  3. Compare it with the value after sha256= in X-RankDebug-Signature-256, using a constant-time comparison.

Example

import crypto from 'node:crypto';

const TOLERANCE_SECONDS = 300;

export function verifyRankDebugWebhook(
  rawBody: string,
  headers: Record<string, string | undefined>,
  secret: string,
): boolean {
  const timestamp = Number(headers['x-rankdebug-timestamp']);
  const signature = headers['x-rankdebug-signature-256'] ?? '';
  if (!Number.isFinite(timestamp)) return false;
  if (Math.abs(Date.now() / 1000 - timestamp) > TOLERANCE_SECONDS) return false;

  const expected =
    'sha256=' +
    crypto.createHmac('sha256', secret).update(`${timestamp}.${rawBody}`).digest('hex');

  const a = Buffer.from(signature);
  const b = Buffer.from(expected);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

Rotating the secret

Send a new secret when updating the webhook, or use Rotate Secret when editing the webhook in the dashboard. The new secret is shown once. It applies from the next attempt, including retries of deliveries that were already queued, so update your receiver at the same time.

Related documentation
  • Webhooks

    Receive RankDebug events at your own HTTPS endpoint, signed so you can trust them.

  • Events

    The events a webhook can subscribe to, when they fire, and what their data contains.

  • Retries and Deliveries

    How failed deliveries are retried, when a webhook is paused, and how to inspect and replay deliveries.

  • Webhooks

    Create and manage webhook endpoints, send test events, and read and replay deliveries.

Was this helpful?

On this page