前端开启Automatic Persisted Queries后,大部分请求正常,偶尔却返回400或“Must provide query string”。直接重启PHP通常没用,先把APQ的两阶段请求拆开看:第一次只带hash,未命中后第二次才携带完整query。

排查记录

我保留了一个失败样本,并用同样输入在无缓存或单节点条件下再跑一遍。这样可以把前端假象、缓存和真正的数据问题分开。

我先看了这几处

  • 用浏览器Network确认失败的是只带extensions的请求,还是回退后的完整请求。
  • 检查CDN缓存键是否包含POST body;有些规则把所有/graphql POST当成同一个对象。
curl -i https://shop.example.com/graphql -H "Content-Type: application/json" --data '{"extensions":{"persistedQuery":{"version":1,"sha256Hash":"bad"}}}'
varnishlog -g request -q 'ReqURL eq "/graphql"'
grep -R "graphql" /etc/nginx /etc/varnish

日志取样要带上请求时间、Store View、实体ID和部署版本。只截一行异常往往不够,至少保留错误前后几十行,才能判断是入口参数还是后续状态被改写。

处理办法

Magento原生并不负责保存所有客户端APQ映射时,应由前端GraphQL客户端或网关负责回退。CDN层对/graphql POST默认pass,或明确把body hash、Store、Authorization是否存在纳入缓存策略;同时保留PersistedQueryNotFound响应,不能把它改写成通用400。发布后清理旧CDN对象。

修改范围

  • 先在单个SKU、单个订单或单个Store验证。
  • 保留可回退的旧配置,不覆盖原始证据。
  • 需要批量处理时分批提交并输出成功/失败清单。

回归时别只刷新页面

  • 首个hash请求得到可识别的未命中响应
  • 第二个完整query请求为200
  • 同一hash再次请求不再丢body
  • 不同Store View不会互串

验收不只看后台提示“成功”。我还会从前台/API再走一次真实链路,并在下一轮cron、索引或缓存命中后重复检查,确认不是短暂正常。

补记

不要把Authorization原值放进可观察的缓存键或日志;只区分“是否登录”仍不够时,应直接绕过共享缓存。