Automation Guide

Webhooks Made Easy: How to Receive and Process Webhooks Using Cloudflare Workers

A secure, fast, and cheap webhook endpoint: verify, dedupe, acknowledge, and process, with copy-ready code.

Cloudflare WorkersWebhooksJavaScriptAutomation

By Troy Tessalone · · 10 minutes

Cloudflare

A practical field guide from Automation Ace.

The short answer

To receive webhooks with Cloudflare Workers, deploy a Worker whose fetch handler accepts POST requests, give its URL to the sending app, then in your code verify the signature, parse the JSON, skip duplicates, respond quickly with 200, and do the real work in the background with ctx.waitUntil() or a queue. Workers respond in milliseconds, scale automatically, and handle high webhook volumes inexpensively.

New to webhooks? Read what is a webhook and API vs webhook. New to Workers? Start with Cloudflare Workers 101.

Why Workers are good webhook receivers

  • Fast responses: many senders time out or retry if you do not answer within seconds. See why webhooks fire twice.
  • Always on, globally: no server to keep running.
  • Full control: verify signatures, handle raw bodies, and return custom status codes, which is harder in no-code tools. Compare Webhooks by Zapier.
  • Cost: requests are counted against a generous free daily allowance. See free-plan automations.

A complete webhook receiver

// src/index.js — receive, verify, dedupe, and process a webhook
export default {
  async fetch(request, env, ctx) {
    if (request.method !== 'POST') return new Response('Method not allowed', { status: 405 });

    const raw = await request.text();                       // raw body for signature checks
    const signature = request.headers.get('X-Signature') || '';
    if (!(await verifyHmac(raw, signature, env.WEBHOOK_SECRET))) {
      return new Response('Invalid signature', { status: 401 });
    }

    let event;
    try { event = JSON.parse(raw); } catch { return new Response('Bad JSON', { status: 400 }); }

    // Deduplicate: skip events we have already seen (KV binding named SEEN)
    const id = event.id;
    if (id && await env.SEEN.get(id)) return new Response('Duplicate ignored', { status: 200 });
    if (id) await env.SEEN.put(id, '1', { expirationTtl: 60 * 60 * 24 * 7 });

    // Respond fast; finish the work in the background
    ctx.waitUntil(processEvent(event, env));
    return new Response('OK', { status: 200 });
  },
};

async function processEvent(event, env) {
  if (event.type === 'order.paid') {
    await fetch(env.SLACK_WEBHOOK_URL, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ text: `New paid order ${event.data.id} for ${event.data.total}` }),
    });
  }
}

async function verifyHmac(body, signatureHex, secret) {
  const key = await crypto.subtle.importKey(
    'raw', new TextEncoder().encode(secret),
    { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']
  );
  const mac = await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(body));
  const expected = [...new Uint8Array(mac)].map(b => b.toString(16).padStart(2, '0')).join('');
  return expected.length === signatureHex.length &&
    crypto.subtle.timingSafeEqual(new TextEncoder().encode(expected), new TextEncoder().encode(signatureHex));
}

Set up the bindings and secrets:

# create a KV namespace for dedupe keys and add it to your Wrangler config as SEEN
npx wrangler kv namespace create SEEN
npx wrangler secret put WEBHOOK_SECRET
npx wrangler secret put SLACK_WEBHOOK_URL

Signature header names and formats vary by provider (for example, some use base64 or include a timestamp). Always follow the sender's documentation for exact verification rules.

The five rules of reliable webhook handling

  1. Verify authenticity. Check an HMAC signature or shared secret before trusting the payload. Anyone who finds your URL can post to it.
  2. Validate the payload. Reject malformed JSON and missing fields with 400.
  3. Deduplicate. Senders retry, so store event IDs and ignore repeats. See idempotency and webhook deduplication.
  4. Acknowledge fast. Return 200 quickly and move slow work to ctx.waitUntil(), a Queue, or a Workflow.
  5. Log and alert. Use Workers logs and send failures somewhere visible. See webhook debugging.

When processing gets heavier

  • Queues: push each event onto a Cloudflare Queue and process it in a consumer Worker with automatic retries.
  • Workflows: for multistep processing with waits and per-step retries, start a Cloudflare Workflow from the webhook.
  • Forward to Zapier: verify and clean events in a Worker, then POST them to a Zapier Catch Hook when non-developers own the downstream steps. See chaining webhooks.

Testing your webhook receiver

  • Run npx wrangler dev and send test requests with curl or an API client.
  • Use the sender's “send test event” feature after deploying.
  • Check npx wrangler tail for live logs.
  • Test bad signatures, bad JSON, and duplicates on purpose.

Connecting the webhook to another app's API? See connecting two apps with Workers. For professional help, see webhook automation consulting and webhook automation.

Frequently asked questions

How do I receive a webhook with Cloudflare Workers?

Deploy a Worker with a fetch handler that accepts POST requests, give its URL to the sending app, verify the signature, parse the JSON, deduplicate, return 200 quickly, and process the event.

How do I verify a webhook signature in a Cloudflare Worker?

Read the raw request body with request.text(), compute an HMAC using crypto.subtle and your webhook secret, and compare it with the signature header using a timing-safe comparison. Follow the sender's documented format.

How do I prevent duplicate webhook processing?

Store each event ID in Workers KV or D1 when you process it, and skip any event whose ID already exists. Senders commonly retry, so duplicates are normal.

Why should a webhook handler respond quickly?

Many senders time out and retry if they do not get a fast 2xx response. Acknowledge immediately and do slow work with ctx.waitUntil(), a Queue, or a Workflow.

Can I forward webhooks from Cloudflare Workers to Zapier?

Yes. Verify and clean the event in a Worker, then POST it to a Webhooks by Zapier Catch Hook URL so a Zap handles the rest.

Cloudflare WorkersWebhooksJavaScriptAutomation

Disclaimer: Zapier features, plan availability, and settings can change. Confirm current details in Zapier's help documentation and Cloudflare's developer documentation and pricing pages. Limits and plan features change over time. Code samples are simplified starting points; add your own validation, error handling, and security review before production use before relying on a specific setting. This article may include links to apps, products, or services; some links may be affiliate links, which means Automation Ace may earn a commission at no extra cost to you.

Build Better Systems

Ready to automate with confidence?

Share your tools, process, and goals. Automation Ace can design the workflow, integration, file transfer, or integration that fits your business.

Start a Project →