Route::captcha() 必须放在所有路由最前面,否则会被通配路由捕获导致404或空白;需排除session/auth等中间件干扰,并确保响应头为image/png。

Route::captcha() 必须放在所有路由最前面
验证码图片返回 404 或空白,八成是路由注册顺序错了。Route::captcha() 生成的 /captcha/{config?} 是一个动态路径,如果它被写在 Route::any('{any}', ...)->where('any', '.*') 或其他 catch-all 路由之后,请求会直接被后者捕获,根本不会走到验证码逻辑。
正确做法是在 routes/web.php 顶部、任何 Route::get/Route::post 之前就声明:
Route::captcha(); // 必须第一行或至少在所有通配路由之前
// ✅ 正确示例
Route::captcha();
Route::get('/', [HomeController::class, 'index']);
Route::post('/login', [AuthController::class, 'login']);
- 不要把它塞进
Route::middleware(...)->group(...)里,除非你明确需要中间件——但多数情况下不该加 - 如果你用了
Route::prefix('api')或Route::domain('admin.example.com'),Route::captcha()仍要放在全局作用域,否则子组内无法访问 - Laravel 9+ 启用 Package Discovery 后,仍需手动确认没有重复注册
CaptchaServiceProvider,否则配置可能加载失败导致路由不生效
验证码路由不能被中间件拦截
图片响应是二进制流(Content-Type: image/png),一旦经过 session、auth、locale 或 CORS 中间件,极容易被提前终止或 header 冲突,结果就是空白图或 500 错误。
最稳妥的方式是显式排除中间件:
Route::captcha()->withoutMiddleware([
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\Auth\Middleware\Authenticate::class,
\App\Http\Middleware\SetLocale::class,
]);
- 尤其注意
StartSession:虽然验证码依赖 session 存 hash 值,但它本身不启动 session,而是复用已有 session —— 所以拦截它反而会切断验证链 - 如果你自定义了全局中间件(比如日志、设备指纹),务必检查是否对
/captcha/*做了无差别拦截 - 开发时可临时加
dd('captcha route hit');在CaptchaController或服务提供者里验证是否真进来了
不用 Route::captcha()?那就得自己建控制器并确保响应类型正确
有些项目因历史原因没用 Route::captcha(),而是手写控制器。这时最容易踩的坑是忘记设置 header 或返回方式错误。
错误写法:return view('captcha') 或 return response(captcha())(缺 header);
正确写法必须明确指定 MIME 类型:
use Illuminate\Http\Response;
use Mews\Captcha\Facades\Captcha;
class CaptchaController extends Controller
{
public function show($config = 'default')
{
$img = Captcha::create($config);
return response($img['image'])
->header('Content-Type', $img['mime']); // 必须!不能省
}
}
-
Captcha::create()返回的是数组,不是字符串,直接 echo 会报错 - 别用
response()->json()包装图片数据——前端<img src>只认二进制流,不认 JSON - 若你用的是
gregwar/captcha等替代方案,它的inline()方法已含 header,但build()->output()不含,得自己补
前端 src 地址必须匹配后端路由路径
Blade 里写 {{ captcha_src('flat') }} 却显示 404?说明前端请求的 URL 和后端注册的不一致。
captcha_src() 默认拼的是 /captcha/flat,所以后端必须有对应路径支持——这正是 Route::captcha() 的作用。如果你改过路由前缀(比如注册成了 Route::get('verify/captcha', ...)),那 captcha_src() 就失效了。
- 要么统一用
Route::captcha()+ 原生辅助函数,别动路径 - 要么弃用
captcha_src(),手写 URL:<img src="/verify/captcha?{{ time() }}">,同时确保该路由返回正确图片流 - 点击刷新用
Math.random()是防缓存,但前提是基础路径能通;否则只是反复刷 404



















