Webhook Endpoint Security for a VPS-Hosted Application
A webhook is an untrusted inbound request until verified. Design signature checks, durable processing, and retries together.
Webhooks let an external service notify your application about an event, but the public endpoint can receive requests from anyone. A plausible JSON body is not proof that the provider sent it. Build the endpoint around the provider's documented signature verification, a durable record of accepted work, and a retry strategy that cannot repeat the same business mutation accidentally.
Verify before interpreting the event
Use the provider's maintained verification library where available. Follow its requirements for the raw request body, signature header, secret selection, and timestamp tolerance. Parsing and reserializing JSON before checking a signature can change the signed bytes. Keep the signing secret in protected configuration and never echo it into a response or diagnostic log.
Limit request size and apply appropriate connection and processing timeouts. A provider source-IP allowance can be an additional control when officially supported, but it does not replace signature verification. Proxies and provider infrastructure change, so document any network allowlist dependency and how it is maintained.
Make duplicate delivery harmless
Providers can retry when a response is lost or the endpoint reports failure. Store a stable provider event identifier and make the intended mutation idempotent at the database level. A read-then-write duplicate check without concurrency protection can still allow two workers to apply the same change. Design the uniqueness boundary around the provider and event identity used by the integration.
For an inventory update, a useful test sends the same verified event twice and confirms that the resulting stock adjustment happens once. Then test concurrent handling through the application's normal test facilities. The business result matters more than whether both HTTP requests returned the same message.
Acknowledge durable work, not a hope
Keep the endpoint's synchronous work bounded. If processing moves to a queue, persist the accepted event or job before returning the response that tells the provider delivery succeeded. An in-memory handoff can lose the event if the process stops immediately after acknowledgment. Match response behavior to the provider's documented retry rules.
Do not assume events always arrive in the business order you expect. Use the provider's documented event semantics and retrieve authoritative object state when the integration requires it. A delayed event should not blindly overwrite a newer state merely because it was the last request received.
Keep diagnostics safe and replay testable
Log event identifiers, verification outcomes, processing status, and safe error categories. Avoid full payload logging when events contain personal or payment information. Provide an authorized retry mechanism that preserves history and repeats the same logical work safely. Test invalid signatures, altered bodies, duplicate delivery, and a worker failure before enabling the integration for real events.
During secret rotation, follow the provider's supported overlap procedure and record when the old secret stops being accepted. Keep test and production endpoints distinguishable so a valid event from a test environment cannot accidentally mutate production records through a shared configuration mistake.
Stripe's webhook documentation illustrates provider-specific verification and retries. For related automation hosting, see our n8n guide, and use LayerOne documentation for managing the VPS endpoint.