AJ-Captcha中fontPath必须为服务器绝对路径且PHP进程可读,仅支持.ttf/.otf字体;中文需字体内置Unicode字符集,推荐NotoSansCJKsc-Regular.otf等开源字体,并用file_exists()和is_readable()提前校验。

字体路径在 AJ-Captcha 中由 fontPath 参数控制
AJ-Captcha 的 PHP 版本(如 aj-captcha-php 或基于 GD 的自定义实现)默认不自动加载系统字体,必须显式指定一个可读的 .ttf 文件路径。这个路径不是 URL,而是服务器本地文件系统路径,且需确保 PHP 进程有读取权限。
常见错误是填了 Web 可访问路径(如 /assets/fonts/msyh.ttf)或相对路径(如 ./fonts/msyh.ttf),结果 GD 报错:imagettftext(): Could not find/open font。
-
fontPath必须是绝对路径,推荐用__DIR__ . '/fonts/msyh.ttf'或realpath(__DIR__ . '/../public/fonts/arial.ttf') - 字体文件建议放在项目目录内(如
/fonts/),避免依赖系统路径(/usr/share/fonts/...在容器或共享主机中常不可靠) - Windows 下注意反斜杠转义问题,统一用正斜杠或
DIRECTORY_SEPARATOR,例如__DIR__ . DIRECTORY_SEPARATOR . 'fonts' . DIRECTORY_SEPARATOR . 'simhei.ttf' - 执行前可用
file_exists($fontPath) && is_readable($fontPath)主动校验,比等 GD 报错更早发现问题
GD 扩展对字体格式和编码有硬性要求
AJ-Captcha 依赖 GD 的 imagettftext() 渲染文字,它只支持 TrueType(.ttf)和 OpenType(.otf)字体,且要求字体文件内嵌字符集能覆盖你要生成的验证码内容(比如中文需含 GB2312/Unicode 中文区段)。
很多免费下载的“微软雅黑”或“思源黑体”压缩包里实际是 .ttc(字体集合)或带版权限制的 .ttf,GD 无法加载——表现为验证码显示方块、空格,或直接报 Failed to parse font。
立即学习“PHP免费学习笔记(深入)”;
- 优先使用无版权风险的开源字体,例如
NotoSansCJKsc-Regular.otf(Google Noto 系列)、DejaVuSans.ttf(英文足够,轻量) - 用命令行快速验证字体是否被 GD 认可:
php -r "var_dump(gd_info()['FreeType Support']);"必须为true;再试imagettfbbox(12, 0, '/path/to/font.ttf', '测')是否返回数组 - 如果验证码含中文但字体不支持,不要试图用 iconv 或 mb_convert_encoding 转码——问题在字体本身,不是 PHP 字符串编码
配置位置取决于你用的是哪个 AJ-Captcha 封装
没有统一的 “AJ-Captcha 官方 PHP SDK”,所以 fontPath 设置方式因实现而异。常见三种情况:
- 若用的是
aj-captcha-php(GitHub 上较活跃的第三方封装),在初始化时传入:$captcha = new \AjCaptcha\Captcha([ 'fontPath' => __DIR__ . '/fonts/NotoSansCJKsc-Regular.otf', 'fontSize' => 24, ]); - 若自己基于 GD 写的简易版,通常在生成函数内硬编码:
imagettftext($image, $size, 0, $x, $y, $color, <strong>/absolute/path/to/font.ttf</strong>, $text) - 若集成进 Laravel / ThinkPHP 等框架,注意配置可能被缓存,改完
config/captcha.php后要运行php artisan config:clear或清空 Runtime 缓存
字体路径错误时最典型的两个现象
不是所有报错都直接说“字体找不到”。GD 的静默失败很常见,得结合日志和输出判断:
- 验证码图片完全空白(HTTP 响应头正确但 body 为空)→ 检查
fontPath是否存在、是否可读、GD 是否启用 FreeType - 验证码显示为一串方框(□□□□)或问号(????)→ 字体支持了,但字符集不匹配,换支持 UTF-8 中文的 .ttf/.otf,别用仅含 ASCII 的英文字体
真正麻烦的是字体路径语法看似正确,但 PHP 运行用户(如 www-data)没权限读取 —— 这时候 file_exists() 返回 false,但你不主动检查就永远卡在“为什么明明路径对却没字”。



















