把 Nginx HTTPS 配扎实:证书路径、TLS 参数、HSTS 与验证步骤

从证书文件落盘到 listen 443 ssl、TLS 版本与加密套件、HSTS 响应头的逐项配置,说明每一步的前置条件和验证方法,并覆盖 80 端口跳转、OCSP Stapling 与续期后 reload 的常见坑。

本文目录
把 Nginx HTTPS 配扎实:证书路径、TLS 参数、HSTS 与验证步骤

很多"HTTPS 配了但浏览器还是告警"的现场,最后查出来都不是什么高深问题:证书路径写错一个字母、ssl_certificate 用了中间证书而不是 fullchain,或者 reload 忘了执行。下面按顺序把一份能过浏览器和扫描工具检查的 Nginx HTTPS 配置走一遍,每一步都给出验证方法。

前置条件

  • 域名解析已生效,A/AAAA 记录指向这台机器,且 80/443 在防火墙和云安全组里都放通。
  • 已拿到证书,最好是用 ACME 客户端签的 Let's Encrypt 证书,文件权限别让普通用户可读私钥。
  • Nginx 编译时带 ngx_http_ssl_module(绝大多数发行版包默认带,nginx -V 2>&1 | grep ssl 确认)。

证书文件用哪个

这是最常见的事故点。签发后目录里通常有四个文件,Nginx 只需要两个:

ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

用 fullchain.pem,不要用 cert.pem。后者只有服务器证书,缺中间证书,桌面浏览器多数能自己补链,但部分 Android 老设备和不少 API 客户端会直接握手失败——报错还特别隐晦,像是网络抖动。

验证链是否完整:

openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null | openssl x509 -noout -issuer -subject

也可以用 curl -vI https://example.com 看握手阶段有没有 unable to get local issuer certificate 之类的报错。

server 块主体

server {
    listen 443 ssl;
    http2 on;
    server_name example.com;

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers off;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 1d;

    root /var/www/example;
    # ...其余配置
}
把 Nginx HTTPS 配扎实:证书路径、TLS 参数、HSTS 与验证步骤:DNS ownership verification using a delegated CNAME, shown as a precise handoff between two controlled zones
把 Nginx HTTPS 配扎实:证书路径、TLS 参数、HSTS 与验证步骤:DNS ownership verification using a delegated CNAME, shown as a precise handoff between two controlled zones查看原图

几个取舍说明:

  • http2 on; 是新写法;旧版 Nginx 用 listen 443 ssl http2;,两者别混写,会报重复警告。
  • TLS 1.3 的套件由协议定死,ssl_ciphers 管不到它。如果只保留 1.2 + 1.3,Mozilla 推荐的中间档套件对绝大多数站点够用,不必手写一长串。
  • session cache 给共享内存一个名字,多 worker 才能复用,否则每个 worker 各存一份,恢复率低。

改完先 nginx -t,再 systemctl reload nginx。顺序反过来也不是不行,只是你可能半夜收到同事的问候。

80 端口跳转

单独一个 server 块,别和 443 的混在一起,续期走 HTTP-01 时也好处理:

server {
    listen 80;
    server_name example.com;
    return 301 https://$host$request_uri;
}

验证:curl -sI http://example.com/ 应返回 301,Location 指向 https。注意 curl 默认不跟随跳转,加 -L 才是浏览器的行为。

HSTS 要谨慎地加

HSTS 告诉浏览器"之后只许走 HTTPS",是个单向承诺,写错了没有后悔药——max-age 内你只能换域名。正确姿势是小步爬坡:

add_header Strict-Transport-Security "max-age=300" always;

先给 5 分钟,确认全站没有遗留的 HTTP 资源引用、没有忘记配证书的子域,再逐步拉到 15768000(180 天)。两个坑:

  • includeSubDomains 只有在你确定所有子域(包括别人搭的测试站)都有有效证书时才加,否则等于亲手把同事的测试环境送走。
  • preload 是提交到浏览器内置名单的入口,提交后想撤销要等浏览器发版周期,慎重。
  • always 不能省,否则错误页面(如 502)不带这个头。
把 Nginx HTTPS 配扎实:证书路径、TLS 参数、HSTS 与验证步骤:an ACME request being validated at separate stations and turned into a new deployable artifact while private key material remains at its origin
把 Nginx HTTPS 配扎实:证书路径、TLS 参数、HSTS 与验证步骤:an ACME request being validated at separate stations and turned into a new deployable artifact while private key material remains at its origin查看原图

验证:

curl -sI https://example.com/ | grep -i strict-transport

顺手开 OCSP Stapling

让服务器代替客户端去查证书吊销状态,省一次第三方请求:

ssl_stapling on;
ssl_stapling_verify on;
resolver 223.5.5.5 8.8.8.8 valid=300s;
resolver_timeout 5s;

resolver 必须显式写,用你网络里可靠的递归 DNS。验证:echo | openssl s_client -connect example.com:443 -servername example.com -status 2>/dev/null | grep -i "ocsp response",看到 OCSP Response Status: successful 即生效。

续期之后别忘了 reload

证书文件续期换了内容,但 Nginx 已经把旧证书读进内存,不 reload 就还在用旧的。Certbot 系客户端可以用 deploy hook:certbot renew --deploy-hook "systemctl reload nginx"。

在 CertbotX 中实践时,这一步可以省掉手工 hook:它通过 ACME 自动签发并在到期前重新签发,完成后能按配置把新证书自动部署到 Agent、EdgeOne 或 CDN 目标,并留下部署记录供审计。如果域名 DNS 托管在别处、不方便改 NS,可以用 DNS-01 的 CNAME 委托,把 _acme-challenge 指给 CertbotX 管理的区域完成校验,主 DNS 区域不动。

收尾自检清单

  • nginx -t 通过,reload 后 journalctl -u nginx -n 20 无报错。
  • curl -vI https://example.com 握手成功、证书 subject/issuer 正确。
  • http 跳转、HSTS 头、OCSP stapling 三项各自的验证命令都过了一遍。
  • 证书到期日看一下:openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null | openssl x509 -noout -enddate,确认和续期配置对得上。

---

← 返回资讯中心