验证码验证失败的根本原因是mews/captcha默认将验证码存入session,而迁移后session驱动不一致或写入延迟导致check时读不到;应改用cache::put()存入redis并绑定ip/ua、设置ttl、验证前先has()再get()、通过后forget(),同时确保cache_driver配置正确且清空配置缓存。

验证码失效不是缓存目录路径错了,而是验证码没存进 Cache,或者存了但 key 被覆盖、过期逻辑没走对。
为什么迁移后验证码总提示“验证失败”
常见现象是:页面首次加载时能显示验证码图片,但输入正确后仍报错;刷新几次后偶尔又正常。这不是前端或视图问题,根本原因是 Captcha 默认依赖 session 存验证码,而 Laravel 迁移后常出现 session 驱动不一致(比如从 file 切到 redis)、session 生命周期错乱,或多个并发请求把刚生成的验证码冲掉了。
- 迁移时若未同步更新
CACHE_DRIVER和SESSION_DRIVER,session()->put('captcha', ...)可能写入失败但无报错 - 使用
mews/captcha时,其generate()和check()方法默认都操作 session,而 session 在跨请求/跨机器场景下不可靠 - 哪怕配置了 redis session,Laravel 的 session 写入是延迟的(响应头发出后才真正保存),导致
check()时还没写完
必须把验证码改存到 Cache 而非 Session
用 Cache::put() 替代 session()->put() 是最直接有效的解法,且要绑定会话标识或 IP,避免被复用或枚举。
- 生成验证码时,key 建议用
'captcha_'.md5($request->ip().$request->userAgent()),比单纯用session()->getId()更抗干扰 - 显式设置 TTL,例如
Cache::put($key, $code, 120)(2 分钟),不要依赖 session 过期时间 - 验证前必须先调
Cache::has($key),不能直接Cache::get($key) === $input——Redis 惰性删除机制下,key 可能已过期但尚未被清理,get()返回 null 导致恒为 false - 验证通过后立刻
Cache::forget($key),防止重放;验证失败时不删,让用户还能重试
别漏掉缓存驱动和配置的连带检查
即使代码改了,如果底层缓存没配对,Cache::put() 依然会静默失败。
- 确认
.env中CACHE_DRIVER=redis(或memcached),且对应服务可连通;file驱动在多服务器部署下不可用 - 执行
php artisan config:clear && php artisan cache:clear,否则旧配置可能让Cache仍走 session 或 array 驱动 - 检查
config/cache.php中default是否指向你期望的 store,特别是用了多模态缓存时,别误设成array - 测试命令:进入
php artisan tinker,运行Cache::put('test_key', 'ok', 60) && Cache::has('test_key'),返回true才算通
真正容易被忽略的是:验证码校验逻辑里没做「存在性 + 值匹配」的原子判断,以及迁移后没清掉 bootstrap/cache/config.php —— 它可能还记着旧的 cache 配置,让所有 Cache::* 调用都失效。这两点不处理,换再多次 key 格式也没用。











