CanHook
Features Pricing Docs FAQ Blog Log in Get started free

How to Rotate a Webhook Signing Secret With Zero Downtime

By The CanHook Team · August 31, 2026 · 1219 words
In short

Rotate a webhook signing secret safely by verifying against both the old and new secret during a short overlap window, confirming real deliveries validate against the new one, then removing the old secret from your code and revoking it at the provider. Swapping the secret in a single step breaks every webhook until your new code deploys.

Your webhook signing secret leaked into a log file, or your security policy requires rotating it every quarter, so you generate a new one and paste it into your code. If you swap the value in a single step, every webhook that lands between the provider's config change and your next deploy fails signature verification, and a handler that correctly rejects bad signatures starts dropping real events, not test ones. A webhook signing secret is the shared value both your server and the sending provider use to compute an HMAC over each request, and a clean rotation runs through a short window where your endpoint verifies against both the old and the new secret at once.

Why Can't You Just Swap the Secret in One Step?

Because the provider and your server both need to compute the same HMAC from the same secret, and that change is never atomic across two systems you do not control together. The instant you update the secret in the provider's dashboard, it starts signing new deliveries with the new value, but your server keeps running the old code until you deploy, so verification fails for everything in between. If your handler returns a non-2xx status on a bad signature, which is the correct behavior, the provider queues the event for retry instead of discarding it, so you do not lose data, but you do generate alert noise and a backlog you have to work through.

What Does a Dual-Secret Verification Window Look Like?

The fix is to make your verification code accept either secret for a short period, then narrow back down to one once you have confirmed the cutover worked.

  1. Generate the new secret without deleting the old one, if the provider lets you keep both active.
  2. Deploy verification code that checks the new secret first and falls back to the old secret if that check fails.
  3. Update the provider's dashboard to sign with the new secret, or add it as a second signing key where that is supported.
  4. Capture live traffic during the overlap window and confirm requests are validating against the new secret specifically, not just landing on the fallback.
  5. Remove the old secret from your verification code once nothing is hitting the fallback path.
  6. Revoke the old secret at the provider so a copy sitting in an old log file or a leaked environment variable stops being useful to anyone.

How Do Different Providers Handle Rotation?

Support for running two secrets at once is not universal, so check your provider before you plan the cutover.

ProviderMultiple active secrets?Practical approach
StripeYes, rolling a webhook endpoint's secret keeps the old one valid for a short overlap periodRoll the secret in the dashboard, deploy dual-secret code first, confirm, then let the old one lapse
GitHubNo, one secret per webhookPoint traffic at a second, temporary webhook configured with the new secret, migrate, then delete it
ShopifyNo, one secret per webhook subscriptionSame second-webhook approach as GitHub
SlackOne signing secret per app, used for every eventRegenerating it invalidates the old value immediately, so deploy your code first

GitHub, Shopify, and most single-secret providers give you no server-side overlap at all, so a second temporary endpoint is what buys you the overlap window they do not. Single-secret storage is common precisely because it is simpler to build and audit, so do not assume overlap support without checking the provider's own settings page first, the behavior changes between vendors and between API versions of the same vendor.

What Does the Verification Code Look Like?

The change from single-secret to dual-secret verification is small. Try each candidate secret in order and accept the first match.

function verifySignature(string $rawBody, string $signatureHeader, array $secrets): bool
{
    foreach ($secrets as $secret) {
        $expected = hash_hmac('sha256', $rawBody, $secret);
        if (hash_equals($expected, $signatureHeader)) {
            return true;
        }
    }
    return false;
}

$ok = verifySignature($rawBody, $signatureHeader, [$newSecret, $oldSecret]);

The same pattern in Node.js uses a timing-safe comparison instead of a plain string check, since a variable-time compare on a signature is its own small vulnerability.

