缓存无法写入主因是服务端未发出“允许缓存”信号;需检查response header中cache-control是否缺失或为no-store/no-cache,etag是否稳定,中间层是否覆盖头,及状态码是否符合200/301等可缓存规范。

缓存无法写入,往往不是代码没执行,而是服务端压根没发出“允许缓存”的信号。Response Header 是第一道诊断窗口,它直接暴露了缓存策略是否被正确声明、是否被中间层覆盖、是否与客户端预期冲突。
检查 Cache-Control 是否缺失或冲突
这是最常见也最容易忽略的问题。若响应头中完全缺失 Cache-Control,浏览器和代理(如 CDN、Nginx)默认视为不可缓存;若存在但值为 no-store 或 no-cache,则明确禁止缓存写入。
- 用浏览器 DevTools 的 Network 面板或 curl 查看完整响应头:
curl -I https://api.example.com/graphql - 重点关注是否存在
Cache-Control: public, max-age=3600这类可缓存声明 - 注意框架中间件(如 Laravel 的
CacheHeaders)是否被条件跳过,或被后续中间件覆盖
验证 ETag / Last-Modified 是否稳定生成
即使设置了 Cache-Control,若协商缓存标识(ETag 或 Last-Modified)每次响应都不同,客户端仍会反复回源,导致“缓存未写入”假象——实际是缓存被频繁淘汰。
- 对同一查询发起两次请求,比对响应头中的
ETag值是否一致 - 若使用动态内容(如含时间戳、随机数、用户 ID),需确保这些字段不参与哈希计算
- PHP 中推荐用规范化后的查询字符串 + 数据版本号生成 ETag:
md5($normalizedQuery . $schemaVersion)
排查中间层覆盖或剥离缓存头
Nginx、CDN、API 网关等常默认移除或重写缓存头,尤其当后端未显式设置 Vary 或 Content-Encoding 时。
- Nginx 默认不透传
Cache-Control,需显式配置:proxy_pass_request_headers on;并确认未启用expires指令覆盖 - CDN(如 Cloudflare、阿里云DCDN)可能将后端的
public强制转为private,需在控制台检查“缓存规则”是否启用“尊重源站头” - 网关(如 Spring Cloud Gateway)若配置了全局响应过滤器,可能无意中清空了缓存头
确认响应状态码是否支持缓存
HTTP 规范规定:只有特定状态码(如 200、203、204、206、300、301、410)才可被缓存。若接口返回 201(Created)、401(Unauthorized)或 5xx 错误,即使头里写了 max-age,标准缓存机制也不会写入。
- 检查日志或监控,确认实际返回的状态码是否符合缓存前提
- GraphQL 场景中,错误响应(
"errors"字段非空)常伴随 200 状态码,此时需额外判断响应体结构是否影响缓存逻辑 - 避免用 200 包裹业务错误(如
{"code":500,"msg":"xxx"}),这类响应易被缓存但语义错误











