Laravel 通过动态构造 options 数组控制 MySQL SSL:仅当 DB_SSL_ENABLED=true 时注入 PDO::MYSQL_ATTR_SSL_* 参数,否则完全不传,避免强制 SSL 导致本地连接失败。

如何在 Laravel 中按环境控制 MySQL SSL 连接开关
直接结论:Laravel 本身不提供「SSL 开关」的抽象配置项,但可以通过 options 动态注入 PDO 的 PDO::MYSQL_ATTR_SSL_* 参数,并结合 .env 控制是否启用。关键不是“怎么开”,而是“怎么让本地跳过、生产强制走 SSL”且不报错。
Laravel 数据库配置里哪里加 SSL 参数
SSL 相关参数必须塞进数据库连接的 options 数组里,不能写在 host 或 database 等字段中。Laravel 的 config/database.php 支持对每个连接定义 'options' => [],PDO 初始化时会透传进去。
常见错误现象:SQLSTATE[HY000] [2002] SSL connection error 或 mysqli_real_connect(): SSL connection failed —— 多半是本地没配证书却硬开了 SSL;或者生产配了但路径写错/权限不对。
- 只在
mysql连接配置下加options,sqlite或pgsql不适用 - 必须用
PDO::MYSQL_ATTR_SSL_CA、PDO::MYSQL_ATTR_SSL_CERT、PDO::MYSQL_ATTR_SSL_KEY这些常量名,不能写字符串"ssl_ca" - 路径必须是绝对路径(尤其
storage_path()或base_path('certs/')),相对路径在 CLI 和 Web 环境下行为不一致
示例(仅示意结构,不建议直接复制):
'mysql' => [
'driver' => 'mysql',
'url' => env('DATABASE_URL'),
'host' => env('DB_HOST', '127.0.0.1'),
// ...其他字段
'options' => [
PDO::MYSQL_ATTR_SSL_CA => env('DB_SSL_CA') ? base_path(env('DB_SSL_CA')) : null,
PDO::MYSQL_ATTR_SSL_CERT => env('DB_SSL_CERT') ? base_path(env('DB_SSL_CERT')) : null,
PDO::MYSQL_ATTR_SSL_KEY => env('DB_SSL_KEY') ? base_path(env('DB_SSL_KEY')) : null,
],
],
为什么 DB_SSL_CA=null 会导致连接失败而不是降级
MySQL 客户端协议里,只要设置了任意一个 PDO::MYSQL_ATTR_SSL_*,PDO 就会强制走 SSL 握手流程。哪怕你只设了 PDO::MYSQL_ATTR_SSL_CA => null,底层仍会尝试 SSL 连接,然后因无有效 CA 而失败 —— 它不会自动 fallback 到非 SSL 模式。
所以「关闭 SSL」的正确做法不是传 null,而是**完全不传该 key**。这就要求用条件判断动态构造 options 数组。
- 别在
.env里写DB_SSL_CA=null,Laravelenv()会把它当字符串"null",不是 PHPnull - 用
env('DB_SSL_ENABLED', false)做布尔开关,再用if控制是否往options里 push 键值 - 本地开发推荐用
DB_SSL_ENABLED=false,生产用DB_SSL_ENABLED=true+ 实际证书路径
本地开发免证书又想兼容生产 SSL 的最小改动方案
最稳妥的做法是在 config/database.php 中对 mysql 连接做运行时判断,而不是靠配置文件合并或多环境 config 文件。因为 config:cache 会固化数组结构,而 options 是运行时才解析的。
实操建议:
- 在
.env新增DB_SSL_ENABLED=true(生产)或DB_SSL_ENABLED=false(本地) - 在
config/database.php的mysql配置块内,把options改成闭包或三元表达式生成的数组 - 证书路径统一用
base_path(env('DB_SSL_CA')),确保.env里填的是相对路径如certs/ca.pem,而非绝对路径 - 测试时用
php artisan tinker执行DB::connection()->getPdo(),看是否报错;不要只信php artisan migrate成功就以为 SSL 生效了
真正容易被忽略的点:Laravel 的 DB::reconnect() 不会重读 options,一旦连接已建立,改 .env 后必须重启队列进程、Horizon 或 FPM Worker 才能生效。


















