Rotating a webhook secret without breaking subscribers

A secret that can only be replaced atomically cannot be rotated without coordinating a deploy on somebody else’s system.

// sign with the new secret, and also with the old one
$headers = [
    'X-Signature'    => $this->sign($body, $secrets->current()),
    'X-Signature-Old'=> $this->sign($body, $secrets->previous()),
];

// or, better, a single header with several values:
// X-Signature: t=1663...,v1=abc...,v1=def...
// the subscriber accepts if ANY matches.

The multi-value header is the arrangement every large provider converged on, because it lets the subscriber verify against either secret without knowing which is current. Publishing an overlap window — both valid for thirty days — turns a rotation from a coordinated event into a self-service one.