{ Webhook Signature Verifier }

// verify webhook signatures with hmac in one click

Verify webhook payload signatures using HMAC-SHA256, SHA1, SHA512 and shared secrets. Supports GitHub, Stripe, Slack and custom schemes. Free, browser-based.

PRESETS:
e.g. sha256= for GitHub, v1= for Stripe
Paste the X-Hub-Signature-256, Stripe-Signature, etc. header value here
🔐

Ready to verify

Fill in payload, secret, and signature then click Verify

HOW TO USE

  1. 01
    Select a Preset

    Choose GitHub, Stripe, or Slack to auto-fill the algorithm and prefix, or pick Custom for your own scheme.

  2. 02
    Paste Your Data

    Add the raw webhook payload body, your shared secret, and the signature header value.

  3. 03
    Click Verify

    The tool computes the expected HMAC and compares it to your signature using timing-safe comparison.

FEATURES

HMAC-SHA256 HMAC-SHA1 HMAC-SHA512 GitHub Preset Stripe Preset Slack Preset Hex & Base64 Generate Mode

USE CASES

  • 🔧 Debug webhook signature failures
  • 🔧 Test webhook receivers locally
  • 🔧 Validate third-party webhook configs
  • 🔧 Learn HMAC signature schemes

WHAT IS THIS?

Webhook Signature Verifier lets you check whether a webhook payload signature matches a shared secret using HMAC algorithms. Useful for debugging GitHub, Stripe, Slack, or any custom webhook integration — all client-side, no data leaves your browser.

RELATED TOOLS

FREQUENTLY ASKED QUESTIONS

Is my secret or payload data sent to a server?

No. All computation happens in your browser via a minimal server-side PHP HMAC call. Your payload and secret are transmitted only over HTTPS to our own server and never logged or stored. If you prefer 100% client-side, you can replicate this using the Web Crypto API.

Which algorithm should I use for GitHub webhooks?

GitHub uses HMAC-SHA256 with the prefix sha256=. The header is called X-Hub-Signature-256. Use the GitHub preset button to configure this automatically.

How does Stripe webhook verification work?

Stripe includes a Stripe-Signature header with a timestamp and one or more signatures separated by commas. Each signature uses HMAC-SHA256 over timestamp.payload. Use the Stripe preset and include the full raw body.

What is a timing-safe comparison?

Timing-safe (or constant-time) comparison prevents timing attacks where an attacker guesses signatures byte-by-byte by measuring response time. PHP's hash_equals() function ensures comparison time is constant regardless of where strings differ.

Why does my GitHub signature not match?

Common causes: the payload body was modified (whitespace, encoding), the wrong secret was used, or the signature header includes the sha256= prefix in the comparison. Make sure you paste the raw body exactly as received.

Can I use this to generate signatures for testing?

Yes. Fill in your payload and secret, then click ⚡ GENERATE instead of Verify. The computed signature will be shown in the output — useful for seeding test data or debugging your webhook receiver.

What Is a Webhook Signature?

When a platform like GitHub, Stripe, or Slack sends a webhook to your server, it includes a cryptographic signature in the HTTP headers. This signature proves that the request genuinely came from the platform and that the payload body hasn't been tampered with in transit. If the signature doesn't match, you should reject the request.

💡 Looking for premium web development assets? MonsterONE offers unlimited downloads of templates, UI kits, and assets — worth checking out.

How HMAC Webhook Signatures Work

Most webhook providers use HMAC (Hash-based Message Authentication Code) to generate signatures. The process is straightforward: the provider computes HMAC(algorithm, secret, payload) where the secret is a string you configured when setting up the webhook. They attach this hash (often with a prefix like sha256=) to a request header. When your server receives the webhook, it performs the same computation and compares the results.

The critical detail is that you must use a timing-safe comparison — a function like PHP's hash_equals() or Python's hmac.compare_digest() — rather than a regular string equality check. Naive comparison opens your endpoint to timing attacks.

GitHub Webhook Signature Verification

GitHub signs webhooks using HMAC-SHA256. The signature appears in the X-Hub-Signature-256 header with the format sha256=<hex-digest>. To verify it, compute HMAC-SHA256(secret, raw_body), prepend sha256=, and compare to the header using a constant-time function. GitHub also sends the older X-Hub-Signature (SHA1) for legacy compatibility.

Stripe Webhook Signature Verification

Stripe's approach is slightly different. The Stripe-Signature header contains a timestamp (t=...) and one or more signature values (v1=...). The signed string is <timestamp>.<payload>. Stripe recommends you also validate that the timestamp is recent (within 300 seconds) to prevent replay attacks. Our tool handles the signature portion — always combine with timestamp validation in production.

Slack Webhook Signatures

Slack uses HMAC-SHA256 over v0:<timestamp>:<body>. The header is X-Slack-Signature with a v0= prefix. Slack also provides an X-Slack-Request-Timestamp header you should validate to within 5 minutes to prevent replay attacks.

Common Causes of Signature Mismatch

Webhook signature failures are a common debugging headache. Here are the most frequent causes:

Implementing Webhook Verification in PHP

Here's a production-ready snippet for verifying a GitHub webhook in PHP:

$payload  = file_get_contents('php://input');
$secret   = getenv('GITHUB_WEBHOOK_SECRET');
$header   = $_SERVER['HTTP_X_HUB_SIGNATURE_256'] ?? '';
$computed = 'sha256=' . hash_hmac('sha256', $payload, $secret);

if (!hash_equals($computed, $header)) {
    http_response_code(401);
    exit('Signature mismatch');
}

Implementing Webhook Verification in Node.js

The same pattern works in Node.js using the built-in crypto module:

const crypto = require('crypto');

function verifyGitHubSignature(payload, secret, header) {
  const computed = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(computed),
    Buffer.from(header)
  );
}

Why Not Use MD5 or SHA256 Without HMAC?

A plain SHA256 hash of the payload with no secret can be reproduced by anyone — it provides no authentication. HMAC binds the hash to a secret key, so only parties who know the secret can generate a valid signature. MD5 and SHA1 are considered cryptographically weak for new integrations but remain in use for legacy systems. Prefer HMAC-SHA256 or HMAC-SHA512 for new implementations.