function verifySignature(rawBody, signatureHeader, secrets) {
  const sigBuf = Buffer.from(signatureHeader, 'hex');
  for (const secret of secrets) {
    const expected = crypto.createHmac('sha256', secret).update(rawBody).digest();
    if (expected.length === sigBuf.length && crypto.timingSafeEqual(expected, sigBuf)) {
      return true;
    }
  }
  return false;
}

Put the new secret first in both versions. Log which entry in the array matched, at info level, while the fallback is in place. When the old-secret branch stops firing entirely across a full day of traffic, that is your signal the rotation is complete, and the deploy after that drops it from the list so you are back to a normal single-secret check.

How Do You Confirm the New Secret Works Before You Cut Over?

Compute the HMAC by hand against a real captured request before any of that traffic reaches your production handler. A CanHook catch URL is a disposable endpoint that stores whatever headers and body arrive, valid signature or not, so pointing a temporary or duplicate webhook at it during the overlap window lets you read back the exact signature header the provider actually sent and confirm your new secret reproduces it. You can see what a captured request looks like on the CanHook dashboard before you wire up anything real. The same capture is also useful for inspecting the raw request the provider sends, and once you have one saved you can replay it against your local dev server as many times as you need while you iterate on the dual-secret code, the same way you would for verifying a provider's normal signature scheme, such as GitHub's HMAC-SHA256 headers.

What's the Rollback Plan If the Rotation Breaks?

Because you have not removed the old secret yet, traffic keeps flowing on it if the new one fails to validate for some reason, an unexpected encoding difference or a copy-paste error in the value are the usual culprits. If the provider supports re-rolling, generate a fresh secret and repeat the dual-secret step rather than trying to debug the broken one under pressure. This is the entire argument for the overlap window: a one-step swap has no rollback beyond restoring the old secret at the provider and hoping you catch it before too many retries burn through.

Is This Overkill for a Small Side Project?

For low-volume, non-critical webhooks, dropping a handful of retried events during a short deploy window is a real but survivable trade-off, most providers retry a failed delivery for a day or more, which covers a slow deploy. If the webhook drives billing, inventory, or any state change a customer would notice, a one-step swap is a genuine incident risk for the cost of maybe ten extra lines of code, and that is not a close call. Pick based on what breaks downstream, not on how the code looks. Write the decision down once, in a comment next to the verification function, so the next person rotating the secret does not have to re-litigate it under time pressure.

You do not need a live production webhook to test any of this first. Create a free CanHook endpoint at canhook.com, the free tier includes catch URLs with no relay setup required, point a temporary webhook or a couple of curl requests at it, and read back the exact signature header your dual-secret code needs to match.

Frequently asked questions

What is a webhook signing secret?

A webhook signing secret is a value shared between your server and the sending provider, used to compute an HMAC over each request body so your endpoint can confirm the request actually came from that provider and was not altered in transit.

Why does rotating a webhook secret in one step break deliveries?

The provider starts signing with the new secret the moment you save it in their dashboard, but your server keeps checking against the old secret until you redeploy, so every request in that window fails verification and gets rejected or retried.

How long should the dual-secret overlap window last?

Only as long as it takes you to confirm the new secret is validating real traffic and nothing is falling through to the fallback check, typically the time for one deploy plus enough live requests to be confident, then remove the old secret.

Can I rotate a GitHub or Shopify webhook secret without downtime?

Not on the same webhook, since both only store one active secret at a time. Configure a second, temporary webhook with the new secret, confirm it validates correctly, then delete the old webhook and the temporary one together.

What should my endpoint do if neither the old nor new secret matches?

Return a non-2xx status and do not process the payload. Most providers treat that as a failed delivery and retry it automatically, so you keep the data instead of losing it while you fix the rotation.

Does CanHook verify webhook signatures for me?

No, CanHook captures the raw headers and body exactly as they arrive so you can inspect the real signature value, but the HMAC verification itself happens in your own application code, using whichever secret or secrets you configure it to check.