HTTPS配置与HTTP2优化
1. 概念与原理
Nginx 上启用 HTTPS 三件事:拿证书、配 TLS、强制跳转。HTTP/2 / HTTP/3 在此之上加 http2 / quic 监听。这一篇给一套生产 A+ 配置 + 关键调优点。
2. 申请证书:Let's Encrypt + certbot
# Ubuntu
sudo apt install certbot python3-certbot-nginx
# 一键申请并修改 Nginx 配置
sudo certbot --nginx -d example.com -d www.example.com
# 仅签发,自己改配置
sudo certbot certonly --webroot -w /var/www/html -d example.com
# DNS-01 验证(适合无 80 端口访问、通配符)
sudo certbot certonly --manual --preferred-challenges dns -d "*.example.com"
证书路径:/etc/letsencrypt/live/example.com/
fullchain.pem← Nginx 用这个(含中间证书)privkey.pem
自动续期已写进 systemd timer / cron:
sudo systemctl list-timers | grep certbot
sudo certbot renew --dry-run
3. 完整生产配置(A+ 评分)
# /etc/nginx/snippets/ssl-params.conf
ssl_protocols TLSv1.2 TLSv1.3;
# TLS 1.2 cipher(1.3 是固定的)
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;
ssl_prefer_server_ciphers off;
# Session 复用
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 8.8.8.8 valid=300s;
resolver_timeout 5s;
# DH 参数(如果用 DHE,现代配置可不要)
# ssl_dhparam /etc/nginx/dhparam.pem;
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
include snippets/ssl-params.conf;
# HSTS(确认所有子域都 HTTPS 后再加)
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
# 其他安全头
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# ... 业务 location
}
# HTTP → HTTPS 永久重定向
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
# ACME challenge 必须走 HTTP
location /.well-known/acme-challenge/ {
root /var/www/html;
}
location / {
return 301 https://$host$request_uri;
}
}
4. HTTP/2
启用就是加 http2 关键字:
listen 443 ssl http2;
新版 Nginx(1.25.1+)改用独立指令:
listen 443 ssl;
http2 on;
4.1 HTTP/2 调优
http2_max_concurrent_streams 128; # 默认 128 一般够用
http2_recv_timeout 30s;
http2_idle_timeout 3m;
# 大请求 body
client_max_body_size 100m;
client_body_buffer_size 1m;
4.2 关闭 Server Push(已 deprecated)
新版 Nginx 已移除 http2_push。如果你看老资料里有 http2_push,不要用。Chrome 已不支持。
4.3 HTTP/2 带来的副作用
- 不再需要域名分片:HTTP/2 一连接多路复用,分片反而慢
- 不再需要 sprite:小请求成本低
- 但
gzip仍然需要(HPACK 只压头)
5. HTTP/3 / QUIC
Nginx 1.25+ 内置支持:
server {
listen 443 ssl;
listen 443 quic reuseport; # UDP 443
http2 on;
http3 on;
# 通告 HTTP/3 可用
add_header Alt-Svc 'h3=":443"; ma=86400';
ssl_certificate ...;
ssl_certificate_key ...;
}
部署前提:
- 防火墙 / 安全组放行 UDP 443
- 客户端首次仍走 HTTP/2,看到
Alt-Svc后续走 HTTP/3 - 可同时支持 HTTP/2、HTTP/3
测试:
curl --http3 -v https://example.com
# Chrome DevTools → Network → Protocol 列看 h3
6. HSTS Preload
让浏览器源码内置永远 HTTPS:
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
申请前置条件:
- 主域有效 HTTPS
- HTTP 必须 301(不能 302)跳 HTTPS
- 包含
includeSubDomains+preload max-age >= 31536000(1 年)- 所有子域都支持 HTTPS(一旦 preload 就锁死)
慎重:撤销 preload 极慢(数月),且部分老版本浏览器永不撤销。新公司或多业务环境前要确保所有子域永久 HTTPS。
7. 多域名 / SAN / 通配符
7.1 一个证书多个域名
申请时多个 -d:
certbot --nginx -d example.com -d www.example.com -d api.example.com
证书 SAN 字段含三个域名,Nginx 一个证书全用。
7.2 通配符证书
certbot certonly --manual --preferred-challenges dns \
-d "*.example.com" -d "example.com"
需要 DNS-01 验证(在 DNS 加 TXT 记录)。常用 DNS 厂商插件自动化:
sudo apt install python3-certbot-dns-cloudflare
sudo certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials /root/.secrets/cloudflare.ini \
-d "*.example.com"
7.3 SNI 路由
server {
listen 443 ssl http2;
server_name a.example.com;
ssl_certificate /etc/.../a-fullchain.pem;
# ...
}
server {
listen 443 ssl http2;
server_name b.example.com;
ssl_certificate /etc/.../b-fullchain.pem;
# ...
}
Nginx 根据 ClientHello 的 SNI 选 server。第一个 server 是默认(无 SNI 或不匹配时)。
8. mTLS(双向认证)
server {
listen 443 ssl http2;
ssl_client_certificate /etc/nginx/client-ca.crt; # 信任的客户端 CA
ssl_verify_client on; # on / optional / off
ssl_verify_depth 2;
location / {
# 把客户端证书信息传给后端
proxy_set_header X-SSL-Client-DN $ssl_client_s_dn;
proxy_set_header X-SSL-Client-Verify $ssl_client_verify;
proxy_set_header X-SSL-Client-Serial $ssl_client_serial;
proxy_pass http://backend;
}
}
企业内网 API、IoT 场景常用。
9. 调试与验证
9.1 SSL Labs 测试
最权威:ssllabs.com/ssltest。目标 A+ 评分。
9.2 命令行验证
# 看证书链
openssl s_client -connect example.com:443 -servername example.com -showcerts < /dev/null
# 看是否启用 OCSP Stapling
openssl s_client -connect example.com:443 -servername example.com -status < /dev/null | grep -A 17 "OCSP response:"
# 测特定 TLS 版本
openssl s_client -connect example.com:443 -tls1_3
openssl s_client -connect example.com:443 -tls1_2
# 测 cipher
nmap --script ssl-enum-ciphers -p 443 example.com
9.3 性能测试
# TLS 握手时间
curl -w "TLS: %{time_appconnect}s\n首字节: %{time_starttransfer}s\n" \
-o /dev/null -s https://example.com
10. 故障排查
10.1 证书错误
NET::ERR_CERT_AUTHORITY_INVALID # 证书链不全(用 fullchain.pem 而非 cert.pem)
NET::ERR_CERT_DATE_INVALID # 过期 / 客户端时间错
NET::ERR_CERT_COMMON_NAME_INVALID # 域名不匹配(看 SAN)
ERR_SSL_PROTOCOL_ERROR # TLS 版本 / cipher 不兼容
10.2 证书续期失败
sudo certbot renew --dry-run
# 看错误:80 端口被占用、DNS 验证失败、配额超限
# Let's Encrypt 速率限制:每域 50 次/周
# 调试用 staging 环境
sudo certbot --staging --nginx -d example.com
10.3 OCSP Stapling 不工作
# 看 nginx error.log
# OCSP_basic_verify failed → 证书链不对,检查 ssl_trusted_certificate
# resolver 没配
ssl_stapling on;
resolver 1.1.1.1 valid=300s; # 必加
10.4 HTTP/2 没启用
curl -I --http2 https://example.com 2>&1 | grep "HTTP/"
# 看是 HTTP/2 还是 HTTP/1.1
# 必须 SSL + HTTP/2 一起开
listen 443 ssl http2;
10.5 HSTS 配错锁死
把 max-age=31536000 配上 includeSubDomains,但某子域没 HTTPS:所有用户访问该子域永久失败。
修复:暂时把 max-age 改成 0(让浏览器忘记)。但已经 preload 的没救,只能等几个月。
11. 常见反模式
cert.pem而非fullchain.pem:iOS / Android 部分客户端报错ssl_protocols TLSv1 TLSv1.1:不合规、不安全ssl_ciphers HIGH:!aNULL:!MD5这种老配置:包含弱算法- HSTS 直接
max-age=63072000+includeSubDomains:未确认所有子域 HTTPS 就锁死 - HTTP 配 302 到 HTTPS:HSTS preload 不通过(必须 301)
- HTTP/2 还做域名分片:每域名一次握手,反而慢
ssl_session_tickets on+ 不轮换 ticket key:破坏前向安全- OCSP Stapling 没配 resolver:报错日志刷屏,stapling 实际没工作
- 私钥 chmod 644:同机器其他用户能读
- 不监控证书过期:90 天 Let's Encrypt 续期失败 = 全站挂