Nginx 从入门到精通 / 07 - HTTPS 与 TLS / HTTPS & TLS
HTTPS 与 TLS / HTTPS & TLS
🟢 基础 / Basics — 快速启用 HTTPS
Let’s Encrypt + Certbot(免费证书)
# 1. 安装 Certbot
sudo apt install -y certbot python3-certbot-nginx
# 2. 自动获取证书并配置 Nginx
sudo certbot --nginx -d example.com -d www.example.com
# 3. 验证自动续期
sudo certbot renew --dry-run
# 4. 设置自动续期 cron
echo "0 3 * * * root certbot renew --quiet --post-hook 'systemctl reload nginx'" \
| sudo tee /etc/cron.d/certbot-renew
执行后 Certbot 会自动修改 Nginx 配置,生成类似:
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri; # HTTP 强制跳转 HTTPS
}
server {
listen 443 ssl;
server_name example.com www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}
手动配置 SSL
server {
listen 443 ssl http2;
server_name example.com;
# 证书文件
ssl_certificate /etc/nginx/ssl/example.com.crt; # 证书链(含中间证书)
ssl_certificate_key /etc/nginx/ssl/example.com.key; # 私钥
# SSL 会话缓存
ssl_session_cache shared:SSL:10m; # 10MB 共享缓存,约 40,000 个会话
ssl_session_timeout 1d; # 会话有效期 1 天
ssl_session_tickets off; # 禁用 session tickets(更安全)
location / {
proxy_pass http://127.0.0.1:3000;
}
}
HTTP 强制跳转 HTTPS
# 方式 1:独立 server 块(推荐)
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
return 301 https://example.com$request_uri;
}
# 方式 2:在同一个 server 块中用 if
server {
listen 80;
listen 443 ssl;
server_name example.com;
if ($scheme = http) {
return 301 https://$host$request_uri;
}
# ⚠️ 不推荐,if 在 server 块中有坑
}
🟡 进阶 / Intermediate — TLS 安全配置
现代 TLS 配置(Mozilla 推荐)
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
# 协议版本:只允许 TLS 1.2 和 1.3
ssl_protocols TLSv1.2 TLSv1.3;
# 密码套件(TLS 1.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:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
# 优先使用服务端密码套件
ssl_prefer_server_ciphers off; # TLS 1.3 建议 off
# DH 参数(TLS 1.2 需要)
ssl_dhparam /etc/nginx/ssl/dhparam.pem;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/nginx/ssl/chain.pem;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
}
# 生成 DH 参数(耗时较长,建议 2048 位)
sudo openssl dhparam -out /etc/nginx/ssl/dhparam.pem 2048
HSTS(HTTP 严格传输安全)
server {
listen 443 ssl;
server_name example.com;
# 告诉浏览器:未来 1 年内只用 HTTPS 访问
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# ⚠️ 注意:
# 1. 一旦启用 preload 并提交到浏览器预加载列表,将无法轻易撤销
# 2. 子域名也会被强制 HTTPS
# 3. 先用小的 max-age 测试,确认无误后再加大
}
HSTS 工作流程:
首次访问(无 HSTS 缓存):
Client → http://example.com → 301 → https://example.com ✓
后续访问(有 HSTS 缓存):
Client → 直接访问 https://example.com(浏览器自动跳转,不发 HTTP 请求)
↑ 完全消除了首次 HTTP→HTTPS 跳转中的中间人攻击窗口
多域名 / 通配符证书
# 通配符证书(*.example.com)
server {
listen 443 ssl;
server_name example.com *.example.com;
ssl_certificate /etc/nginx/ssl/wildcard.example.com.crt;
ssl_certificate_key /etc/nginx/ssl/wildcard.example.com.key;
}
# SAN 证书(多域名共用一个证书)
server {
listen 443 ssl;
server_name example.com www.example.com api.example.com;
ssl_certificate /etc/nginx/ssl/multi-domain.crt;
ssl_certificate_key /etc/nginx/ssl/multi-domain.key;
}
# SNI:不同域名使用不同证书
server {
listen 443 ssl;
server_name site-a.com;
ssl_certificate /etc/nginx/ssl/site-a.crt;
ssl_certificate_key /etc/nginx/ssl/site-a.key;
}
server {
listen 443 ssl;
server_name site-b.com;
ssl_certificate /etc/nginx/ssl/site-b.crt;
ssl_certificate_key /etc/nginx/ssl/site-b.key;
}
SNI (Server Name Indication) 原理:
Client Nginx
│ │
│── ClientHello ─────────────────► │
│ SNI: example.com │ ← 客户端在握手阶段就告诉服务器域名
│ │
│◄── ServerHello ──────────────────│ ← Nginx 选择对应的证书
│ Certificate: *.example.com │
│ │
│ TLS 握手完成,开始 HTTP 通信 │
🔴 高级 / Advanced — TLS 高级主题
双向 TLS(mTLS / 客户端证书认证)
server {
listen 443 ssl;
server_name api.example.com;
# 服务端证书
ssl_certificate /etc/nginx/ssl/server.crt;
ssl_certificate_key /etc/nginx/ssl/server.key;
# 客户端证书验证
ssl_client_certificate /etc/nginx/ssl/ca.crt; # CA 证书
ssl_verify_client on; # 要求客户端证书
ssl_verify_depth 2;
location /api/ {
# 将客户端证书信息传给后端
proxy_set_header X-Client-Cert-DN $ssl_client_s_dn;
proxy_set_header X-Client-Cert-Serial $ssl_client_serial;
proxy_set_header X-Client-Cert-Verify $ssl_client_verify;
proxy_pass http://backend;
}
# 可选:某些路径不要求客户端证书
location /health {
ssl_verify_client optional; # 可选认证
return 200 "OK";
}
}
mTLS 流程:
Client Nginx
│ │
│── ClientHello ─────────────────► │
│◄── ServerHello + ServerCert ─────│ ← 标准 TLS
│ │
│◄── CertificateRequest ──────────│ ← 要求客户端证书
│── ClientCert + ClientVerify ────►│ ← 客户端发送证书
│ │
│ 双方验证证书,建立加密通道 │
TLS 1.3 0-RTT(Early Data)
server {
listen 443 ssl;
server_name example.com;
ssl_protocols TLSv1.3;
# 启用 0-RTT
ssl_early_data on;
# 将 Early-Data 头传给后端(防止重放攻击)
proxy_set_header Early-Data $ssl_early_data;
}
传统 TLS 1.2(2-RTT):
Client → ServerHello → 1 RTT
Client → ApplicationData → 2 RTT
TLS 1.3(1-RTT):
Client → ServerHello → 1 RTT
Client → ApplicationData
TLS 1.3 0-RTT(重连时):
Client → ApplicationData → 0 RTT(首次数据和握手同时发送)
↑ 但有重放攻击风险,只能用于幂等请求
OCSP Stapling 详解
server {
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/nginx/ssl/chain.pem;
# OCSP 响应缓存
resolver 1.1.1.1 8.8.8.8 valid=300s;
resolver_timeout 5s;
}
无 OCSP Stapling:
Client → OCSP Responder(证书颁发机构)
1. 额外的网络请求
2. 暴露用户浏览记录给 CA
3. 增加延迟
有 OCSP Stapling:
Nginx → OCSP Responder(定期查询,缓存响应)
Client → Nginx(直接返回缓存的 OCSP 响应)
1. 无额外请求
2. 用户隐私不受影响
3. 更快的握手
SSL 性能优化
server {
# 1. 会话缓存(减少完整握手)
ssl_session_cache shared:SSL:50m; # 50MB,约 200,000 个会话
ssl_session_timeout 4h;
ssl_session_tickets on;
ssl_session_ticket_key /etc/nginx/ssl/ticket.key; # 轮换 key
# 2. 硬件加速(如果有 AES-NI)
# Nginx 自动使用,无需额外配置
# 验证: openssl engine -t
# 3. 早发送(TCP_CORK / TCP_NODELAY)
ssl_buffer_size 4k; # 默认 16k,小值可减少 TTFB
}
# 验证 SSL 配置
# 在线工具
# https://www.ssllabs.com/ssltest/
# 命令行测试
openssl s_client -connect example.com:443 -tls1_2
openssl s_client -connect example.com:443 -tls1_3
# 查看证书信息
echo | openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -text -noout
自签名证书(开发环境)
# 生成自签名证书
openssl req -x509 -nodes -days 365 \
-newkey rsa:2048 \
-keyout /etc/nginx/ssl/selfsigned.key \
-out /etc/nginx/ssl/selfsigned.crt \
-subj "/CN=localhost"
# 或使用 mkcert(更方便,自动信任)
# 安装: https://github.com/FiloSottile/mkcert
mkcert -install
mkcert localhost 127.0.0.1 ::1
# 生成 localhost+2.pem 和 localhost+2-key.pem
小结 / Summary
| 层级 | 你需要知道的 / What You Need to Know |
|---|---|
| 🟢 基础 | Let’s Encrypt + Certbot,HTTP → HTTPS 跳转 |
| 🟡 进阶 | TLS 1.2/1.3 配置,HSTS,OCSP Stapling,SNI 多证书 |
| 🔴 高级 | mTLS 双向认证,TLS 1.3 0-RTT,会话缓存优化,SSL 性能调优 |
下一章:缓存机制 / Caching