HTTPS 不只是在监听端口后加上 ssl,而是一整套证书链、协议版本、加密套件与会话复用的工程组合。配置不当的 HTTPS 可能面临证书链不完整导致安卓客户端握手失败、只支持旧协议被安全扫描降级、未开启 OCSP Stapling 让每次握手都要外连 CA 等服务问题。本文从证书链结构讲起,覆盖 TLS 协议与套件策略、HTTP/2 与 ALPN、OCSP Stapling、会话复用与 HSTS,给出可直接落地的生产配置。
核心认知:TLS 安全是"协议版本 + 套件白名单 + 证书可信链"三者的交集。三者中任一项配置错误,都会让 HTTPS 形同虚设或直接不可用。
1. 证书链与 ssl_certificate
1.1 证书链的组成
一个完整的 HTTPS 证书链通常包含三部分:
终端实体证书(example.com) ← 网站证书
↑ 由谁签发
中间 CA 证书(R10 / Let's Encrypt) ← 通常已包含在 fullchain
↑ 由谁签发
根 CA 证书(ISRG Root X1) ← 客户端内置信任
1.2 Nginx 证书配置
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
}
| 文件 | 内容 | 说明 |
|---|---|---|
fullchain.pem | 证书 + 中间 CA | Nginx 建议用这个 |
cert.pem | 仅终端证书 | 缺少中间链会握手失败 |
privkey.pem | 私钥 | 权限需 600,勿外泄 |
chain.pem | 仅中间 CA | 可拼进 fullchain |
避坑:证书链不完整是安卓端握手失败的常见原因。
fullchain.pem必须包含中间 CA;可随时用openssl s_client -connect验证链是否完整。
2. 协议版本与加密套件
2.1 协议版本选择
server {
ssl_protocols TLSv1.2 TLSv1.3; # 关闭 TLSv1/1.1 与 SSLv3
ssl_prefer_server_ciphers on;
}
| 协议 | 状态 | 说明 |
|---|---|---|
| SSLv3 / TLSv1.0 | 禁用 | POODLE/BEAST 漏洞 |
| TLSv1.1 | 禁用 | 过于老旧 |
| TLSv1.2 | 启用 | 兼容性主力 |
| TLSv1.3 | 启用 | 更安全更快,握手缩短 |
2.2 加密套件白名单
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305';
选择原则:
| 维度 | 要求 |
|---|---|
| 密钥交换 | 仅 ECDHE(前向保密) |
| 分组模式 | AES-GCM / ChaCha20-Poly1305 |
| 对称密钥 | AES-128/256 |
| 禁止项 | RC4、3DES、CBC 系弱套件 |
核心认知:TLSv1.3 的套件由协议内建,上述
ssl_ciphers主要约束 TLSv1.2 握手。坚持 ECDHE 与 GCM,可同时满足前向保密与性能。
3. HTTP2 与 ALPN
3.1 开启 HTTP2
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate fullchain.pem;
ssl_certificate_key privkey.pem;
}
新版 Nginx 用独立指令:
server {
listen 443 ssl;
http2 on; # Nginx 1.25.1+ 推荐写法
}
3.2 ALPN 协商
ALPN 让客户端与服务器在 TLS 握手中协商应用协议:
# 默认即可,Nginx 会向客户端通告 h2 与 http/1.1
ssl_alpn_protocols h2 http/1.1;
| 协商结果 | 含义 |
|---|---|
| h2 | 客户端与服务器都支持 HTTP/2 |
| http/1.1 | 退化到 HTTP/1.1 |
| none | 老客户端,仍可工作 |
3.3 HTTP2 的收益
| 能力 | HTTP/1.1 | HTTP/2 |
|---|---|---|
| 连接复用 | 队头阻塞,6 连接上限 | 单连接多路复用 |
| 请求头 | 重复发送 | HPACK 压缩 |
| 优先级 | 无 | 流优先级与依赖 |
| 服务端推送 | 无 | 可主动推送资源(谨慎使用) |
避坑:HTTP/2 要求所有请求头小写、且不再支持部分 HTTP/1.1 能力。若同时暴露明文 80 端口,不要在其上启用 http2(h2c 默认关闭)。
4. OCSP Stapling
4.1 为什么需要 OCSP
客户端校验证书是否被吊销,默认会向 OCSP 服务器发起查询,这会拖慢握手甚至单点故障。OCSP Stapling 由服务器定期抓取响应并"钉"在握手里发给客户端。
server {
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
resolver 1.1.1.1 8.8.8.8 valid=300s;
resolver_timeout 5s;
}
4.2 验证 Stapling 是否生效
openssl s_client -connect example.com:443 -status -servername example.com 2>&1 \
| grep -A3 "OCSP response"
# 期望看到: OCSP Response Status: successful (0x0)
| 配置项 | 作用 |
|---|---|
ssl_stapling on | 开启钉住响应 |
ssl_stapling_verify | 校验 OCSP 响应签名 |
ssl_trusted_certificate | 提供中间链供校验 |
resolver | OCSP 域名解析 |
避坑:
ssl_trusted_certificate指向 chain.pem 而非 fullchain.pem;缺少 resolver 时 Stapling 会静默失败,务必用 s_client 实测确认。
5. 会话复用与性能
5.1 会话缓存与票据
server {
ssl_session_cache shared:SSL:10m; # 共享缓存 10MB,约可存 4 万会话
ssl_session_timeout 1d;
ssl_session_tickets off; # 关闭票据,用服务端缓存更可控
}
| 机制 | 优点 | 缺点 |
|---|---|---|
| 会话缓存(shared) | 同 worker 共享,防重协商 | 多实例需共享存储 |
| 会话票据(ticket) | 无状态,适合多实例 | 私钥泄露影响面大 |
| 无复用 | 每连接全握手 | 性能最差 |
5.2 握手性能优化清单
ssl_session_cache shared:SSL:20m;
ssl_session_timeout 4h;
ssl_session_tickets off;
ssl_early_data on; # 0-RTT,仅 TLSv1.3,注意重放攻击风险
核心认知:会话复用把 TLS 握手中的"完整握手"降级为"缩短握手",是 HTTPS 性能的关键杠杆。开启
ssl_early_data(0-RTT)前必须评估重放攻击对业务的影响。
5.3 完整 HTTPS server 模板
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate fullchain.pem;
ssl_certificate_key privkey.pem;
ssl_trusted_certificate chain.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384';
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 valid=300s;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
}
这份模板把证书链、协议、套件、会话复用、Stapling 与 HSTS 一次性配齐,可直接作为生产基线。
6. HSTS 与重定向
6.1 全站 HTTPS 重定向
server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri;
}
6.2 HSTS 强制 HTTPS
server {
listen 443 ssl;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
}
| 参数 | 含义 |
|---|---|
max-age | 浏览器信任 HTTPS 的时长(秒) |
includeSubDomains | 子域名一并强制 |
preload | 申请进入浏览器预加载列表 |
always | 对 4xx/5xx 也输出该头 |
避坑:HSTS 一旦下发,浏览器会拒绝访问该域名的明文 HTTP。务必在全部子域名都能提供 HTTPS 后再开启
includeSubDomains,否则子域会被"锁死"。
7. 证书管理与自动化
7.1 证书自动轮换
# certbot 续期并触发 Nginx 重载
certbot renew --quiet --deploy-hook "nginx -s reload"
7.2 验证与诊断命令
# 查看证书详情与有效期
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
| openssl x509 -noout -dates -subject -issuer
# 查看支持的协议与套件
openssl s_client -connect example.com:443 -tls1_3 -servername example.com
7.3 常见故障排查
| 现象 | 原因 | 修复 |
|---|---|---|
| 安卓握手失败 | 证书链缺中间 CA | 使用 fullchain.pem |
| 扫描评分低 | 协议或套件过旧 | 收紧 ssl_protocols/ciphers |
| 握手缓慢 | 未开 Stapling/会话复用 | 开启 OCSP Stapling + session cache |
| 证书过期 | 未配置自动续期 | certbot renew + reload hook |
| 子域强制失败 | HSTS 覆盖范围过大 | 先收敛子域再开 includeSubDomains |
8. 总结
| 环节 | 要点 |
|---|---|
| 证书链 | fullchain.pem 含中间 CA,私钥权限 600 |
| 协议 | TLSv1.2 + TLSv1.3,禁用旧版 |
| 套件 | ECDHE + AES-GCM/ChaCha20,前向保密 |
| HTTP/2 | http2 on,ALPN 协商 h2 |
| OCSP | ssl_stapling on + verify + resolver |
| 会话复用 | shared 缓存 + 票据权衡 |
| HSTS | max-age + includeSubDomains 谨慎开启 |
| 运维 | certbot 自动续期 + s_client 定期验证 |
HTTPS 加固完成后,流量已加密可信,但性能还有很大优化空间。下一篇将聚焦缓存与压缩,用 proxy_cache 与 gzip 把响应延迟进一步降下来。
延伸阅读
- Nginx 核心配置结构 — server 块与指令上下文基础
- Nginx 反向代理与负载均衡 — 443 端口的代理转发
- Nginx 缓存与压缩优化 — HTTPS 下的缓存策略
- Nginx 限流与安全防护 — 安全响应头与访问控制
- Nginx 日志分析与性能调优 — TLS 握手性能观测
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。