ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

CentOS 8 Nginx SSL证书配置实战:从安装到自动续期

CentOS 8 Nginx SSL证书配置实战:从安装到自动续期 CentOS 8 上给 Nginx或 Tengine装 SSL 证书这事我前前后后做过不下几十次从最早的裸配 443 端口到后来批量迁移站点、接入自动续期每一步都踩过不少坑。最近又帮朋友处理了一台CentOS 8服务器上的证书续期问题索性把整套流程、配置思路和排查经验整理成这篇实操记录。这篇文章解决的是“拿到证书文件之后到底怎么放、怎么配、怎么验证、怎么续期”的问题适合正在用 Nginx 或 Tengine 跑业务的运维、站长以及需要自己动手部署 HTTPS 的后端开发。很多人觉得装证书就是填两个文件路径那么简单真上手就会发现证书链拼接、权限限制、跳转配置、OCSP 装订、自动续期这些环节每一个都能卡住你半小时。这篇文章我会按从设计到落地的顺序把这几个环节全部拆开讲透。1. 先想清楚证书选型与 Nginx/Tengine 方案拆解1.1 为什么非上 HTTPS 不可先说点基础的。SSL 证书这个名字其实有点误导准确说它是一套公钥基础设施里的身份凭证。服务器持有私钥对外公开的是证书里的公钥和身份信息。客户端连上来时服务器把证书发给客户端客户端用内置的 CA 根证书去验证这个证书是不是可信 CA 签发的验证通过后双方再通过密钥协商生成会话密钥后续所有数据都用对称加密传输。这个过程解决了两个核心问题数据在传输途中不被窃听篡改以及客户端确认自己连的确实是目标服务器而不是中间人。有人觉得自己的站没有登录功能、不涉及敏感数据就不用上 HTTPS。这个想法在现在的网络环境下已经行不通了。主流浏览器对纯 HTTP 站点会直接打上“不安全”的标记用户看到这个标识的跳出率非常高。另外 HTTP/2 和即将全面推广的 HTTP/3 在主流浏览器里都要求 HTTPS 才能启用不上证书你连现代协议的红利都吃不到性能上就先输一截。单纯的 HTTP 数据就像明信片路上经手的人都能看到内容HTTPS 是上了锁的快递箱只有收件人手里的钥匙能打开箱子上的封条还能证明中途没有被拆过。1.2 Nginx 和 Tengine 选哪个Nginx 和 Tengine 的关系可以理解成 Tengine 是 Nginx 的增强兼容分支配置语法、模块体系、目录结构基本一致。Tengine 基于 Nginx 做了很多性能优化和功能增强比如动态模块加载、更好的负载均衡算法但值得注意的是它从某个版本开始基于 Nginx 1.24 分支演进两者在 SSL/TLS 相关配置指令上几乎完全兼容。我的建议很简单如果你已经在跑 Nginx 就没必要为了“更好用”冒险迁移到 Tengine如果新项目且团队对 Tengine 有维护经验用它也没问题。这篇文章里的所有配置在两个环境里都可以直接照抄。从 HTTP 服务层面上说证书配置的核心就三个要素证书文件放哪、私钥文件放哪、443 端口怎么监听。这个逻辑在 Nginx 和 Tengine 里毫无区别。真正需要注意的是底层 OpenSSL 版本差异它决定了你支持哪些 TLS 协议版本和加密套件。CentOS 8 系统自带的 OpenSSL 1.1.1 已经支持 TLS 1.3这意味着只要 Nginx 版本不是太老你不需要额外编译就能开启 TLS 1.3这一点比 CentOS 7 时代省事不少。1.3 证书文件格式和构成申请到的证书通常会有几个文件服务器证书、中间证书、私钥有的 CA 还会给你根证书。部署时需要把服务器证书和中间证书拼成一个完整链路文件这叫 fullchain私钥单独放。PEM 格式就是 base64 编码的文本块以-----BEGIN CERTIFICATE-----开头这是 Linux 环境下 Nginx 唯一需要关心的格式。如果从 Windows 平台导出的是 PFX 格式需要先转成 PEM 再部署。2. 动手前准备生成密钥、申请证书与环境自查2.1 证书类型选哪种证书类型直接决定申请流程和成本。低防护级别的域名验证型证书只验证域名所有权申请流程最快几分钟就能签发适合个人站点、测试环境、中小业务。高防护级别的企业验证型证书需要提交企业营业执照等材料CA 会人工审核签发时间按天算适合电商、金融、政企类站点。还有更高级别的扩展验证型证书浏览器地址栏会显示企业名申请流程更严格。如果你的场景就是给公司官网、API 网关或内部系统加 HTTPS低防护级别的免费证书无论是安全性还是成本上都够用了。这里重点推荐通过 ACME 协议自动签发和续期的免费证书。ACME 是自动化证书管理协议客户端程序向 CA 证明你对域名的控制权后CA 自动签发证书有效期通常为 90 天。它最大的价值在于全流程自动化申请、验证、签发、部署、续期一条龙彻底告别“证书过期导致站点挂掉”这种低级事故。商业证书的优势是有效期更长、有企业身份背书、有技术支持但价格不菲且续期操作繁琐。自签名证书只适合内网测试浏览器会报不受信任不要用在生产环境。2.2 用 OpenSSL 生成私钥和 CSR申请证书之前需要自己生成私钥和证书签名请求。私钥生成我建议用 RSA 2048 位起步保守一点可以上 3072 位。ECDSA 密钥性能更好但兼容性要看你客户端的广泛程度生产环境我通常 RSA 和 ECDSA 双证书配合。生成命令如下openssl genrsa -out example.com.key 2048 openssl req -new -key example.com.key -out example.com.csr \ -subj /CCN/STBeijing/LBeijing/OExample Inc/OUIT/CNexample.com这里有一个关键点现在 CA 普遍要求证书里必须带 SAN也就是主题备用名称纯 CN 字段的证书已经不被信任了。所以要么在 CSR 生成时加上 SAN 扩展要么在 CA 的控制面板里重新填写域名列表。多域名证书和泛域名证书在 CSR 阶段就要声明对应域名。建议把example.com和www.example.com都写上否则访问根域名时证书不匹配浏览器照样报警。私钥生成时不要设置密码短语。如果设置了Nginx 每次启动或 reload 都需要人工输入密码虽然可以用ssl_password_file指令解决但多一个文件就多一份泄露风险纯属自找麻烦。私钥的权限后面讲这里先记住一条铁律私钥永远不要出现在命令行参数里不要上传到任何第三方平台不要提交到 Git 仓库。2.3 CentOS 8 环境检查与依赖准备动手配置之前先把环境状态摸清楚。CentOS 8 官方源已经停止维护如果这台机器还在用旧版系统跑生产你需要先确认 yum 源还能不能用、核心组件的安全补丁怎么解决。我建议至少把系统升级到兼容评估版本或者换用其他长期维护的发行版这台机器上的 Nginx 和 OpenSSL 版本直接决定后续配置方式。需要检查的内容按顺序来cat /etc/redhat-release # 确认系统版本 nginx -v # 确认 Nginx 版本建议 1.20 以上 openssl version # 确认 OpenSSL 版本1.1.1 以上才支持 TLS 1.3 sestatus # 确认 SELinux 状态SELinux 这个问题特别容易坑新手。CentOS 8 默认 SELinux 是 enforcing 模式如果 Nginx 尝试读取一个没有正确上下文的私钥文件会被 SELinux 直接拒绝表现就是nginx -t提示权限不足。解决办法有两个一是用restorecon -v /etc/nginx/ssl/example.com.key恢复正确上下文二是对自定义证书目录执行semanage fcontext -a -t httpd_config_t /etc/nginx/ssl(/.*)?然后restorecon -Rv /etc/nginx/ssl。我强烈建议用第二种方式直接把整个目录的 SELinux 类型指定好以后新增证书文件只要执行一次restorecon就完事。直接用setenforce 0关掉 SELinux 虽然省事但生产环境绝对不建议这么干属于把门锁拆了换安全感。3. 核心实操把 SSL 证书配置进 Nginx 并强制跳转 HTTPS3.1 证书文件放置与权限管理我习惯把所有站点的证书统一放在/etc/nginx/ssl/下面每个域名一个子目录比如/etc/nginx/ssl/example.com/。目录里面放fullchain.pem和example.com.key两个文件。fullchain.pem 是服务器证书和中间证书拼接后的完整链路文件顺序必须是服务器证书在最前中间证书在后顺序反了客户端校验链就会失败。私钥权限设置为 root 有读写权限其他人一律不可访问mkdir -p /etc/nginx/ssl/example.com mv fullchain.pem /etc/nginx/ssl/example.com/ mv example.com.key /etc/nginx/ssl/example.com/ chown -R root:root /etc/nginx/ssl chmod 600 /etc/nginx/ssl/example.com/example.com.key chmod 644 /etc/nginx/ssl/example.com/fullchain.pem很多人不理解为什么私钥是 600 权限后 Nginx 还能读。Nginx master 进程是以 root 身份启动的只有 worker 进程会降权到 nginx 用户。读取私钥这个动作发生在 master 进程解析配置阶段所以 root 能读就够了。如果你用非 root 方式启动 Nginx比如通过 systemd 指定了Usernginx那私钥权限就得改成chown root:nginx且权限至少是 640否则 reload 的时候会报cannot load certificate key。3.2 写一份能直接用的 443 配置核心配置块长这样这是我目前在生产环境用的精简模板server { listen 443 ssl; http2 on; server_name example.com www.example.com; ssl_certificate /etc/nginx/ssl/example.com/fullchain.pem; ssl_certificate_key /etc/nginx/ssl/example.com/example.com.key; ssl_session_cache shared:SSL:10m; ssl_session_timeout 1d; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; ssl_prefer_server_ciphers on; ssl_stapling on; ssl_stapling_verify on; resolver 127.0.0.1 valid300s; location / { root /usr/share/nginx/html; index index.html; } }逐个说参数含义。listen 443 ssl是监听 443 端口并启用 SSL 协议的旧式写法新版本 Nginx 也支持。http2 on是 Nginx 1.25.1 之后推荐的写法老版本是在listen后面直接加http2参数如果你用的版本较老改成listen 443 ssl http2;就行。ssl_certificate指向拼接好的完整链路文件ssl_certificate_key指向私钥这两个是所有配置的基石。ssl_session_cache设置会话缓存10m 大约能存 10 万个会话 ID对于中小流量站点完全够用作用是让相同客户端复用会话密钥减少重复握手开销。ssl_session_timeout 1d设置会话有效期一天。ssl_protocols直接关掉 TLS 1.0 和 1.1这两个老版本协议已经有严重已知漏洞现在没有任何业务场景需要兼容它们。只保留 TLS 1.2 和 1.3覆盖了目前所有主流浏览器和移动设备。ssl_ciphers那一长串是推荐的现代加密套件集合优先使用 ECDHE 密钥交换和 AEAD 对称加密算法。这样的配置在安全扫描里的评级通常是 A 或 A。ssl_prefer_server_ciphers on表示优先使用服务端定义的加密套件而不是客户端建议的这能防住一些老客户端主动降级到弱加密套件的行为。3.3 HTTP 全部跳转到 HTTPS光配好 443 还不够还要把 80 端口的所有访问请求指到 HTTPS 上。80 端口单独开一个 server 块做跳转server { listen 80; server_name example.com www.example.com; return 301 https://$host$request_uri; }这里用$host而不是$server_name是有讲究的。$host会取请求头里的 Host 字段能保证用户通过 IP、泛域名或临时域名访问时跳转到对应域名如果用$server_name就会固定跳转到配置里的域名碰到用户用 IP 访问的场景就白屏了。301 跳转是永久跳转浏览器会记住这个跳转关系下次直接请求 HTTPS减少一次往返开销。网上一堆用 rewrite 实现跳转的教程其实完全没必要rewrite 会跑一遍正则性能上不如 return。如果你的站点想彻底杜绝 HTTPS 降级风险可以加上 HSTS 响应头add_header Strict-Transport-Security max-age31536000; includeSubDomains; preload always;但这里有个大坑HSTS 一旦下发浏览器会在设定的时长内强制走 HTTPS中途如果你想临时关闭 HTTPS 排查问题用户那边会直接访问失败。所以生产环境务必在 HTTPS 稳定运行一段时间后再加 HSTS并且先用max-age86400短时长验证确认没问题再改成一年。3.4 验证配置并平滑重载每次改完配置都要先测试再重载nginx -t systemctl reload nginxnginx -t会解析配置文件并检查证书文件是否能正常读取如果这里报错根据报错信息修改。配置文件合法后执行systemctl reload这个动作是平滑重载已有的连接不会中断新连接会走新配置。注意生产环境千万不要用systemctl restart nginx重启会瞬间断开所有活跃连接如果站点正在跑长连接或正在上传下载大文件用户端会直接被切断。重载后用 OpenSSL 自带的客户端工具做一次握手验证openssl s_client -connect example.com:443 -servername example.com \ -showcerts /dev/null重点看输出里subject是不是你的域名、issuer是不是签发证书的 CA、verify return code是不是 0并且链条显示0 s:到根证书每一环都完整。再配合curl -vI https://example.com检查 HTTP 响应头确认返回 200 而不是证书错误。4. 证书续期与自动化别再等浏览器弹警告才更新4.1 手动续期的完整流程无论免费证书还是商业证书都有有效期。商业证书通常是一年ACME 签发的是 90 天。手工续期的流程和第一次申请基本一样重新生成 CSR私钥可以复用提交给 CA通过验证后下载新证书替换服务器上的 fullchain.pem然后 reload。整个过程最耗时的是 CA 的验证环节。ACME 协议通常用两种验证方式。HTTP 方式要求 CA 能访问到http://example.com/.well-known/acme-challenge/token这个 URLChallenge 文件需要放在站点根目录下。如果你用的是类似/usr/share/nginx/html的默认目录记得确认这个路径下能正常写入文件并且 Nginx 不会因为 location 规则拦截.well-known路径。DNS 方式要求你在域名的 DNS 解析服务商那里临时添加一条 TXT 记录这种方式适合域名没有对外开放 80 端口、或者正在迁移服务器的场景。4.2 ACME 自动化续期部署思路如果你用 ACME 客户端自动管理证书建议把私钥和证书统一放到/etc/nginx/ssl/域名/目录客户端的部署钩子脚本在续期成功后自动把新证书拷贝过去、执行nginx -t并触发 reload。部署钩子的伪代码大致是这样的#!/bin/bash DOMAINexample.com DEPLOY_DIR/etc/nginx/ssl/$DOMAIN cp $CERT_PATH $DEPLOY_DIR/fullchain.pem cp $KEY_PATH $DEPLOY_DIR/example.com.key chown root:root $DEPLOY_DIR/* chmod 600 $DEPLOY_DIR/example.com.key restorecon -Rv $DEPLOY_DIR 2/dev/null || true nginx -t systemctl reload nginx这里最关键的一步是nginx -t放前面只有配置合法才执行 reload。我见过有人写的钩子直接systemctl reload nginx结果某次续期拿到的新证书和配置不匹配Nginx 直接加载失败整站瞬间挂掉。这种事故完全可以通过前置校验来避免。自动化任务建议用系统自带的定时器每天跑一次检查。ACME 客户端本身会判断证书剩余有效期一般剩余不足 30 天才会真正续期所以每日任务开销很小。4.3 多域名和泛域名证书管理如果你的站有多个子域名建议申请一张多域名证书把域名全部列进去。子域名多到一定规模一张泛域名证书覆盖*.example.com更省事。泛域名证书只能通过 DNS 方式验证因为 CA 要为所有子域名做验证HTTP 方式不可能逐一处理。管理的目录结构我建议按域名拆/etc/nginx/ssl/ ├── example.com/ │ ├── fullchain.pem │ └── example.com.key ├── api.example.com/ │ ├── fullchain.pem │ └── api.example.com.key └── *.example.com/ ├── fullchain.pem └── wildcard.example.com.key这样每个 server 块引用的路径一目了然排错时不用猜。多域名证书续期只需要跑一次 ACME 流程但泛域名和单域名证书要分开跑别写进同一个任务里。5. 排坑实录证书链、混合内容、OCSP 装订一次说清5.1 证书链不完整引发的信任告警症状是浏览器地址栏出现锁异常用curl返回证书链验证失败但纯用openssl s_client又看起来正常。经典原因是你只把服务器证书填进了ssl_certificate少了中间证书。CA 签发的证书链里服务器证书是叶子它需要被中间 CA 签名中间 CA 又被根 CA 签名。客户端只信任内置的根证书如果服务端不发中间证书客户端就没法构建出完整的信任链。解决办法是把中间证书内容追加到服务器证书后面也就是 fullchain 拼接。文件里有两个BEGIN CERTIFICATE块第一个是服务器证书第二个是中间证书。如果拼接反了有的客户端会报错有的能自动修正但增加验证开销。拼接完用这条命令检查链条顺序openssl crl2pkcs7 -nocrl -certfile fullchain.pem | openssl pkcs7 -print_certs -noout输出结果里第一条应该是你的域名证书第二条是中间证书。顺序不对就调整后重新nginx -t systemctl reload nginx。证书链问题在校验命令里其实一眼就能看出来只要verify return code不是 0基本就是链路拼接的问题。5.2 混合内容被浏览器拦截证书配好、HTTPS 正常访问之后经常遇到页面排版错乱、图片不显示、接口加载失败的情况。打开浏览器开发者工具的 Console 面板会看到大量类似“Mixed Content”的报错。原因很简单页面主体已经走 HTTPS但页面里引用的 JS、CSS、图片、接口请求还是 HTTP 地址。浏览器出于安全策略会直接阻止这些不安全子资源的加载。排查思路分三步。第一在源码层面把资源引用改成相对路径比如//example.com/js/app.js这种方式会自动跟随当前页面的协议。第二检查后端接口返回的 URL 是不是写死了 HTTP这段逻辑要全局搜。第三如果有反向代理确认代理转发时正确处理了X-Forwarded-Proto头否则后端程序在生成页面链接时仍然以为是 HTTP 请求。5.3 OCSP 装订配置的意外状况OCSP 装订是 Nginx 的一个性能和安全优化特性。它的作用是让服务器主动从 CA 的 OCSP 服务器查询证书吊销状态把结果缓存并随证书一起发给客户端省去客户端自己反向连接 CA 查询的等待时间。配置我已经在模板里写了但这里要说一个容易踩的坑Nginx 在开启ssl_stapling on后需要配置resolver指令指定 DNS 解析服务器否则 Nginx 无法解析 OCSP 服务器地址功能等于没开。更麻烦的是如果 CA 的 OCSP 服务器访问不稳定Nginx 可能整个站点都拒绝服务。我的建议是生产环境先ssl_stapling_verify on配合日志观察一段确认没有报错后再长期开着。另外部分 CDN 和云负载均衡器本身就带 OCSP 装订能力做了多层架构时不需要每层都开启反而容易出连锁故障。5.4 高频问题速查表症状检查命令常见原因解决要点nginx -t报权限不足ls -l /etc/nginx/ssl/*.key私钥权限过宽或属主不对SELinux 上下文错误chmod 600chown root:rootrestorecon浏览器提示“不安全”或证书已过期openssl s_client -connect 域名:443 -servername 域名证书到期、证书链不完整、域名不匹配检查证书有效期和 subject拼接 fullchainHTTP 能访问HTTPS 一直转圈curl -vI https://域名443 端口没监听、防火墙拦截确认ss -lntp里 443 已监听firewall-cmd 放行页面布局乱了但证书正常浏览器控制台看 Mixed Content页面有 HTTP 子资源资源改成相对协议或 HTTPS移动端访问证书报错但电脑正常openssl s_client看 chain 数量缺少中间证书部分移动端不自动补链拼接完整证书链TLS 版本显示 1.0nginx -V看 TLS 支持Nginx 太老或配置没生效升级 Nginx确认ssl_protocols TLSv1.2 TLSv1.3reload 后老连接断开systemctl status nginx误用 restart 替代 reload以后统一用systemctl reload nginx我做批量 HTTPS 迁移时交过一次学费当时图快直接在服务器上用脚本批量替换证书文件结果有几台机器证书链拼接顺序搞错了偏偏浏览器本地缓存了旧的验证结果很多用户直到第二天才发现问题。后来我立了一条规矩任何证书变更必须在测试环境走完nginx -t加openssl s_client的完整验证链确认足够安全后再推到生产绝不跨过验证直接上。这个方法虽然慢一点但风险屏障厚很多也推荐你照此执行。最后再分享一个小技巧证书文件目录里保留上一次的 fullchain 副本命名带日期后缀比如fullchain.pem.20250101.bak。证书更新后如果出现异常可以在几秒内回滚到旧版本。这个习惯救过我两次成本几乎为零但价值极高。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表