Nginx HTTPS 配置实战:SSL 证书部署、TLS 优化与常见错误排查

个人站长把网站从 HTTP 升级到 HTTPS,早已不是"可选项"而是"必选项":Chrome 会把没有证书的页面直接标记为不安全,搜索引擎明确把 HTTPS 作为排名信号,而 HTTP/2、HTTP/3 这些提速技术也都要求先有 HTTPS。Nginx 作为个人站长最常用的 Web 服务器,配置 HTTPS 并不复杂,但证书链、TLS 版本、HSTS、OCSP 这些细节如果处理不好,会出现"手机能开电脑打不开"、"提示证书错误"这类奇怪问题。这篇文章以实战为主线,从证书准备、server 块配置、TLS 安全基线、性能优化到常见错误排查,把 Nginx 部署 HTTPS 的全流程讲透。

一、证书准备:搞清楚证书链再动手

很多新手第一次配置 HTTPS 失败,不是因为 Nginx 写错,而是证书文件本身有问题。浏览器验证证书时,会沿着"服务器证书 → 中间证书 → 根证书"这条链逐级向上验证。你从证书商那里下载的证书文件通常只有服务器证书,中间证书需要单独下载拼接,否则大部分浏览器会报"证书链不完整"。

个人站长最省心的方案是用 Let's Encrypt 的 certbot 自动申请,它会自动帮你处理好证书链和续期。首次申请可以这样执行:

certbot certonly --webroot -w /var/www/html -d example.com -d www.example.com --email you@example.com --agree-tos --no-eff-email

申请成功后证书文件在 /etc/letsencrypt/live/example.com/ 目录下:fullchain.pem 是服务器证书加中间证书的完整链,privkey.pem 是私钥。配置 Nginx 时用 fullchain.pem 而不是 cert.pem,这是最常见的坑——只配 cert.pem 就会遇到链不完整的问题。如果你是购买商业证书或者用其他工具签发的证书,记得把服务器证书和中间证书按顺序拼接成一个文件再使用。

私钥文件的权限也要注意。Nginx 的 worker 进程以低权限用户运行,但 master 进程启动时需要读取私钥。把私钥权限设置为 600、属主设为 root 最稳妥:

chmod 600 /etc/letsencrypt/live/example.com/privkey.pem
chown root:root /etc/letsencrypt/live/example.com/privkey.pem

如果你的网站访客以国内用户为主,可以考虑申请 ECC 证书。ECC 算法用更短的密钥就能达到同等安全强度,TLS 握手时运算量更小,在低端 CPU 和老旧手机上握手速度优势明显,同时证书体积小、握手包更少,对移动网络更友好。certbot 默认签发 RSA 证书,加 --key-type ecdsa 参数即可签发 ECC 证书。需要提醒的是,使用 ECC 证书时 ssl_ciphers 里要保留 ECDSA 相关的套件(例如 ECDHE-ECDSA-AES128-GCM-SHA256),否则部分客户端可能握手失败。如果拿不准,RSA 证书搭配现代加密套件完全够用,不必为了追求新特性给自己引入额外变量,先把基础配置跑稳更重要。

二、Nginx server 块配置与 HTTP 跳转

证书准备好之后,在 Nginx 配置里新增一个监听 443 的 server 块。以本站常用的 LNMP 环境为例,站点配置文件一般在 /usr/local/nginx/conf/vhost/ 或 /etc/nginx/conf.d/ 目录下:

server {
    listen 443 ssl;
    server_name example.com www.example.com;
    root /var/www/html;
    index index.php index.html;

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

    location / {
        try_files $uri $uri/ =404;
    }
}

配置写完后一定先执行 nginx -t 检查语法,确认输出 syntax is ok 再平滑重载,避免写错配置把线上服务搞挂:

nginx -t
nginx -s reload

接下来把原来的 HTTP 请求全部 301 跳转到 HTTPS。单独保留一个监听 80 的 server 块做跳转,比在同一个 server 块里写两个 listen 更清晰:

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

跳转这里有一个细节值得注意:用 $host 变量会取请求头里的 Host,能保留用户访问的原始域名,这样 example.com 和 www.example.com 都能正确跳到对应的 HTTPS 地址,不会出现全部跳到同一个域名的问题。如果你用了 CDN 或者反代,还要考虑用户是通过 IP 直连还是域名访问,避免跳转死循环。

三、TLS 安全基线:协议版本与加密套件

证书只是第一步,TLS 的配置强度直接决定网站的安全性。SSLv2、SSLv3 早已被证明可被 POODLE 等攻击破解,TLSv1.0 和 TLSv1.1 也因存在已知漏洞在 2020 年后被主流浏览器弃用。现在的最低标准是 TLSv1.2,能开 TLSv1.3 就开。在 server 块里加上:

ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;

这里解释几个关键参数:ssl_session_cache 开启会话缓存后,同一个客户端在超时时间内再次访问可以跳过完整的 TLS 握手,明显降低重复访问的延迟,10m 大约能缓存两万个会话;ssl_session_tickets 建议关闭,因为会话票据的密钥如果泄露,攻击者可以解密记录的流量,关闭后配合会话缓存同样能拿到复用效果,安全性更高。

OCSP Stapling 是另一个值得开启的加速项。默认情况下浏览器拿到证书后要自己去 OCSP 服务器查询证书吊销状态,多一次网络往返;开启 Stapling 后由 Nginx 代查并把结果随握手一起发给浏览器,省掉这次往返。配置如下:

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

