一、完整配置示例
server {
listen 80;
server_name www.it586.cn it586.cn;
return 301 https://www.it586.cn$request_uri;
}
server {
listen 443 ssl;
http2 on;
server_name www.it586.cn;
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
root /var/www/it586;
index index.html;
location / { try_files $uri $uri/ =404; }
}
旧版本 Nginx 用 listen 443 ssl http2;,1.25.1 之后改为 http2 on;,写错会报 unknown directive 或警告。
二、证书链必须拼接
cat www.it586.cn.crt intermediate.crt > fullchain.pem
顺序是站点证书在前、中间证书在后,根证书不需要。顺序反了或漏了中间证书,浏览器可能「自动补齐」看起来正常,但 curl、Java、部分移动端会报 unable to find valid certification path。
验证:
openssl s_client -connect www.it586.cn:443 -servername www.it586.cn -showcerts
输出里应该能看到至少两张证书。
三、80 跳 443 的正确写法
return 301 https://$host$request_uri;
要点:
- 用
301而不是302,让搜索引擎转移权重。 - 带上
$request_uri,保留原路径和查询串,否则所有旧链接都会跳到首页。 - 如果站点规范域名是 www,这里直接写死
https://www.it586.cn,同时解决「http→https」和「裸域→www」两件事。
注意:做这个跳转前先确认证书覆盖了裸域。如果证书只有 www.it586.cn,用户访问 https://it586.cn 会先看到证书错误,跳转都来不及发生。
四、TLS 版本与套件
- 只开
TLSv1.2 TLSv1.3,关掉 TLS 1.0/1.1(已不安全,且主流浏览器早已不支持)。 - 套件交给默认值通常就好,强行指定一长串反而容易漏掉新协议。
ssl_prefer_server_ciphers off让客户端选择,配合 TLS 1.3 性能更好。
五、HSTS 要谨慎
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
一旦下发,浏览器在 max-age 内强制用 HTTPS 访问,证书出问题时用户连「继续访问」都点不了。首次上线建议先用 max-age=300 试运行,确认证书续期正常后再加长。
六、续期之后
免费证书 90 天到期,自动续期后必须让 Nginx 重新加载:
certbot renew --quiet
nginx -s reload
只 renew 不 reload,Nginx 还在用内存里的旧证书,浏览器依旧报过期。建议在续期钩子里直接加 reload:
certbot renew --deploy-hook "nginx -s reload"
七、常见问题
| 现象 | 原因 |
|---|---|
SSL_CTX_use_PrivateKey ... key values mismatch | 证书与私钥不配对 |
| 浏览器「不受信任」 | 中间证书缺失或用了自签名 |
| 部分资源被拦截 | 页面里还有 http:// 的图片/脚本(混合内容) |
| 老客户端连不上 | 只开了 TLS 1.3,或套件过严 |
| 证书过期但已续期 | 没 reload |
私钥文件权限建议 600,属主为 root,不要让 Nginx 以外的用户可读。
小结
fullchain.pem = 站点证书 + 中间证书,顺序不能反;80 端口用 301 带 $request_uri 跳转;只开 TLS 1.2/1.3;HSTS 先短后长;续期后必须 reload。