Webhook链路里往往同时存在两种代理:发送方服务通过正向代理访问互联网,接收端位于CDN、WAF、负载均衡或反向代理之后。前者影响能否建立连接,后者可能影响Host、路径、请求体大小、真实来源地址和超时。排查时必须标明代理位于哪一侧。
先画清请求路径
| 位置 | 职责 | 常见问题 |
|---|---|---|
| 发送方 | 生成事件、签名并发送 | DNS、出口代理、407和超时 |
| 正向代理 | 代表发送方连接接收端 | CONNECT、TLS与出口策略 |
| 反向代理/WAF | 接收公网请求并转发 | 请求体限制、路径改写与超时 |
| Webhook应用 | 验签、去重、入队和处理 | 原始请求体、幂等与业务错误 |
签名为什么必须使用原始请求体
很多Webhook以原始字节、时间戳和共享秘密计算HMAC。框架若先解析JSON再重新序列化,字段顺序、空格或字符编码可能变化,导致签名不一致。应用应在解析前保留原始请求体,并按发送方文档构造待签名内容。
代理会改动哪些签名相关信息
反向代理可能解压请求、规范化路径、修改Host或移除自定义头。签名方案若覆盖这些内容,代理配置必须保证一致。不要把客户端提供的X-Forwarded-For直接当可信来源,只有受控反向代理写入的头才可用于审计。
为什么业务已经处理还会重推
发送方通常在连接超时、读取超时或收到非2xx时重试。如果接收端已经入库,却在返回响应前断线,发送方无法知道处理结果,会再次推送。因此Webhook消费必须按事件ID或稳定业务键实现幂等。
网络重试语义可参考超时、重试与幂等恢复。
接收端应该先处理还是先响应
对于耗时业务,常见做法是完成验签、时间戳检查和去重后,将事件可靠写入队列,再快速返回成功。不能在未持久化事件前就返回200,否则进程随后崩溃会丢失事件;也不应在HTTP请求中执行过长任务导致发送方重试。
防重放需要哪些条件
- 验证签名并使用常量时间比较;
- 检查签名时间戳在允许窗口内;
- 保存事件ID或Nonce的处理状态;
- 过期事件按策略拒绝或人工复核;
- 秘密定期轮换,并支持短暂双密钥过渡。
IP白名单为何只能作为辅助
Webhook服务商可能调整出口IP,CDN或云网络也可能变化。只依赖IP会造成误封或错误信任。更稳妥的是HTTPS、签名、防重放、最小路径暴露与限流组合;IP白名单可作为额外一层,并建立更新流程。
状态码怎样设计
验签失败应返回明确的4xx并记录脱敏原因;临时内部故障可返回5xx让发送方按规则重试;永久业务拒绝不应无限触发重试。具体状态与发送方重试策略需共同设计,避免所有错误都返回200后静默丢弃。
大请求和超时看哪一层
附件通知或批量事件可能超过WAF、反向代理或应用的请求体上限。应记录各层限制和读取超时。代理504不一定代表应用未处理,应通过事件ID查询接收端状态后再决定补发。
日志怎样既可追踪又不泄密
记录事件ID、发送方、接收时间、签名验证结果、HTTP状态和处理状态即可。不要记录签名秘密、完整Authorization、个人信息或整个请求体。需要留存原始事件时,应加密、限制访问并设置保留期。
验证流程
- 使用测试密钥发送一条带唯一ID的事件;
- 确认正向和反向代理各层状态;
- 验证原始请求体签名;
- 模拟响应丢失并观察重复事件;
- 确认幂等只产生一次业务结果;
- 测试旧时间戳、错误签名和超大请求;
- 检查日志与队列没有泄露秘密。
结论
Webhook代理故障不只是“能不能收到”。验签、快速持久化、重试和幂等必须一起设计,才能在代理超时或响应丢失时保持事件可信且不重复执行业务。






