ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

PHP8.5配置HTTPS证书自动续期怎么弄

PHP8.5配置HTTPS证书自动续期怎么弄 前言先把版本这件事说清楚HTTPS 证书的签发与续期属于运维/部署层面的工作和 PHP 版本没有关系。标题里的 PHP 8.5 只是运行环境——证书由 Nginx、Apache 或负载均衡器持有PHP-FPM 作为 FastCGI 后端根本不参与 TLS 握手。所以本文不会去套 8.5 的语言特性而是讲清楚三件事证书为什么会过期、续期任务怎么配置才可靠、以及 PHP 在整套流程里能做什么监控与告警。证书过期这件事最坑的地方在于它不会提前报错。到期当天浏览器直接弹您的连接不是私密连接用户点继续的人不多App 里的接口调用则会因为 CA 校验失败整片失败。运维侧通常没有任何告警因为你没配置。第二个常见症状是续期成功了但线上还是旧证书。这几乎总是同一个原因自动化工具把新的证书文件写到磁盘上了但 Web 服务器进程还在用启动时加载进内存的那份——没有 reload。本文以最主流的 Lets Encrypt 为例证书有效期 90 天用 certbot 和 acme.sh 两条常见路线说明配置方法最后给一个用 PHP 写的到期监控脚本。一、为什么必须自动化90 天与 ACMELets Encrypt 的证书有效期是 90 天它的设计意图就是逼你自动化——手动续期一年四次总有一次会在长假里到期。续期通过 ACME 协议Automatic Certificate Management EnvironmentRFC 8555完成客户端证明自己控制该域名CA 签发新证书。概念说明有效期90 天续期窗口通常是剩余 30 天以内续期触发certbot renew只对快到期的证书动手没到期就跳过可用--force-renewal强制速率限制CA 对同一域名有签发频率限制所以不要用短周期 cron 反复强制续期调试请用--dry-run失败后果续期失败通常会重试但如果没人看日志最后就是过期不要用--force-renewal当日常任务它每次都会真的申请一张新证书很容易撞上速率限制反而把正常续期也拖垮。日常任务用renew验证流程用--dry-run。二、签发与验证方式怎么选ACME 有两种主流验证方式选错了会在申请通配符证书时卡住方式原理适用限制HTTP-01在站点根目录的/.well-known/acme-challenge/下放一个校验文件单域名、普通多域名不支持通配符需要 80 端口可达DNS-01在_acme-challenge加一条 TXT 记录通配符、内网机器、无法开放 80 端口需要 DNS 服务商的 API 凭据用 certbot 的典型命令# webroot 方式Nginx 已经在跑把校验文件放在站点根目录 certbot certonly --webroot -w /var/www/html -d example.com -d www.example.com # 由 certbot 自动改 Nginx 配置会修改配置文件建议先备份 certbot --nginx -d example.com # 验证续期流程不消耗签发配额最重要的一步 certbot renew --dry-run用 acme.sh 的典型命令它默认会自动装好定时任务# 单域名webroot 方式 acme.sh --issue -d example.com -d www.example.com --webroot /var/www/html # 通配符必须用 DNS 方式 acme.sh --issue -d example.com -d *.example.com --dns dns_ali # 安装证书并指定续期后要执行的重载命令 acme.sh --install-cert -d example.com \ --key-file /etc/nginx/ssl/example.com.key \ --fullchain-file /etc/nginx/ssl/example.com.fullchain.pem \ --reloadcmd systemctl reload nginx注意--install-cert里的--reloadcmd这一步才是续期之后真正生效的关键。acme.sh 每次续期成功都会执行它。三、Nginx 侧怎么配才对Nginx 里引用证书时请引用live目录软链接或者--install-cert指定的固定路径不要去引用archive目录里带编号的历史文件server { listen 443 ssl; server_name example.com; ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; }/etc/letsencrypt/live/example.com/下的文件是指向archive/里最新版本的软链接续期后软链接会被更新所以只要引用的路径不变reload 一下就能用上新证书。如果写死成archive/example.com/cert3.pem这种路径续期后会产生cert4.pem而 Nginx 还在读cert3.pem——表现为续期明明成功浏览器看到的还是旧证书。reload 用systemctl reload nginx它会平滑加载新配置老连接继续处理完不要用restart会切断正在处理的请求。四、PHP 能做什么到期监控与告警PHP 不持有证书但它很适合做巡检部署一个只在内网或后台访问的脚本定期读取证书文件算出剩余天数不足阈值就告警邮件、企业微信、钉钉机器人等。这样即使自动续期因为 DNS 凭据过期、ACME 校验失败等原因中断你也能在证书真正过期前几周收到通知。关键 API 是openssl_x509_read()加openssl_x509_parse()后者返回的数组里带有validTo_time_t到期时间戳不依赖外部命令也不需要 shell 权限。代码实战完整可运行示例把下面这段保存为cert-check.php需要PHP 8.1用到了never与str_contains()纯 CLI 用法。给$targets填上真实的证书路径即可若路径不存在脚本会跳过并计入失败。?php declare(strict_types1); // PHP 8.1 // 用法php cert-check.php [阈值天数默认 21] /** * 读取 PEM 证书返回剩余天数无法解析时返回 null * 注意读取叶子证书cert.pem即可fullchain.pem 里有多张证书取到的是第一张 */ function daysLeft(string $pemPath): ?int { if (!is_readable($pemPath)) { return null; } $pem file_get_contents($pemPath); if ($pem false || $pem ) { return null; } $cert openssl_x509_read($pem); if ($cert false) { return null; } $info openssl_x509_parse($cert); if (!is_array($info) || !isset($info[validTo_time_t])) { return null; } $seconds (int) $info[validTo_time_t] - time(); return (int) floor($seconds / 86400); } /** 域名 证书路径 */ $targets [ example.com /etc/letsencrypt/live/example.com/cert.pem, ]; $threshold (int) ($argv[1] ?? 21); $exitCode 0; foreach ($targets as $domain $path) { $days daysLeft($path); if ($days null) { printf([ERR ] %-20s 无法读取或解析: %s\n, $domain, $path); $exitCode 2; // 读取失败也要报警不能当成没问题 continue; } $level match (true) { $days 0 EXPIRED, $days 7 CRIT, $days $threshold WARN, default OK, }; printf([%-7s] %-20s 剩余 %4d 天\n, $level, $domain, $days); if ($days $threshold) { $exitCode 1; // 交给调用方cron / 监控系统判断 } } // 用退出码表达结果0 正常1 需要关注2 检查失败 exit($exitCode);输出形如[OK ] example.com 剩余 62 天把它配进 crontab每天跑一次任何非 0 退出码都触发告警很多监控系统直接支持命令退出码非 0 即报警# 每天 09:10 巡检一次证书剩余天数 10 9 * * * /usr/bin/php /opt/tools/cert-check.php 21 /dev/null 21 || /opt/tools/notify.sh 证书即将到期同时别忘了检查自动续期任务本身还活着# Debian/Ubuntu 上 certbot 用 systemd timer systemctl list-timers --all | grep certbot systemctl status certbot.timer # 传统 cron 方式常见于 /etc/cron.d/certbot grep -r certbot /etc/cron.d/ /etc/crontab 2/dev/null常见坑点坑 1续期成功但没有 reload线上仍是旧证书。❌ 只配了certbot renew没有任何重载动作证书文件更新了Nginx 内存里还是旧的。 ✅ 用 certbot 的--deploy-hook systemctl reload nginx只在成功续期后触发不是每次 renew 都触发或 acme.sh 的--reloadcmd。改完从外部连一次 443 端口用openssl s_client -connect 域名:443 -servername 域名取出线上返回的证书再交给openssl x509 -noout -enddate看到期时间确认生效的确实是新证书。坑 2定时任务静默失败没人发现。❌0 3 * * * certbot renew不带日志也不带告警DNS 凭据过期后连续失败几个月直到证书过期才被发现。 ✅ 加--quiet但保留输出重定向到日志并额外部署上一节的巡检脚本做兜底告警两者是防和查的关系缺一不可。坑 3Nginx 里引用了 archive 目录的具体文件。❌ssl_certificate /etc/letsencrypt/archive/example.com/cert3.pem;续期后新版本是cert4.pem配置还指着旧的。 ✅ 一律引用live目录下的软链接或--install-cert输出的固定路径。坑 4用--standalone续期但 80 端口被 Nginx 占着。❌ 续期时报端口已被占用而日常访问一切正常所以问题只在续期时暴露。 ✅ 有 Web 服务器在跑就用--webroot或对应插件坚持用 standalone 就得在续期钩子里停掉 Web 服务器风险更高不推荐。坑 5给通配符证书用 HTTP-01。❌certbot certonly --webroot -d *.example.com直接失败因为通配符无法用文件校验证明域名控制权。 ✅ 通配符必须走 DNS-01并且给 DNS API 凭据最小权限凭据泄漏等于别人能替你的域名签发证书。坑 6手动修改live/archive目录下的文件。❌ 手工改过的证书或私钥在下一次续期时被覆盖改动神秘消失。 ✅ 要调整参数改/etc/letsencrypt/renewal/*.conf要加域名就重新执行一次带全部-d的签发命令。坑 7加了新域名却忘了加-d。❌ 站点新增api.example.com证书里还是老的两个域名访问新域名报证书不匹配——而证书剩余天数还是满的巡检脚本显示 OK。 ✅ 加域名时重新签发并带上全部域名巡检脚本里也该检查证书的 SAN 列表是否覆盖预期域名openssl_x509_parse()返回的extensions.subjectAltName可以看到。坑 8以为证书续期要重启 PHP-FPM。❌ 每次续期都systemctl restart php8.5-fpm把正在处理的请求全部切断。 ✅ TLS 由 Nginx/负载均衡处理PHP-FPM 作为 FastCGI 后端与证书无关reload Nginx 就够了。只有 PHP 主动发起对外 HTTPS 请求时才涉及信任库curl.cainfo/ 系统 CA那是另一个话题。总结关注点结论适用范围证书续期是运维层面的事与 PHP 版本无关PHP 8.5 只是运行环境有效期90 天续期窗口约剩余 30 天内renew会跳过未到期证书验证方式单域名用 HTTP-01webroot通配符必须 DNS-01生效关键续期后必须 reload Web 服务器用 deploy-hook / reloadcmd 固化这一步路径引用Nginx 只引用live软链接或固定路径不引用 archive 里的编号文件失败兜底续期日志 每日巡检告警两条路都不能省调试用--dry-run验证流程别用--force-renewal当日常任务PHP 的角色读证书文件算剩余天数、发告警不参与 TLS 本身证书自动续期真正的难点不在怎么签而在怎么确认它一直有效任务要有人看日志证书要有人算剩余天数续期后要有人 reload。把这三件事分别用定时任务、巡检脚本和 deploy-hook 固化下来HTTPS 才不会在某天早上突然变成浏览器的红色警告页。
返回列表