根本原因是内网代理用自建CA证书劫持HTTPS流量,PHP的OpenSSL不自动读系统证书库,需手动将内网根证书与Mozilla官方CA包合并为combined.pem,并在php.ini中配置openssl.cafile指向该文件。

Composer 报 “unable to get local issuer certificate” 是代理在中间重签流量
这根本不是 Composer 或 PHP 配置错了,而是你所在网络(公司/学校/防火墙)用自建 CA 证书劫持了 HTTPS 流量。浏览器能过是因为你手动导入了那个内网根证书,但 PHP 的 OpenSSL 默认不读系统证书存储,只认 openssl.cafile 指向的 PEM 文件。
典型现象:composer install 失败,但浏览器访问 https://packagist.org 正常;curl -v https://packagist.org/packages.json 同样报错;php -r "print_r(openssl_get_cert_locations());" 显示的 default_cert_file 路径存在,但验证仍失败。
- 别急着改
composer config --global disable-tls true—— 这会让所有包走明文,凭据可能被截获 - 也别指望系统级
ca-certificates包自动生效,Linux/macOS 的 OpenSSL 不会自动加载系统证书库 - 关键动作是:把你们 IT 部门提供的那个内网根证书(通常是
.crt或.pem文件)加进 PHP 的信任链
怎么把公司代理证书加进 PHP 的 openssl.cafile
你得把内网根证书和 Mozilla 官方 CA 包合并成一个 PEM 文件,再让 PHP 读它。直接覆盖原 openssl.cafile 是最稳的做法,避免路径冲突或权限问题。
围绕关键发现、作用机制、临床相关性及研究局限性展开讨论。适用于撰写或优化任何生物医学论文的“讨论(Discussion)”部分——包括结果解读、与既往文献关联、阐释意外发现、界定研究局限性,以及撰写结论。当用户输入以下任一指令时也会自动触发该功能: - “write my discussion” - “help me discuss my findings” - “how do I compare to prior studies” - “write the limitations par
- 确认你拿到的内网证书是 PEM 格式(开头是
-----BEGIN CERTIFICATE-----),如果不是,用openssl x509 -in company.crt -out company.pem -outform PEM转换 - 下载最新官方 CA 包:
curl -o cacert.pem https://curl.se/ca/cacert.pem - 合并两个文件:
cat company.pem cacert.pem > combined.pem(Linux/macOS)或用记事本手动拼接(Windows,注意换行一致) - 把
combined.pem放到稳定路径,比如/etc/php/certs/combined.pem(Linux)或C:\php\extras\ssl\combined.pem(Windows) - 编辑
php.ini,设openssl.cafile="/etc/php/certs/combined.pem"(Linux)或openssl.cafile="C:/php/extras/ssl/combined.pem"(Windows) - 重启 CLI 环境(关掉终端重开)或 Web 服务,再跑
php -i | grep "openssl.cafile"确认路径已更新
为什么 composer config --global cafile 不起作用
因为 Composer 的 cafile 配置只影响它自己基于 php-http 的 HTTP 客户端,而 SSL 验证失败通常发生在底层 cURL 或 OpenSSL 扩展调用时——尤其是当 Composer 需要调用 Git 克隆私有仓库、或解析 packages.json 的初始 HTTPS 请求时,这些都绕不开 PHP 的扩展层。
-
composer config -g cafile /path/to/company.pem只对后续 Composer 自己发的 HTTP 请求有效 - 但它无法修复
git clone https://...报的SSL certificate problem: self signed certificate in certificate chain - 也无法解决
curl_setopt(): CURLOPT_SSL_VERIFYPEER failed这类底层错误 - 真正起效的永远是
php.ini里的openssl.cafile,不是 Composer 的任何配置项
CI/CD 环境里怎么处理代理证书
Docker 构建、GitHub Actions、GitLab CI 这些环境没有图形界面,也没法手动点“信任此证书”,必须显式注入证书文件并固化配置。
- Docker:在
Dockerfile中COPY company.pem /usr/local/etc/php/certs/,然后RUN echo "openssl.cafile=/usr/local/etc/php/certs/company.pem" >> /usr/local/etc/php/conf.d/docker-php-ext-openssl.ini - GitHub Actions:用
actions/upload-artifact或echo "${{ secrets.COMPANY_CERT }}" | tee /tmp/company.pem写入,再在run步骤中改php.ini - GitLab CI:把证书内容存为 CI 变量,用
echo "$COMPANY_CERT" > $CI_PROJECT_DIR/company.pem,再通过PHP_INI_SCAN_DIR加载额外 ini 文件 - 切记:不要在 CI 脚本里写
export COMPOSER_DISABLE_TLS=1—— 这等于把整个构建流水线的 HTTPS 验证关掉,风险极高
最易被忽略的一点:很多公司内网证书是二级 CA 签发的,只导入根证书还不够,得把中间证书一起塞进 combined.pem,否则 OpenSSL 仍会报 “unable to get local issuer certificate”。拿不准就找 IT 要完整的证书链 PEM 文件,而不是单个根证书。

