resolver 指令必须配置,Nginx 需要用它解析 OCSP 服务器的域名。国内服务器建议把 223.5.5.5(阿里 DNS)放在前面,避免某些国外 DNS 被污染导致 Stapling 失效。

四、HSTS 与安全响应头

HSTS(HTTP Strict Transport Security)的作用是告诉浏览器:以后访问这个域名只允许用 HTTPS,浏览器会记住这个约定,即使你输入 http:// 也会被自动改写成 https://,从源头杜绝中间人把 HTTPS 降级成 HTTP 的攻击。配置非常简单:

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

max-age 单位是秒,31536000 正好是一年;includeSubDomains 表示子域名也生效。这里必须提醒一个 Nginx 的经典坑:add_header 指令有继承特性,如果 server 块里写了 add_header,那么 location 块里如果没有自己的 add_header 就会全部失效。所以要么把 HSTS 写在所有 location 之前并且不要在 location 里重复定义其他 add_header,要么在需要下发响应头的 location 里把 HSTS 重新写一遍。加上 always 参数可以保证连 404、302 这类非 200 响应也带上该头。

注意:HSTS 一旦开启,浏览器在一年内都会强制 HTTPS,所以一定要确认 HTTPS 完全稳定了再开,否则临时想退回 HTTP 调试会发现浏览器死活不肯放行,只能等 max-age 过期或者让用户在浏览器里手动清理站点数据。

五、HTTP/2 与传输优化

开启 HTTP/2 后,浏览器与服务器之间所有资源共享一个连接,多路复用消除了 HTTP/1.1 的队头阻塞问题,对页面里几十个静态资源的站点提速非常明显。老版本 Nginx 直接在 listen 指令上加 http2 参数即可:

listen 443 ssl http2;

Nginx 1.25.1 之后的版本把 http2 改成了独立的指令,写法是 listen 443 ssl; 然后单独写 http2 on;,升级 Nginx 后配置不生效先检查是不是这个原因。开启后可以用浏览器的开发者工具确认协议版本,网络面板里能看到资源走的协议是 h2。

此外建议配合开启 gzip 压缩文本类资源。HTML、CSS、JS 经过 gzip 后体积能减少六成以上,配合 HTTPS 一起做,页面加载速度会有质的提升。压缩配置写在 http 块即可:

gzip on;
gzip_min_length 1k;
gzip_comp_level 6;
gzip_types text/plain text/css application/javascript application/json application/xml image/svg+xml;
gzip_vary on;

六、常见错误排查清单

部署过程中遇到问题不要慌,按下面的清单逐项排查,绝大多数问题都能快速定位。

第一,手机浏览器打不开、提示证书链问题,但电脑 Chrome 正常。这几乎可以断定是证书链不完整——只配置了服务器证书没带中间证书。用下面的命令看服务端实际下发的证书链:

openssl s_client -connect example.com:443 -servername example.com

输出里 Certificate chain 部分应该有多层证书,如果只有一层,把 fullchain.pem 换上去即可。

第二,提示证书域名不匹配(NET::ERR_CERT_COMMON_NAME_INVALID)。说明证书的域名和访问的域名对不上,要么证书申请时没包含该域名,要么访问的是证书外的域名。检查证书包含哪些域名:

openssl x509 -in /etc/letsencrypt/live/example.com/fullchain.pem -noout -text | grep -A1 "Subject Alternative Name"

第三,Nginx 启动报错 SSL_CTX_use_PrivateKey_file 相关错误。通常是私钥文件权限过大或者私钥与证书不匹配,检查 chmod 600 是否执行,以及确认证书和私钥确实是同一对(重新申请即可解决)。

第四,页面能打开但里面有些图片、脚本加载不出来,控制台报 mixed content。这是页面里残留了 http:// 的资源链接,HTTPS 页面加载 HTTP 资源会被浏览器拦截。全站搜索替换 http:// 为 https://,或者给站点配置 CSP 头 upgrade-insecure-requests 让浏览器自动升级。

第五,443 端口不通。检查云服务商的安全组、防火墙是否放行了 443,本地用 telnet 或 nc 测试端口连通性:

nc -vz example.com 443

第六,证书过期导致网站突然打不开。certbot 的证书有效期只有 90 天,必须配置自动续期。certbot renew 命令会检查所有证书,临近过期才会续期,可以加 --dry-run 测试:

certbot renew --dry-run

通过后把它写进 crontab 每天执行两次,并在续期成功后自动重载 Nginx:

0 3,15 * * * certbot renew --quiet --deploy-hook "nginx -s reload"

七、验证与持续监控

全部配置完成后,用 curl 验证一下实际效果:

curl -sI https://example.com/ | head -20

响应头里应该能看到 HTTP/2 200、strict-transport-security、以及 ocsp 相关特征。想全面评估 TLS 强度,可以用 ssllabs.com 的在线检测工具,评分达到 A 就说明配置合格。证书过期是定时炸弹,建议写一个简单的巡检脚本扔进 crontab,每天检查证书剩余天数,低于 30 天就发邮件提醒自己,避免某天早上网站突然打不开才手忙脚乱。

HTTPS 配置是一次投入长期受益的工作:安全上杜绝了流量被窃听篡改,SEO 上是明确的正向信号,性能上 HTTP/2 带来的提升立竿见影。把这套配置沉淀成自己的模板,以后每新开一个站点都能十分钟内完成部署,这才是个人站长该有的效率。

Last modification:September 5th, 2026 at 07:56 am

Leave a Comment