私有仓库使用HTTPS+自签名证书时,composer install报错“unable to get local issuer certificate”,根本原因是Composer未加载指定CA证书;必须在composer.json的repositories配置中显式设置options.ssl.cafile为PEM格式、绝对路径、权限644的完整证书链文件,相对路径、波浪号、错误格式或顺序均导致静默失败。

私有仓库用 HTTPS + 自签名证书时,composer install 报 SSL certificate problem: unable to get local issuer certificate,不是证书“不合法”,是 Composer 根本没加载你给的 CA;必须在 repositories 里显式配 options.ssl.cafile,且路径、格式、权限三者缺一不可。
composer.json 里怎么写 ssl.cafile 路径
只在项目级 composer.json 的对应仓库配置中生效,不能靠全局 cafile 覆盖:
-
options.ssl.cafile是唯一被识别的字段,写成ssl.cafile或cafile都无效 - 值必须是 PEM 格式证书链的**绝对路径**,比如
/etc/ssl/certs/internal-ca.pem;相对路径(./certs/ca.pem)或波浪号(~/certs/ca.pem)会静默失败 - Windows 下路径用正斜杠或双反斜杠:
C:/certs/internal-ca.pem或C:\certs\internal-ca.pem;单反斜杠(C:certsca.pem)会被 PHP 当作转义符处理 - 证书文件需含完整链:中间 CA 在前、根 CA 在后;用
cat intermediate.pem root.pem > combined.pem拼接,别手动复制粘贴(BOM 和换行符会破坏解析)
为什么全局 config ssl.cafile 不管用
全局 composer config --global ssl.cafile 会影响所有 HTTPS 请求(包括访问 packagist.org),但私有仓库的 options.ssl.cafile 优先级更高——一旦它存在,Composer 就忽略全局设置;所以问题常出在:
围绕关键发现、作用机制、临床相关性及研究局限性展开讨论。适用于撰写或优化任何生物医学论文的“讨论(Discussion)”部分——包括结果解读、与既往文献关联、阐释意外发现、界定研究局限性,以及撰写结论。当用户输入以下任一指令时也会自动触发该功能: - “write my discussion” - “help me discuss my findings” - “how do I compare to prior studies” - “write the limitations par
- 路径写错或文件不存在:运行
ls -l /path/to/internal-ca.pem确认真实存在且可读 - PHP 进程无权限读取:Docker 中 www-data 用户可能读不了
/root/certs/;建议统一放/etc/ssl/certs/并设权限为644 - 证书不是 PEM 格式:用
openssl x509 -in your-ca.crt -text -noout验证是否以-----BEGIN CERTIFICATE-----开头;DER 格式需先转:openssl x509 -inform DER -in your-ca.cer -outform PEM -out ca.pem - Composer ≥2.5 版本静默 fallback:如果
cafile加载失败,它不会报错,而是退回到系统默认 CA 路径,继续报错 60 —— 所以得先确认系统 CA 是否本身就不含你的内网根证书
Docker 构建中证书总失效的硬伤点
明明 COPY 了证书、也写了 composer config --global ssl.cafile,但 composer install 还是报错,大概率卡在这几个地方:
-
apk add --no-cache ca-certificates必须在composer config之前执行(Alpine 镜像);否则openssl_get_cert_locations()返回空,Composer 找不到默认 CA 路径,连 fallback 都没得 fallback - 别在
Dockerfile里用VOLUME或tmpfs挂载证书路径;Composer 读的是容器内静态文件,挂载点在构建阶段不可见 - CI 环境(如 GitHub Actions)中,证书路径要对所有 runner 类型一致;GitHub-hosted runners 默认没装
ca-certificates,得自己apt-get install -y ca-certificates - 路径权限检查不能只看
ls -l:进容器跑php -r "echo file_get_contents('/app/certs/internal-ca.pem') ?: 'fail';",直接验证 PHP 进程能否读取
secure-http=false 和 disable-tls 是陷阱
这两个配置和自签名证书问题无关,还可能让问题更难定位:
-
secure-http=false只允许 Composer 接受 HTTP 源,但私有仓库仍是 HTTPS 协议,它根本不会触发;而且 packagist.org 已强制 HTTPS,设了也没用 -
disable-tls=true是彻底关闭 TLS,导致无法校验服务端身份,签名验证链断裂,composer diagnose会直接标红secure-http: FAIL - 混用
secure-http=false和options.ssl.cafile会导致后者被跳过——因为协议层都关了,证书校验自然不执行
最易被忽略的是:证书链顺序和文件纯净度。哪怕多一个空行、少一级中间 CA、或用了带 BOM 的 UTF-8 编辑器保存,OpenSSL 就拒绝解析,而 Composer 不报具体原因,只甩一句 “unable to get local issuer certificate”。

















