captcha::create() 返回空白或 check() 恒为 false 的根本原因在于环境链路问题:输出流被提前破坏、session 未启用或跨域不一致、缓存替代方案可解决前后端分离校验难题。

think-captcha 不是开箱即用的功能,装完扩展、配好参数,Captcha::create() 依然返回空白或 check() 恒为 false——问题几乎全出在环境链路上,不是代码写错了。
为什么 Captcha::create() 返回空白图像?
这是最常卡住人的第一步。根本原因不是 GD 没装,而是 PHP 输出流被提前破坏。
- 响应头已发送:控制器方法里任何 echo、var_dump、Log::info(),甚至模板里多一个空行、BOM 字符,都会触发 headers already sent,导致二进制图像流无法输出
- GD 扩展未启用:运行 php -m | grep gd 确认输出 gd;若无,需在 php.ini 中取消 extension=gd 注释并重启 Web 服务
- 方法没走纯输出路径:不要在布局模板中调用 Captcha::create(),必须是独立路由绑定的纯接口,例如 Route::get('captcha', [\think\captcha\facade\Captcha::class, 'create']);
- 文件编码带 BOM:用 VS Code 或 Sublime Text 将控制器文件保存为 UTF-8 无 BOM 格式
为什么 captcha_check() 总是 false?
90% 的校验失败和逻辑无关,是上下文断裂。
- Session 未真正启用:检查 app/middleware.php 是否已取消注释 \think\middleware\SessionInit::class;手动调用 session_start() 无效,必须靠中间件初始化
- Session ID 不一致:前后端分离时,前端请求域名(如 http://localhost:8080)与后端 API(如 http://api.test)跨域,Cookie 不共享,$_SESSION 根本不是同一个容器
- 验证码已被消耗:check() 成功后自动清除 session 中的值;若 JS 重复提交、或用户刷新页面后重输旧验证码,必然失败
- 大小写不匹配:默认配置下校验区分大小写;前端输入 ab3x,后端生成的是 AB3X,直接比对会失败,建议统一转小写:strtolower($input) === strtolower(session('captcha_code'))前后端分离项目怎么安全校验?
硬配 withCredentials + CORS 在开发环境极不稳定,尤其 localhost 端口差异、SameSite 策略、子域名 Cookie 路径都会翻车。
- 改用缓存替代 session 存储:生成时调用 Captcha::create() 得到 ['code' => 'K9mP', 'img' => 'data:image/png;base64,...'],再用 cache($key, $code, 300) 存 5 分钟,把 $key(如 UUID)返回给前端
- 提交时带上 $key 和用户输入 $code,后端用 cache($key) 取值比对,成功后立刻 cache($key, null) 清除
- 注意关闭 config/captcha.php 中的 'api' => false(默认就是 false),否则 Captcha::create() 不会返回数组
哪些配置项真正影响线上表现?
别堆参数,盯住这四个关键项:
- 'expire':单位秒,建议设 300(5 分钟),太长易被重放,太短伤体验
- 'useNoise' 和 'useCurve':必须为 true,否则 OCR 识别率飙升;但开启后若字体加载失败(如 TTF 路径错),图像可能变黑块
- 'length':设 4 最平衡,6 以上用户输入错误率明显上升
- 'fontSize':设 25 左右,小于 18 易被绕过,大于 30 在小尺寸 imageW 下会截断
captcha_check() 总是 false?
90% 的校验失败和逻辑无关,是上下文断裂。
- Session 未真正启用:检查 app/middleware.php 是否已取消注释 \think\middleware\SessionInit::class;手动调用 session_start() 无效,必须靠中间件初始化
- Session ID 不一致:前后端分离时,前端请求域名(如 http://localhost:8080)与后端 API(如 http://api.test)跨域,Cookie 不共享,$_SESSION 根本不是同一个容器
- 验证码已被消耗:check() 成功后自动清除 session 中的值;若 JS 重复提交、或用户刷新页面后重输旧验证码,必然失败
- 大小写不匹配:默认配置下校验区分大小写;前端输入 ab3x,后端生成的是 AB3X,直接比对会失败,建议统一转小写:strtolower($input) === strtolower(session('captcha_code'))前后端分离项目怎么安全校验?
硬配 withCredentials + CORS 在开发环境极不稳定,尤其 localhost 端口差异、SameSite 策略、子域名 Cookie 路径都会翻车。
- 改用缓存替代 session 存储:生成时调用 Captcha::create() 得到 ['code' => 'K9mP', 'img' => 'data:image/png;base64,...'],再用 cache($key, $code, 300) 存 5 分钟,把 $key(如 UUID)返回给前端
- 提交时带上 $key 和用户输入 $code,后端用 cache($key) 取值比对,成功后立刻 cache($key, null) 清除
- 注意关闭 config/captcha.php 中的 'api' => false(默认就是 false),否则 Captcha::create() 不会返回数组
哪些配置项真正影响线上表现?
别堆参数,盯住这四个关键项:
- 'expire':单位秒,建议设 300(5 分钟),太长易被重放,太短伤体验
- 'useNoise' 和 'useCurve':必须为 true,否则 OCR 识别率飙升;但开启后若字体加载失败(如 TTF 路径错),图像可能变黑块
- 'length':设 4 最平衡,6 以上用户输入错误率明显上升
- 'fontSize':设 25 左右,小于 18 易被绕过,大于 30 在小尺寸 imageW 下会截断
'expire':单位秒,建议设 300(5 分钟),太长易被重放,太短伤体验
- 'useNoise' 和 'useCurve':必须为 true,否则 OCR 识别率飙升;但开启后若字体加载失败(如 TTF 路径错),图像可能变黑块
- 'length':设 4 最平衡,6 以上用户输入错误率明显上升
- 'fontSize':设 25 左右,小于 18 易被绕过,大于 30 在小尺寸 imageW 下会截断
Session 启动时机、输出流洁净度、跨域时的存储介质——这三个点任何一个出问题,整个验证码流程就静默失效。它不报错,只返回 false 或空白,得顺着链路一节一节摸过去。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











