Webhook Guides

How to Secure a Webhook Endpoint

A webhook endpoint is public by design, so security starts before the request reaches application business logic. A secure endpoint verifies who sent the request, preserves the raw body for signature checks, limits accepted methods, avoids secret leakage, and handles retries without duplicate side effects.

FastHook helps by putting a source authentication boundary in front of your receivers. Sources can verify provider signatures or generic auth, connections can filter and control delivery, and destinations can use outbound signatures or custom headers so receivers can trust traffic from FastHook.

Security checklist

  1. Use HTTPS and a dedicated webhook URL per provider and environment.
  2. Verify provider signatures, HMAC, API keys, Basic Auth, or provider-specific tokens at ingress.
  3. Verify signatures against the exact raw request body, not parsed JSON.
  4. Restrict allowed HTTP methods to the methods the provider actually uses.
  5. Use timestamp tolerance when the provider signs timestamps.
  6. Store event ids or delivery ids before applying side effects.
  7. Use destination auth so receivers can verify FastHook deliveries.
  8. Audit retries and replay because redelivery can create duplicate effects.

Authentication options

MethodBest forWhat to watch
Provider signatureStripe, GitHub, Shopify, Slack, and provider presets.Must use the exact raw body and correct secret.
Generic HMACInternal services or providers with configurable HMAC headers.Define header, prefix, and timestamp behavior clearly.
API key headerSimple internal senders.Rotate keys and avoid sending them in query strings.
Basic AuthSimple service-to-service senders.Use HTTPS and rotate credentials.
Destination signatureReceiver verifying FastHook outbound delivery.Separate from provider-to-source signatures.

Threat model

A webhook endpoint should assume that unknown clients can find or guess the URL. The security goal is not to hide the endpoint forever; it is to make sure unauthenticated, replayed, malformed, or duplicate requests cannot create trusted business side effects.

ThreatDefenseFastHook layer
Fake senderProvider signature, HMAC, API key, or Basic Auth.Source authentication.
Body tamperingVerify the exact raw request body.Source verification and receiver checks.
Replay attackSigned timestamp tolerance and idempotency keys.Source auth plus receiver idempotency.
Endpoint abuseAllowed methods, filters, and destination rate limits.Source and connection rules.
Downstream spoofingFastHook destination signatures or custom headers.Destination authentication.

Create a signed source

For custom HMAC senders, configure the signature header and secret at the source. For provider presets, use the matching provider source type when one exists.

HMAC source
curl -X POST "https://api.fasthook.io/v1/sources" \
  -H "Authorization: Bearer fhp_xxx" \
  -H "x-team-id: tm_3b5335b627084a838b" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Signed production source",
    "type": "WEBHOOK",
    "status": "enabled",
    "config": {
      "auth_type": "HMAC",
      "auth": {
        "secret": "provider_webhook_secret",
        "signature_header": "x-provider-signature",
        "prefix": "sha256="
      },
      "allowed_http_methods": ["POST"]
    }
  }'

Raw-body verification

Many webhook signature bugs come from verifying the wrong string. If a framework parses JSON first and then your code signs JSON.stringify(body), the bytes can differ from the original provider request. Whitespace, key order, encoding, and body transformations all matter.

Verify the raw body before parsing when the provider signs the raw body. After verification succeeds, parse the JSON and apply routing or business logic. If FastHook verifies the provider at the source and you also transform the payload before delivery, use FastHook destination signatures for the transformed FastHook-to-receiver hop.

Raw-body HMAC shape
import crypto from "node:crypto";

function verifyRawBody({ rawBody, signature, secret }) {
  const expected = "sha256=" + crypto
    .createHmac("sha256", secret)
    .update(rawBody)
    .digest("hex");

  const actualBytes = Buffer.from(signature || "");
  const expectedBytes = Buffer.from(expected);

  return actualBytes.length === expectedBytes.length &&
    crypto.timingSafeEqual(actualBytes, expectedBytes);
}

Protect the receiver too

Source verification proves the provider request was authentic at ingress. Your receiver may still need to prove that the downstream delivery came through FastHook, especially if the destination URL is public or payloads are transformed before delivery.

Destination auth
{
  "type": "HTTP",
  "config": {
    "url": "https://api.example.com/webhooks/fasthook",
    "http_method": "POST",
    "auth_type": "FASTHOOK_SIGNATURE",
    "auth": {}
  }
}

Secret rotation and environments

Every environment should have its own source URL and secret. Rotating a secret should be a planned operation: configure the new secret, send a signed test event, watch rejected requests, then remove the old value from the provider dashboard or sender configuration.

  • Use different secrets for local, staging, and production.
  • Do not paste webhook secrets into examples, issue trackers, or screenshots.
  • Rotate secrets after accidental exposure or when ownership changes.
  • Keep enough rejected-request history to prove whether a rotation caused failures.
  • Prefer provider-specific source types when FastHook supports the provider.

Rate limits and abuse controls

Authentication keeps fake senders out, but it does not protect a slow receiver from legitimate bursts. Use filters to drop irrelevant events early and destination rate limits to pace expensive side effects. This matters during provider retry storms, replay windows, and incident recovery.

  • Narrow allowed HTTP methods before production traffic starts.
  • Filter high-volume event families away from destinations that do not need them.
  • Pause unsafe connections while a receiver is being fixed.
  • Replay with a small window first, then expand only after attempts succeed.

Replay-safe security

Security is not only about blocking fake requests. Retries and replay can deliver the same authentic event more than once. A receiver should store an idempotency key before changing billing state, fulfillment, account access, notifications, or other side effects.

  • Use provider event id, delivery id, or a stable business object id.
  • Treat provider retries, FastHook retries, and operator replay as duplicate-capable.
  • Retry only after fixing auth, schema, timeout, or receiver health issues.
  • Throttle replay so recovery traffic does not become a second incident.

Common security mistakes

  • Trusting event type headers without verifying the sender.
  • Verifying signatures after a JSON parser has changed whitespace or key order.
  • Sharing one webhook secret across local, staging, and production.
  • Putting secrets in query strings or screenshots.
  • Returning 2xx before accepted work is durable.
  • Replaying a broad window before checking receiver idempotency.

Related guides