Compute an HMAC SHA-256 over the version, the request timestamp and the raw body using your app's signing secret, then compare it to the X-Slack-Signature header. Reject any request whose timestamp is more than five minutes from your clock. Do this before parsing or acting on the payload.
Why the signature matters
Your Slack app exposes a public URL. Anyone can send it a POST that looks like a Slack event or a slash command. Without verification, a stranger can make your app post messages, call internal systems, or leak data. The signing secret is how Slack proves a request is genuine.
Slack's documentation describes the scheme. On each HTTP request Slack adds an X-Slack-Signature header, created by hashing the request body with SHA-256 combined with an HMAC signing secret. Because the signature depends on the timestamp, it also helps against replay. Signing secrets replace the older verification tokens.
The steps
- Read the raw body. Do this before any JSON or form parsing. In some frameworks you must call the raw read method first, because re-serializing changes the bytes.
- Read the timestamp header, X-Slack-Request-Timestamp.
- Check freshness. If the absolute difference between your clock and the timestamp is more than five minutes, ignore the request. The documentation's own example uses 300 seconds.
- Build the base string. Concatenate the version, the timestamp and the raw body with a colon between each part. The version in the documented example is v0.
- Compute the HMAC. Hash the base string with your signing secret and prefix the hex digest with the version.
- Compare in constant time to the header value. If they differ, return an error and stop.
Official SDKs can do this for you. Use them where possible, since a hand rolled comparison is an easy place to slip.
Common mistakes
| Mistake | Effect |
|---|---|
| Parsing JSON before reading the body | Signature never matches |
| Skipping the timestamp check | Captured requests can be replayed |
| Using a normal string equality check | Leaks timing information |
| Logging the signing secret while debugging | The secret ends up in logs |
| Keeping the old verification token as a fallback | Weaker path stays open |
Where to keep the signing secret
In your secrets store, not in code or a chat message. Rotate it if it may have been exposed. The first part of that is covered in how to rotate leaked API keys. A signing secret is a shared secret, so anyone who holds it can forge valid requests.
Respond fast too
Slack expects a 2xx within three seconds for events, so verify, acknowledge and queue. The reasoning is in why a webhook endpoint should answer in seconds.
Try it yourself
Click through it at your own pace, or open it full screen. Look at how endpoints without authentication checks get flagged.
Where Safeguard fits
Safeguard's API security scanning and dependency scanning can help you find unauthenticated routes and vulnerable libraries in an integration like this. A free scan is a way to start.
Quick check
Is the five minute window required?
It is what Slack's documented example uses to limit replays. Pick a window no wider than that.
Do I still need to verify if the URL is secret?
Yes. A URL can leak through logs, proxies or screenshots. The signature is the control.
Sources
- Verifying requests from Slack, Slack developer documentation
Last reviewed: 19 October 2026.