Webflow webhook signature missing or failing? Start with how the webhook was created

Webflow signs webhook deliveries with hex HMAC-SHA256 in x-webflow-signature — but whether you get that header at all, and which secret verifies it, both depend on how the webhook was created. That one fact explains most “signature missing” and “signature never matches” reports. Here's the debugging order.

Cause 1: the header isn't there — dashboard webhooks are unsigned

If x-webflow-signature and x-webflow-timestamp are simply absent, nothing is broken and nobody is attacking you. Webflow's docs say it plainly: webhooks created through the site dashboard “will not include the request headers needed to validate request signatures”. Dashboard webhooks send no signature headers at all.

Fix: recreate the webhook through the API — either from an OAuth app or with a site token — and you'll get signed deliveries. If your handler requires the header (most SDK-based verifiers throw on a missing header), a dashboard-created webhook will fail 100% of deliveries, which also marches you toward deactivation (see the retry section below).

Cause 2: verifying with the wrong secret — there are two kinds

Webflow has two different signing keys depending on creation method:

Mixing these up — verifying a site-token webhook with your app's client secret, or vice versa — produces a signature mismatch on every single delivery. If you rotate the OAuth client secret, every OAuth-app webhook's signatures change with it.

Cause 3: the timestamp is in MILLISECONDS

The signed string is the timestamp and raw body joined with a colon:

expected = hex( HMAC_SHA256( key = signing_key, message = timestamp + ":" + raw_body ) )

…and x-webflow-timestamp is unix milliseconds, not seconds. Webflow's own replay-window sample computes currentTime - timestamp > 300000 (5 minutes in ms). Code that treats the header as seconds either rejects every delivery as “too old” or accepts week-old replays — the same family of bug as WorkOS's ms timestamps. The colon separator is the other classic miss: sign ts + ":" + body, not ts + body.

Cause 4: re-serializing the body — the docs' own sample has this trap

Webflow's manual-validation docs show timestamp + ":" + JSON.stringify(request_body) — stringifying the parsed body. That only matches while your JSON parser round-trips byte-identically (key order, unicode escapes, number formatting). Form-submission payloads are especially risky: field names arrive as display labels with spaces ("First Name") under payload.data, and any middleware that touches the JSON re-serializes it. HMAC the raw request bytes; read them before any body-parser runs.

Failures, retries, and the deactivation email

All of these count as failed deliveries: any non-200 status, a redirect (Webflow doesn't follow them — a 301 on your webhook path fails every delivery), SSL certificate problems, and slow responses. A failed delivery is retried 3 times at 10-minute intervals, then it's gone — there's no replay. Repeated failure gets the webhook deactivated, with an email notification, and reactivation goes through Webflow support — not a dashboard toggle. Also worth knowing: max 75 webhooks per trigger type, and triggerType in the payload tells you which of the event types fired.

Debug it with a capture bin

Webflow's own webhook tutorial points you at webhook.site to inspect deliveries. A Hookden bin does the same job free, and goes further: select the Webflow scheme in bin settings, paste your signing key, and every delivery gets a live ✓/✗ badge — computed over the exact raw bytes, ms timestamp and colon included.

  1. Create a bin (button below) and register a webhook against it:
    curl -X POST "https://api.webflow.com/v2/sites/{site_id}/webhooks" \
      -H "Authorization: Bearer {token}" -H "Content-Type: application/json" \
      -d '{"triggerType":"form_submission","url":"https://hookden.pages.dev/h/YOUR-BIN"}'
  2. Submit the form. The bin shows the full delivery — headers present or absent, exact bytes. No x-webflow-signature in the capture = you have a dashboard/unsigned webhook; recreate it via the API. (See a realistic sample payload with a verifying signature.)
  3. Signature present but your code rejects it? Check the ✓/✗ badge with your key. Badge ✓ but your handler ✗ → your code has the ms/colon/raw-body bug (causes 3–4). Badge ✗ too → wrong secret (cause 2). You can also verify offline in the client-side signature debugger (Webflow provider).
  4. One-click replay sends the byte-identical body to your real endpoint, so the signature still verifies downstream.

Related: retry-schedule comparison across 26 providers, webhooks to localhost for developing the handler locally, and the Webflow payload example whose signature actually verifies.

No signup needed. Or from your terminal: curl https://hookden.pages.dev/new

← All guides · Signature debugger · Payload examples · Docs · Hookden vs webhook.site