跳到主要内容

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 就锁死)

提交:hstspreload.org

慎重:撤销 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 续期失败 = 全站挂

12. 延伸阅读