Shopify 对失败的 Webhook 一共投递 9 次:第一次尝试加 8 次重试,最后一次在事件发生 4 小时后。然后就停了。在我们的测试里,哪怕服务器恢复了,这些订单也再没有任何投递。订单在 Shopify 里还在,但你的系统通过 Webhook 永远收不到它们。
Shopify 的文档只给了总数,4 小时内重试 8 次,没说每次尝试发生在什么时候。所以我们自己测了一遍。我们在一个 Shopify 开发商店下了四个测试订单,让接收端对所有请求返回 503,持续不到六个小时,并记录了每一个请求。
TL;DR
- 各次尝试落在约 2 秒、3 秒、1 分钟、3 分钟、8 分钟、30 分钟、1 小时、2 小时和 4 小时。四个订单的时间点相差都在 1.4 秒以内
- 第九次尝试之后,什么都没有。接收端不到两小时后恢复,四个订单没有一个通过 Webhook 送达
- 许多指南至今还在写的“48 小时内重试 19 次”已经过时
- 在 Shopify 后台添加的 Webhook,连续投递失败 36 次后也没有被删除。后台页面没有显示任何失败,店主也没有收到邮件
- 要扛过超过四小时的宕机,需要两样东西:在你的服务器宕机时保存事件并应答 Shopify 的一层,以及一个对 Admin API 的对账任务。对于在后台添加的 Webhook 或小型应用,改动最小的办法是用 AnyHook 这样的转发服务:你只需改一个 URL
实测的重试时间表
| 尝试 | 下单后的时间 | 与上一次尝试的间隔 |
|---|---|---|
| 1 | 2 秒 | |
| 2 | 3 秒 | 1 秒 |
| 3 | 1 分钟 | 1 分钟 |
| 4 | 3 分钟 | 2 分钟 |
| 5 | 8 分钟 | 5 分钟 |
| 6 | 30 分钟 | 22 分钟 |
| 7 | 1 小时 | 30 分钟 |
| 8 | 2 小时 | 1 小时 |
| 9 | 4 小时 | 2 小时 |
| 之后 | 没有 |
时间从 X-Shopify-Triggered-At 算起,这是 Shopify 打在 Webhook 上的时间戳。
九次尝试里有五次集中在前八分钟。它们能应付一次短暂抖动,却应付不了一次出错的部署:等有人看到告警时,只剩四次了。
这个时间表看起来是固定的。每一次尝试,四个订单的到达时间都相差在 1.4 秒以内,看不出抖动。同时失败的一批订单,会作为同一批重试:限时抢购的流量高峰,会在一分钟后、三分钟后、八分钟后原样再来一遍。
同一个事件的重试带着相同的 ID。在我们对比的那个订单上,X-Shopify-Webhook-Id 在九次尝试中完全一致,所以它是去重的可靠键。
关键的界线是四小时。端点恢复时,过去四小时内的每个订单都还在路上,会在各自的下一次计划尝试时送达。比这更早的订单,永远不会送达。
“48 小时内重试 19 次”已经过时
你仍会看到这个数字,包括 Webhook 厂商今年发布的指南。它来自 Shopify 以前的策略。Shopify 在2024年9月10日改了这套机制,变成 4 小时内重试 8 次,现行文档的说法也一致。我们的日志与此相符:最后一次尝试发生在第四个小时,第二天也没有收到更多投递。
如果你的运维手册或恢复预案是按两天来算的,就差了十二倍。
订单后来怎么样了
在直连的 Webhook 上,全部 36 次投递(四个订单,每个九次尝试)都收到了 503。接收端在这些尝试的最后一次之后 1 小时 37 分钟重新开始返回 200,Shopify 没有再试。四个订单没有一个送到。
这次宕机没有任何特别之处。周五晚上一次出错的部署,在午夜前修好,看起来也是这样。丢哪些订单,取决于你宕机了多久。在一次六小时的宕机里,前两小时的订单在 Webhook 这条路径上就没了,后四小时的订单会在各自的下一次计划尝试时送达,最晚在你恢复后两小时。这说的是一个在宕机中幸存下来的 Webhook。通过 Admin API 创建的订阅,可能在这期间被删除,之后在你重新创建之前,不会再发送任何新事件。
把这个放到黑色星期五大促中:服务器压力最大,每小时的订单也最多,最前面那几个小时只能靠 Admin API 回填,没有别的办法能找回来。具体步骤见 Shopify 删除了你的 Webhook 订阅。
Webhook 还在,但没有人收到通知
Shopify 的文档说,只有通过 Admin API 配置的订阅,才会在连续失败 8 次后被删除,而警告邮件会发到应用的紧急开发者邮箱。文档没有提到在 Shopify 后台的 Settings → Notifications → Webhooks 里手动添加的 Webhook。它们背后没有应用,而且往往正是把店铺连到 ERP 或仓库系统的那条线。
所以我们去看了。连续 36 次投递失败之后,第二天:
- 这个 Webhook 还在列表里,没有任何变化
- 后台页面没有显示投递记录,也看不出失败。每一行只提供编辑、发送测试和删除
- 店主的邮箱没有收到任何警告邮件
一个已经整整一天每次投递都失败的 Webhook,看起来和一个健康的没有区别。唯一的症状是下游缺订单。通过 Admin API 创建的订阅,失败的方式不一样:Shopify 会把它删除。
宕机超过四小时,怎么保住订单
你需要一个在服务器宕机时保存事件并应答 Shopify 的一层,再加一个对账任务,兜住仍然漏掉的部分。Shopify 自己的文档也建议你建这个任务。下面的选项里有一个是我们做的,请相应地看待我们的意见。
| 方案 | 适用于后台添加的 Webhook | 适合谁 | 代价 |
|---|---|---|---|
| 基于 Admin API 的对账任务 | 是 | 任何漏一个订单就要花钱的店铺 | 事后才运行,按你的计划来。要自己建、自己运行 |
| Amazon EventBridge 或 Google Pub/Sub | 否,后台表单只接受 URL | 已经在该云上的应用 | 只支持应用订阅。消费端、保留期和告警都要自己建 |
| 自建队列,前面放一个小端点 | 是 | 想把一切都放在内部的团队 | 这个端点同样只有五秒应答时间,而且在你系统的其余部分挂掉时它必须还活着 |
| Hookdeck | 是 | 需要数据转换、团队协作流程和大流量的平台团队 | 对于“重试加重放”这点需求来说,产品太重 |
| AnyHook | 是 | 小团队和后台添加的 Webhook:只改一个 URL,无需写代码,免费开始 | 投递是至少一次,所以你仍要去重。Free 套餐在第 35 分钟停止重试,每天超过 1,000 个事件也会拒收 |
怎么选:
- 你是手动添加的 Webhook,或者运行一个小型应用,想让订单扛过一个糟糕的夜晚: 在你的端点前面放一个转发服务。AnyHook 就是为这种情况做的。只改一个 URL,你的处理程序保持原样
- 你是处理大流量的平台团队: Hookdeck 是更完整的产品。两者的对比在这里
- 你的应用已经跑在 AWS 或 Google Cloud 上: EventBridge 或 Pub/Sub 正好适合你现有的环境
- 无论哪种情况: 都要保留一个订单对账任务。转发服务会把那条界线从四小时挪到它自己的重试窗口和保留期,但这条界线依然存在
同样的测试,经过 AnyHook
AnyHook 是一个 Webhook 转发服务:你把 Webhook 指向它,它保存事件、应答发送方,再转发到你的服务器,你的服务器失败时会重试。在这次测试里,每个订单还发给了第二个 Webhook,它指向 AnyHook,由 AnyHook 转发到同一个接收端:
| 尝试 | 经过 AnyHook |
|---|---|
| 1 | 2 秒 |
| 2 | 5 分钟 |
| 3 | 35 分钟 |
| 4 | 3 小时 35 分钟 |
| 5 | 21 小时 35 分钟,已送达 |
AnyHook 约 1 秒就给 Shopify 返回 200,所以 Shopify 没有什么可重试的,这个时间表是 AnyHook 自己对同一个失败的接收端的重试。直连 Webhook 上 Shopify 的尝试早已结束很久之后,第五次尝试发现接收端已经恢复,把仍在等待的三个订单送达了。第一个订单的那一份已经在 35 分钟时送达,原因是我们这边的一次失误,见测试方法。
这一跳给第一次尝试增加了 0.5 到 0.9 秒。Shopify 的签名经受住了转发:接收端对转发过来的请求验证原始的 X-Shopify-Hmac-SHA256,每一次都通过,所以你现有的验证代码照常可用。AnyHook 在第一次投递失败时发了邮件,连续失败五次时又发了一封。Shopify 什么都没发。
这次测试跑在我们自己的 Enterprise 账号上。各个套餐的重试时间点相同。不同的是你能得到几次尝试,以及能重放多久:
| 套餐 | 价格 | 尝试次数 | 最后一次自动尝试 | 重放窗口 |
|---|---|---|---|---|
| Free | $0,无需绑定信用卡 | 3 | 35 分钟 | 3 天 |
| Pro | $20/月 | 5 | 21 小时 35 分钟 | 30 天 |
| Scale | $90/月 | 10 | 5 天 21 小时 | 90 天 |
在 Free 套餐上,这次测试的结果会不同:重试在 35 分钟停止,那时接收端还没恢复。订单仍然在日志里,你需要在三天内手动重放它们。Free 套餐每天超过 1,000 个事件也会拒收,所以不适合用来扛大促。Pro 套餐没有每日上限,第五次尝试的表现和这次一样。
转发服务是你订单路径上多出来的一方。AnyHook 在应答之前,会先把每个事件静态加密并保存。如果它无法保存某个事件,就返回 503 而不是 200,由 Shopify 自己的重试接手。
接入步骤
-
在 AnyHook 中创建一个 App,把你现在的 Webhook URL 设为它的目标地址。想先不注册账号看一看,可以用下面的命令,它会返回一个可用的
inbound_url、一个 API 密钥和一个claim_url。除非你打开认领链接并登录,否则这个端点和它的事件会在 7 天后删除:curl -X POST https://anyhook.net/api/v1/quickstart -
在 Shopify 后台打开 Settings → Notifications → Webhooks → Create webhook。选择事件,把格式设为 JSON,粘贴 AnyHook 的入站 URL。
-
把该页面上显示的签名密钥,复制到 App 的来源签名密钥中,然后在这个 Webhook 的菜单里发送一次测试。它应该出现在 App 的日志里,而伪造的请求会收到 401。你的应用创建的订阅,则改用应用的 client secret 签名。
AI 编程智能体可以用 npx -y anyhook-mcp 完成第一步。
FAQ
Shopify Webhook 失败后会重试几次?
首次尝试之后重试 8 次,也就是一共 9 次投递,持续 4 小时。我们测得各次尝试发生在事件之后约 2 秒、3 秒、1 分钟、3 分钟、8 分钟、30 分钟、1 小时、2 小时和 4 小时。
Shopify 的 Webhook 现在还会重试 48 小时吗?
不会。旧指南里的“48 小时内重试 19 次”,早于 Shopify 在2024年9月做的那次改动。在我们2026年11月的测试里,最后一次尝试发生在第四个小时。
服务器恢复后,Shopify 会补发 Webhook 吗?
只有在事件的各次尝试还没走完时才会。在你恢复前不到四小时发生的事件,会在它的下一次计划尝试时送达。更早的事件不会再发,你需要从 Admin API 把它取回来。
Shopify 怎样算 Webhook 投递失败?
根据 Shopify 的文档,200 范围之外的任何响应都算,包括 3xx 重定向,还有五秒内没有应答的情况,其中连接允许占一秒。我们的测试只用了 503 响应。
Webhook 一直失败,Shopify 会把它删除吗?
根据 Shopify 的文档,通过 Admin API 创建的订阅,连续失败 8 次后会被删除。在我们的测试里,一个在 Shopify 后台添加的 Webhook,连续失败 36 次之后的第二天,仍然在列表里。
Webhook 失败时,Shopify 会发告警吗?
对于应用订阅,警告邮件会发到应用的紧急开发者邮箱。对于我们在后台添加的那个 Webhook,店主没有收到任何邮件,后台页面也没有显示失败。
Shopify 不再重试的订单,怎么找回来?
用 Admin API 查询宕机时间段内的订单,把结果交给你平时的处理程序,并开启去重。步骤见 Shopify 删除了你的 Webhook 订阅。
服务器宕机期间,怎样保住 Shopify 的 Webhook?
在你的端点前面放一个 Webhook 转发服务或队列:它应答 Shopify,保存事件,并用更长的时间表重试你的服务器。对于在后台添加的 Webhook 或小型应用,AnyHook 改动最小,只改一个 URL,有免费套餐,付费套餐的重试时间更长,也没有每日上限。Hookdeck 适合需要数据转换和团队协作流程的平台团队。跑在 AWS 或 Google Cloud 上的应用,可以把订阅改发到 EventBridge 或 Pub/Sub。无论哪种情况,都要保留一个对 Admin API 的对账任务。
测试方法
我们在2026年11月2日做了这次测试,用的是一个 Shopify 开发商店,API 版本 2026-10,主题 orders/create。两个 Webhook 都是在后台添加的。接收端是一个 Cloudflare Worker,记录每一个请求,在 17:43 UTC 之前返回 503,之后返回 200。四个订单在 11:46 到 12:06 UTC 之间下的。
直连 Webhook 上的确切到达时间,单位是距 X-Shopify-Triggered-At 的秒数:
| 尝试 | 四个订单的范围 |
|---|---|
| 1 | 1.4 到 1.9 |
| 2 | 2.3 到 2.9 |
| 3 | 63.3 到 64.2 |
| 4 | 183.6 到 185.0 |
| 5 | 484.4 到 485.8 |
| 6 | 1805.4 到 1806.5 |
| 7 | 3606.2 到 3607.4 |
| 8 | 7207.0 到 7208.2 |
| 9 | 14407.7 到 14409.0 |
有两处不完美。我们这边的一次失误,让接收端在 12:20 UTC 前后恢复了几分钟。直连的尝试没有一次落在这个时间窗口里,但第一个订单经 AnyHook 的那一份落在了里面,所以这个订单没有计入 AnyHook 的结果。另外,接收端是中途才开始检查签名的,所以两条路径上最早的几次尝试都没有经过检查。
我们没有测试超时或连接被拒绝,只测了快速返回的 503。我们没有测试通过 Admin API 创建的订阅,所以上面的删除规则来自 Shopify 的文档。我们没有测试正式店铺,也没有测试大流量。四个订单是个很小的样本,不过这样规整的时间表不太可能是噪声。Shopify 随时可能改动其中任何一项,所以请核对上面的日期。