🔒 第03章 · Nginx TLS 配置
ssl_certificate 配置块解析
03 | Nginx TLS 配置¶
证书签好了,接下来就是让 Nginx 把这套 TLS 真正跑起来。这一章逐行拆解本机真实配置文件:certbot 往 server 块里写了什么、
options-ssl-nginx.conf里的安全参数是干嘛的、80 -> 443跳转块怎么来的、以及反向代理场景里 TLS 究竟在哪一层终止。
Nginx 在 TLS 架构里的位置¶
Nginx 通常扮演 TLS 终止(TLS Termination) 的角色:对外终结加密连接、解密出明文 HTTP,再转发给后端应用。只要明确"加密到 Nginx 为止",后面的 proxy_pass 走的是内网明文,配置思路就清晰了。
客户端 --443/TLS--> Nginx(终止 TLS, 解密) --http--> 后端应用(127.0.0.1:3000)
对比另一种模式 TLS 透传(Pass-through):Nginx 只做转发不解密,把加密流原样交给后端(如
stream模块). 普通 Web 应用几乎都用"终止"模式,因为 Nginx 要读请求头做路由、加安全头、做日志,必须先解密。本机所有站点都是终止模式。
一个完整 server 块的 TLS 部分(本机真实配置)¶
server {
server_name bangdream.xyz www.bangdream.xyz;
# 业务逻辑:代理到后端(被 /location/ 匹配)
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme; # 告诉后端"原始是 https"
}
# —— 下面是 certbot 自动写入的 TLS 块(managed by Certbot)——
listen 443 ssl; # 监听 443 并启用 SSL
ssl_certificate /etc/letsencrypt/live/bangdream.xyz/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/bangdream.xyz/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}
逐行拆解 certbot 写的四条指令¶
| 指令 | 作用 | 指向(本机) |
|---|---|---|
listen 443 ssl; |
让 Nginx 在 443 端口监听并启用 SSL |
listen 443 ssl; |
ssl_certificate |
指定证书链(含叶证书+中间证书) | /etc/letsencrypt/live/bangdream.xyz/fullchain.pem |
ssl_certificate_key |
指定私钥 | /etc/letsencrypt/live/bangdream.xyz/privkey.pem |
include options-ssl-nginx.conf |
引入 certbot 维护的密码套件/协议安全参数 | /etc/letsencrypt/options-ssl-nginx.conf |
ssl_dhparam |
指定 DH 参数,用于(旧式)DHE 密钥交换 | /etc/letsencrypt/ssl-dhparams.pem |
关键理解:
ssl_certificate必须指向fullchain.pem(整条链),而不是cert.pem(只有叶证书)。回想第一章的证书链——客户端需要"叶子+中间"才能验证到根。只发cert.pem会unable to get local issuer certificate。
options-ssl-nginx.conf 拆解(certbot 的安全参数)¶
这个文件基于 Mozilla 的 SSL 配置建议生成,长期由 certbot 更新。本机内容:
ssl_session_cache shared:le_nginx_SSL:10m; # 会话缓存,复用握手结果
ssl_session_timeout 1440m; # 会话缓存 24 小时
ssl_session_tickets off;
ssl_protocols TLSv1.2 TLSv1.3; # 只开安全的两个版本
ssl_prefer_server_ciphers off; # 交给客户端偏好(表意)
ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:...:DHE-RSA-AES128-GCM-SHA256:...";
ssl_protocols TLSv1.2 TLSv1.3:明确禁用老旧的 TLS 1.0 / 1.1(有 POODLE / BEAST 漏洞)。ssl_session_cache shared:le_nginx_SSL:10m:缓存 TLS 会话,避免每个连接重新握手,提升性能。- 那条很长的
ssl_ciphers是 密码套件(cipher suite)白名单,都是现代 GCM/CHACHA20 系,能借"前向保密"(Forward Secrecy,ECDHE)让历史会话不被未来私钥泄漏破解。
易错点:不要手动改这个文件——文件开头就写了"如果你改它,Certbot 将无法自动提供后续安全更新,只会报错并把路径告诉你"。真想调 TLS 参数,就在自己的 server 块里额外覆盖指令,或者在
ssl-config.mozilla.org生成一份自己的 include 文件替换include指向。
80 -> 443 的跳转 server 块¶
certbot 会把"纯 HTTP 的 80 端口"改成一个 301 跳转块(本机真实内容):
server {
if ($host = bangdream.xyz) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80 default_server;
listen [::]:80 default_server;
server_name bangdream.xyz www.bangdream.xyz;
return 404; # managed by Certbot
}
作用:用户访问 http://bangdream.xyz 时,被 301 重定向到 https://bangdream.xyz,浏览器的地址栏因此永远是 HTTPS。return 404 兜底,防止无 TLS 的裸 80 被直接命中。
易错点 1:这里的
listen 80 default_server;和server_name bangdream.xyz ...需要和某个具体域名匹配,否则普通 HTTP 到这台机器会落到错误的 server(默认会走listen 80 default_server),产生诡异的 404 或跳错域名。易错点 2:
return 301不是 302。301 是永久重定向,浏览器和搜索引擎都会记住并缓存,更利于 SEO 和防止 "http 版本仍在被收录"。
自定义一个"理想"的 server 块¶
如果你不想完全依赖 certbot,也可以手写一个(注意别和 certbot 的 # managed by Certbot 行冲突):
server {
listen 443 ssl;
http2 on; # 开启 HTTP/2(比 http1 更快,防队头阻塞)
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_session_cache shared:le_nginx_SSL:10m;
ssl_session_timeout 1440m;
ssl_session_tickets off;
ssl_stapling on; # OCSP stapling(第五章详述)
ssl_stapling_verify on;
resolver 127.0.0.11 valid=30s; # 供 OCSP 解析用
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme; # 后端以此判断是否 https
}
}
易错点:
ssl_stapling需要resolver才能查 OCSP;如果没有 resolver 或网络不通,staple 就起不来。另外新版 Nginxhttp2 on;是单独指令,旧版用listen 443 ssl http2;——版本不同写法不同,别照抄。
场景:反向代理必须把协议传给后端¶
TLS 终止在 Nginx,所以后端看到的是明文 HTTP。后端要生成安全的 https:// 绝对地址、正确处理 cookie 的 Secure 标记,就必须知道原始协议。这就是 X-Forwarded-Proto 的职责:
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme; # $scheme 在 https 连接下就是 "https"
proxy_redirect off;
}
本机所有站点(含 GitLab)的代理块里都写了
proxy_set_header X-Forwarded-Proto $scheme;。丢了它,后端会误以为自己是 http,生成错误的重定向或强制跳回 http,造成"页面无限重定向"。
server_name 与 SNI¶
现代客户端发起 TLS 握手时,会在 ClientHello 里带一个 SNI(Server Name Indication) 字段告诉服务器"我要访问哪个域名",Nginx 据此挑选 server_name 对应的证书。所以一台机上挂多个 HTTPS 域名,每域名一张证书,靠 SNI 区分:
| 域名 | 证书(live/ 下) | Nginx server_name |
|---|---|---|
| bangdream.xyz | bangdream.xyz | bangdream.xyz www.bangdream.xyz |
| dsh.bangdream.xyz | dsh.bangdream.xyz | dsh.bangdream.xyz |
| gitlab.bangdream.xyz | gitlab.bangdream.xyz | gitlab.bangdream.xyz |
| lovelive.top | lovelive.top | lovelive.top www.lovelive.top |
易错点:若同一 IP 的
server块没有listen ssl,或server_name没匹配,客户端会用第一个有 SSL 的默认 server 的证书——导致明明访问了gitlab.bangdream.xyz,浏览器却弹出 "证书是 bangdream.xyz 的" 警告。
检查 Nginx 配置¶
# 语法检查(必做,配完先测)
nginx -t
# 热加载(不中断服务)
nginx -s reload
易错点:任何修改 nginx 配置后,先
nginx -t,再nginx -s reload。-t报错就说明有语法或路径问题,直接 reload 会起不来服务。
延伸:多证书与后续升级¶
certbot --nginx -d在续期时会自动保留并更新你 server 块里的 TLS 行——只要别动# managed by Certbot标记。- 想整体换安全参数,用
https://ssl-config.mozilla.org生成现代配置,替换include指向并另存,再nginx -t && nginx -s reload。
下一章:04-cert-manage → 用 openssl 查看、校验、排查证书问题。