Hành vi gửi
Lấy từ tài liệu của chính hãng, kiểm tra lần cuối 2026-08-06. Hãng không nêu con số nào thì bảng này cũng không ghi.
| Thời hạn phản hồi | hãng không công bố |
|---|---|
| Thử lại | 8 lần gửi: ngay lập tức, 5 giây, 5 phút, 30 phút, 2 giờ, 5 giờ, 10 giờ, 10 giờ. Sau đó là operational webhook message.attempt.exhausted |
| Ngừng gửi | Endpoint bị vô hiệu hóa khi mọi lần gửi đều thất bại trong 5 ngày |
| Nguồn | https://docs.svix.com/retries |
Chữ ký
Kiểm chứng bằng một bộ xác minh chạy thật, có bộ test chứng minh, chứ không đọc từ tài liệu. Kiểm chứng lần cuối 2026-08-20.
| Thuật toán | HMAC-SHA256 |
|---|---|
| Payload được ký | {id}.{timestamp}.{body} |
| Encoding | base64 |
| Header | svix-signature = v1,base64 |
| Dung sai | 5 phút |
Xác minh
Dùng raw body của request, đúng từng byte, trước mọi bước parse JSON. Mọi cơ chế trên trang này đều hỏng ngay khi framework serialize lại payload.
const crypto = require("node:crypto");
// base64(HMAC-SHA256(base64-decoded secret, "{id}.{timestamp}.{body}"))
const id = req.headers["svix-id"];
const ts = req.headers["svix-timestamp"];
const key = Buffer.from(whsecSecret.replace("whsec_", ""), "base64");
const expected = crypto
.createHmac("sha256", key)
.update(`${id}.${ts}.${rawBody}`)
.digest("base64");
// svix-signature holds space-separated candidates like "v1,<base64>"
const valid = req.headers["svix-signature"]
.split(" ")
.some((part) => part.split(",")[1] === expected);Hoặc dùng SDK chính thức: new Webhook(secret).verify(rawBody, headers). SDK này cũng tự áp dụng dung sai dấu thời gian 5 phút cho bạn. Tiền tố whsec_ được bỏ đi và phần còn lại được base64-decode trước khi dùng làm khóa HMAC; bỏ qua bước này là lỗi kinh điển.
Những chỗ dễ vấp
Webhook của Clerk và Resend bên dưới đều là Svix, nên một bộ xác minh dùng được cho cả ba: header và định dạng giống hệt, chỉ khác dashboard nơi lưu secret.
Lịch thử lại gồm 8 lần gửi (ngay lập tức, 5 giây, 5 phút, 30 phút, 2 giờ, 5 giờ, 10 giờ, 10 giờ), sau đó một operational webhook message.attempt.exhausted báo cho bạn biết Svix đã bỏ cuộc. Năm ngày mọi lần gửi đều thất bại thì endpoint bị vô hiệu hóa.
Đọc thêm
AnyHook đứng trước các endpoint nhận webhook từ Svix: trả lời sau khoảng 1 giây, tự thử lại theo lịch riêng khi server của bạn ngừng hoạt động, và giữ mọi sự kiện để bạn phát lại được. Đổi một URL, giữ nguyên code; để nhận được các lần thử lại và phát lại, bạn xác minh AnyHook-Signature, vì chữ ký riêng của Svix hết hạn sau 5 phút.