Nginx 配置 SSL 证书的完整流程可以概括为四步:拿到「域名证书 + 中间证书」合并的 fullchain.pem、上传并改权限、在 server 块写 4 行核心指令(listen 443 ssl、ssl_certificate、ssl_certificate_key、ssl_protocols)、执行 nginx -t 通过后 reload。实际运维中 90% 的失败并非配置写错,而是证书链不完整、私钥与证书不匹配、443 端口未放行、改完没 reload 这四类问题。
Nginx 配置 SSL 证书要改哪几行?
结论:一个能跑通 HTTPS 的 server 块,核心配置只有 4 行,其余都是性能与安全加固项。
先把证书文件放到固定目录(推荐 /etc/nginx/ssl/),权限设为 600、属主 root,然后写入:
server {
listen 443 ssl;
http2 on;
server_name www.example.com;
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
root /var/www/html;
index index.html;
}
注意 http2 on; 是 Nginx 1.25.1 之后的写法,1.25.1 之前要写成 listen 443 ssl http2;,两种混用会直接报语法错误。
80 端口的 server 块单独写一段 301 跳转,不要在同一个块里混着判断:
server {
listen 80;
server_name www.example.com;
return 301 https://$host$request_uri;
}
改完执行 nginx -t 检查语法,返回 syntax is ok 和 test is successful 后,再执行 nginx -s reload。只 nginx -t 不 reload,线上仍是旧配置,这是很多人“明明改了却没生效”的直接原因。
证书链不完整导致手机打不开,怎么修?
结论:只部署域名证书、不带中间证书,是 Android 旧版本和部分小程序端提示“证书无效”的头号原因,修法是把中间证书拼到域名证书后面。
浏览器(如 Chrome、Safari)通常会自动补全中间证书,所以电脑上看着正常;而 Android 7 以下、微信内置浏览器、部分 Java 客户端不做补链,就会直接握手失败。
正确做法:
- 拿到服务商给的
domain.crt(域名证书)和 chain.crt(中间证书);
- 执行
cat domain.crt chain.crt > fullchain.pem,顺序不能反;
- 用
openssl crl2pkcs7 -nocrl -certfile fullchain.pem | openssl pkcs7 -print_certs -noout 查看,应该能看到 2 段及以上 subject/issuer;
- 全站生效后,用
openssl s_client -connect www.example.com:443 -servername www.example.com -showcerts 确认服务端返回的是完整链。
Let's Encrypt 用 certbot 签发时,/etc/letsencrypt/live/域名/ 下的 fullchain.pem 本身就是完整链,直接指向它即可,不要用 cert.pem。
nginx -t 报 key values mismatch 是什么原因?
结论:这条报错表示私钥与证书不是同一对,Nginx 拒绝加载,用两条 openssl 命令比对模数就能确认。
openssl x509 -noout -modulus -in fullchain.pem | md5sum
openssl rsa -noout -modulus -in privkey.pem | md5sum
两个 md5 值必须完全一致。不一致只有三种可能:证书和私钥来自不同次申请(重新签发过证书但没换 key)、文件在上传时被截断、或者误传了其它站点的文件。处理方式就是重新下载配对的两个文件覆盖。
同类的还有 nginx: [emerg] cannot load certificate ... PEM_read_bio_X509_AUX() failed,指向路径错误或文件内容损坏(比如用记事本打开保存过,行尾被改)。用 file fullchain.pem 确认是 ASCII text,且首行是 -----BEGIN CERTIFICATE-----。
配置全对但 443 打不开,查哪里?
结论:配置正确仍然连不上,按“防火墙 → 云安全组 → 端口占用 → SSL 模块”四层依次排查,不要反复改 nginx.conf。
- 本机防火墙:
firewall-cmd --add-service=https --permanent && firewall-cmd --reload(CentOS)或 ufw allow 443(Ubuntu);
- 云服务器安全组:阿里云、腾讯云需在控制台入方向放行 TCP 443,这一层最常被漏掉;
- 端口占用:
ss -lntp | grep 443,确认监听进程是 nginx 而不是被其它程序抢占;
- 编译模块:报
[emerg] the "ssl" parameter requires ngx_http_ssl_module 说明 Nginx 编译时没带 SSL 模块,用 nginx -V 2>&1 | grep -o with-http_ssl_module 核实,没有就得重新编译加 --with-http_ssl_module,改配置文件是无效的。
另外两个高频现象:SSL_ERROR_RX_RECORD_TOO_LONG 通常是用 https 访问了 80 端口,检查跳转是否写反;ERR_TOO_MANY_REDIRECTS 是 CDN 或后端程序也做了强制跳转,形成循环,后端应改读 X-Forwarded-Proto 请求头判断协议,而不是无条件 301。
证书到期怎么自动续期?
结论:Let's Encrypt 证书有效期 90 天,必须配置自动续期 + reload 钩子,否则第 91 天全站 HTTPS 直接中断。
certbot renew --dry-run # 先演练
在 /etc/letsencrypt/renewal/域名.conf 的 [renewalparams] 段加一行 renew_hook = systemctl reload nginx,或用 certbot renew --deploy-hook "systemctl reload nginx"。certbot 默认已注册 systemd timer,用 systemctl list-timers | grep certbot 确认存在即可。
自查到期时间的命令:echo | openssl s_client -servername www.example.com -connect www.example.com:443 2>/dev/null | openssl x509 -noout -dates。
整套流程跑下来,真正花时间的不是写配置,而是把证书链、权限、端口三件事做对。遇到报错别急着重写 nginx.conf,先把 nginx -t 的完整输出和 openssl s_client 的结果贴到 GJ 站长论坛(www.gj0.com)的问答区,基本一眼就能定位是哪一类问题。