48-hour launch: 40% off with code LAUNCH40, Guide $5.40 / Kit $11.40

Stripe: “No stripe-signature header value was provided” — how to fix

Stripe's library threw before checking anything else: the header argument you passed to constructEvent / construct_event was undefined, None or an empty string. Stripe always sends a Stripe-Signature header on webhook deliveries, so either your code read it wrong or something between Stripe and your code didn't pass it on. (Older stripe-node versions, up to v11.0, report the same problem as “Unable to extract timestamp and signatures from header”.)

Causes, most common first

  1. Wrong header-name casing on a plain object. Node lower-cases incoming header names, so in Express req.headers['Stripe-Signature'] is undefined. Use req.headers['stripe-signature'] or req.get('Stripe-Signature'). In Django, request.META['Stripe-Signature'] doesn't exist; the key is HTTP_STRIPE_SIGNATURE.
  2. AWS Lambda behind API Gateway. With an HTTP API, header names in the Lambda event are lower-cased, so event.headers['Stripe-Signature'] is missing. With a REST API non-proxy integration, headers only reach the function if your mapping template passes them; Stripe's docs give a template that passes rawBody and headers.
  3. A proxy, gateway or platform rewrite didn't forward it. Some setups only forward an allowlist of headers to the backend. Log the header names your handler receives (for example Object.keys(req.headers)) on one delivery and check that stripe-signature is there.
  4. The request didn't come from Stripe. A curl, Postman or browser test, an uptime monitor, or a bot hitting your URL won't have the header. Test with stripe listen --forward-to … and stripe trigger …, or Resend a real event from the Dashboard.

Express

app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const sig = req.headers['stripe-signature'];   // lower-case key (or req.get('Stripe-Signature'))
  if (!sig) return res.status(400).send('Missing Stripe-Signature header');
  let event;
  try {
    event = stripe.webhooks.constructEvent(req.body, sig, process.env.STRIPE_WEBHOOK_SECRET);
  } catch (err) {
    return res.status(400).send(`Webhook Error: ${err.message}`);
  }
  res.sendStatus(200);
});

Next.js (App Router)

export async function POST(req) {
  const body = await req.text();
  const sig = req.headers.get('stripe-signature');   // Headers.get() is case-insensitive
  if (!sig) return new Response('Missing Stripe-Signature header', { status: 400 });
  // stripe.webhooks.constructEvent(body, sig, process.env.STRIPE_WEBHOOK_SECRET) ...
}

AWS Lambda (Node)

// Find the header whatever the casing; Stripe sends "Stripe-Signature"
const headers = Object.fromEntries(Object.entries(event.headers || {}).map(([k, v]) => [k.toLowerCase(), v]));
const sig = headers['stripe-signature'];
const body = event.isBase64Encoded ? Buffer.from(event.body, 'base64') : event.body;
const stripeEvent = stripe.webhooks.constructEvent(body, sig, process.env.STRIPE_WEBHOOK_SECRET);

FastAPI

@app.post("/webhooks/stripe")
async def stripe_webhook(request: Request):
    payload = await request.body()
    sig_header = request.headers.get("stripe-signature")   # case-insensitive
    if not sig_header:
        raise HTTPException(status_code=400, detail="Missing Stripe-Signature header")
    event = stripe.Webhook.construct_event(payload, sig_header, endpoint_secret)

A FastAPI parameter stripe_signature: str | None = Header(None) also works, because FastAPI converts underscores to hyphens.

Django

sig_header = request.headers.get("Stripe-Signature")    # or request.META.get("HTTP_STRIPE_SIGNATURE")
event = stripe.Webhook.construct_event(request.body, sig_header, settings.STRIPE_WEBHOOK_SECRET)

Once the header arrives, signature mismatches are usually a raw-body problem: see “No signatures found matching the expected signature for payload” and Express raw body. Python handlers in full: stripe.SignatureVerificationError in Python.

Header found, still failing? The guide covers every common Stripe webhook failure with copy-paste fixes. The kit is a self-hosted Cloudflare Worker relay that verifies Stripe signatures, then retries, replays or dead-letters each event instead of dropping it.

Fix Your Stripe Webhooks guide · Self-Hosted Webhook Relay Kit. Code LAUNCH40 = 40% off until Sun Oct 11, 6:40 AM MT.

Free download: Webhook debugging cheat sheet.

Still failing? Paste your payload, header and secret into the free Stripe signature checker. It runs in your browser and tells you which cause it is.

All five common causes with copy-paste fixes: Fix Your Stripe Webhooks guide ($9). Retries, replay and a dead-letter list in front of your endpoint: Self-Hosted Webhook Relay Kit ($19).