Agent skill

Cloudflare Webhooks Skill

Receive and authenticate Cloudflare Notifications webhooks (generic webhook destinations, not Workers). Use when setting up a Cloudflare alert webhook handler, debugging the cf-webhook-auth header, or handling alert_type values like advanced_ddos_attack_l7_alert, health_check_status_notification, dedicated_ssl_certificate_event_type and workers_alert. Cloudflare does not sign these webhooks: the secret you configure is sent verbatim in the cf-webhook-auth header.

Install this skill

npx skills add hookdeck/webhook-skills --skill cloudflare-webhooks


Cloudflare Notifications (Alerts) can deliver alerts to a generic webhook destination. Cloudflare sends an HTTP POST with a JSON body to your endpoint whenever a notification policy fires, for example a DDoS attack, a failing origin health check or an expiring certificate.

This skill covers Cloudflare Notifications webhooks only. It does not cover Cloudflare Workers, which are code you write and host.

When to Use This Skill

  • How do I receive Cloudflare Notifications / alert webhooks?
  • How do I verify / authenticate a Cloudflare webhook?
  • Why is my Cloudflare cf-webhook-auth check failing?
  • How do I handle advanced_ddos_attack_l7_alert or health_check_status_notification?
  • What does a Cloudflare webhook payload look like (name, text, data, alert_type)?
  • Why does the Cloudflare "Save and Test" message have no alert_type?

No Signature: Shared Secret Header Only

Cloudflare does not sign generic webhook payloads. There is no HMAC, no signature algorithm and no timestamp. When you create the webhook in the dashboard you can enter a Secret; Cloudflare then sends it verbatim in the cf-webhook-auth header of every request.

If this header is not present, or is not your specified value, you should reject the webhook. — Cloudflare docs

Cloudflare recommends always setting a secret for generic webhooks. Without one, nothing authenticates the request.

Verification (core)

const crypto = require('crypto');

function verifyCloudflareAuth(header, secret) {
  if (!header || !secret) return false;                  // fail closed
  const a = Buffer.from(String(header), 'utf8');
  const b = Buffer.from(String(secret), 'utf8');
  return a.length === b.length && crypto.timingSafeEqual(a, b); // length guard first
}

// header name is case-insensitive in HTTP; Node lowercases it
verifyCloudflareAuth(req.headers['cf-webhook-auth'], process.env.CLOUDFLARE_WEBHOOK_SECRET);

For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.

Cloudflare has no SDK with a webhook verification helper, so every example does this compare directly (crypto.timingSafeEqual in Node, hmac.compare_digest in Python). Authenticate before you parse the JSON body.

Common Event Types

The event name is the payload's alert_type field. These are the values shown in Cloudflare's webhook payload schema; other notification types use their own alert_type, and the shape of data varies by type.

alert_typeTriggered when
advanced_ddos_attack_l4_alertA Layer 3/4 DDoS attack is detected
advanced_ddos_attack_l7_alertA Layer 7 DDoS attack is detected
dedicated_ssl_certificate_event_typeA dedicated or custom SSL certificate event, such as expiration
health_check_status_notificationAn origin health check changes status
workers_alertA Workers usage alert fires
workers_observability_alertA Workers observability alert fires
access_custom_certificate_expiration_typeAn Access custom certificate is nearing expiration
(no alert_type)The "Save and Test" message sent from the dashboard: only a text field

Payload fields: name, text, data, ts (unix seconds), account_id, policy_id, policy_name, alert_type, alert_correlation_id and alert_event (ALERT_STATE_EVENT_START / ALERT_STATE_EVENT_END). Cloudflare says account_id, policy_id and alert_type may be absent for some alerts. See references/overview.md.

Environment Variables

# The value you entered in the Secret field of the Cloudflare webhook
CLOUDFLARE_WEBHOOK_SECRET=your_webhook_secret

With no secret configured, the examples reject every request with a 500 rather than accept unauthenticated webhooks.

Local Development

npx hookdeck-cli listen 3000 cloudflare --path /webhooks/cloudflare

No account required — the CLI creates a guest account on first run and gives you a public HTTPS URL plus a web UI for inspecting requests (including the cf-webhook-auth header). Use port 8000 for the FastAPI example.

Cloudflare delivers generic webhooks only to a publicly resolvable IP on port 80 or 443, so a localhost URL won't work without a tunnel like this.

Reference Materials


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 2, 2026

View on GitHub →