mysql服务端未启用ssl支持是pdo连接失败的主因,需确认have_ssl= yes、正确配置my.cnf并重启,yii2中ssl参数必须通过attributes设置而非dsn。

MySQL 服务端没开 SSL,pdo_mysql 连不上
Yii2 里配了 sslca、sslcert 等参数却报错 SSL connection error: unknown error number,大概率是 MySQL 服务压根没启用 SSL 支持。不是 Yii2 配错了,是底下的 mysqld 没准备好。
先连上 MySQL 执行:
SHOW VARIABLES LIKE 'have_ssl';返回
DISABLED 或 NO 就得去服务器上配 MySQL 的 my.cnf,加上:ssl-ca = /path/to/ca.pem<br>ssl-cert = /path/to/server-cert.pem<br>ssl-key = /path/to/server-key.pem重启
mysqld 后再查一次,必须是 YES 才行。
- 只开客户端 SSL 参数不生效——服务端必须支持且启用
-
have_openssl也得是YES,否则即使有ssl-ca文件也加载失败 - Docker 部署时容易漏掉挂载证书文件或没在容器内配置
my.cnf
Yii2 db 组件里怎么填 SSL 参数
不是所有 PDO DSN 写法都支持 SSL 选项,Yii2 必须通过 attributes 显式传入,dsn 字符串里加 ?sslmode=require 是无效的(那是 PostgreSQL 的写法)。
正确写法是在 config/db.php 里这样配:
'class' => 'yii\db\Connection',<br>'dsn' => 'mysql:host=localhost;dbname=myapp',<br>'username' => 'root',<br>'password' => 'xxx',<br>'attributes' => [<br> PDO::MYSQL_ATTR_SSL_CA => '/etc/mysql/ssl/ca.pem',<br> PDO::MYSQL_ATTR_SSL_CERT => '/etc/mysql/ssl/client-cert.pem',<br> PDO::MYSQL_ATTR_SSL_KEY => '/etc/mysql/ssl/client-key.pem',<br>],
-
PDO::MYSQL_ATTR_SSL_CA是必须的,哪怕只验证服务端身份 - 如果 MySQL 配了
require_secure_transport=ON,那客户端不带证书就直接被拒,连握手都进不去 - 路径必须是 PHP 进程能读到的绝对路径;用
__DIR__拼接比相对路径更稳
连接成功但查询报 SSL connection error
常见于证书过期、权限不对、或 CA 不匹配。PDO 建连时不会立刻校验证书,但一执行查询(比如 SELECT 1)就崩。
可以临时加个调试:在 attributes 里补上
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,让错误更明确。真实报错可能是:
SSL connection error: certificate verify failed 或 unable to get local issuer certificate。
- 检查
ca.pem是否包含完整的信任链(比如中间 CA 缺失) - 确认
client-cert.pem和client-key.pem是一对,且私钥没密码(PDO 不支持带密码的私钥) - Linux 下注意 SELinux 或 AppArmor 可能拦截 PHP 读证书文件,用
ls -Z看上下文
要不要用 sslmode=VERIFY_IDENTITY?
MySQL 原生不支持这个参数,它是 libpq(PostgreSQL 客户端)的概念。PDO for MySQL 没有对应机制,所谓“验证主机名”得靠 PHP 层自己做——比如在连接后执行 SHOW STATUS LIKE 'Ssl_cipher' 确认非空,再用 parse_url($dsn)['host'] 对比 Ssl_server_not_after 里的 CN(不推荐,太重)。
实际项目中,只要 PDO::MYSQL_ATTR_SSL_CA 正确设置,且 MySQL 开了 require_secure_transport,就已经能防中间人了。硬要验证域名反而容易因通配符或 SAN 不匹配导致连接失败。
- 别在 DSN 里写
sslmode=verify-full——PDO 会忽略,还可能干扰解析 - 生产环境优先保证 CA 根证书可信,而不是纠结证书里写的 hostname
- 自签名证书场景下,
ssl_ca必须是签发该服务端证书的那一个,不能随便换











