TLS全流程 + Nginx HTTPS配置实战 + 会话绑定 vs 复制的架构选型
一、HTTPS工作流程:从TLS握手到加密数据传输(含性能关键点)
1. 完整TLS 1.2/1.3握手流程(以Nginx为服务端)
▶ TLS 1.2(传统四次握手,阿里已逐步淘汰)
Client Server|-------- ClientHello -------->| (支持的Cipher Suites, TLS版本, 随机数C)|<------- ServerHello ---------| (选定Cipher, 随机数S)|<------- Certificate --------| (服务端证书链)|<--- ServerKeyExchange? ----| (ECDHE需此步)|<--- ServerHelloDone -------||---- ClientKeyExchange ---->| (用公钥加密预主密钥 或 ECDHE公钥)|---- ChangeCipherSpec ------>||---- Finished -------------->| (用协商密钥加密校验)|<--- ChangeCipherSpec ------||<--- Finished --------------||<====== 加密应用数据 =======>|
▶ TLS 1.3(阿里生产主力,1-RTT甚至0-RTT)
Client Server|---- ClientHello (含密钥共享) -->||<--- ServerHello + Certificate -| (合并多消息,1-RTT完成)|<--- EncryptedExtensions ------||<--- Finished -----------------||---- Finished ---------------->||<====== 加密应用数据 =======>|
优化点:
- 2021年双11:全站切TLS 1.3,首屏加载快200ms;
- 证书链优化:中间CA证书必须拼接完整,否则iOS客户端握手失败(2019年吃过亏);
- OCSP Stapling:避免客户端在线验证吊销(
ssl_stapling on;
),减少100ms延迟。
二、Nginx配置HTTPS:关键文件 + 核心配置项(阿里生产模板)
1. 必需文件
文件类型 | 说明 | 规范 |
---|---|---|
证书文件(.crt 或 .pem ) | 包含域名证书 + 中间CA证书(顺序:域名证书在上,根CA不用) | 用cat your_domain.crt intermediate.crt > fullchain.crt 拼接 |
私钥文件(.key ) | RSA或ECDSA私钥,必须600权限 | 禁止明文提交Git,用KMS加密托管 |
2. Nginx HTTPS核心配置(含安全加固)
server {listen 443 ssl http2; # 启用HTTP/2(QPS提升30%)server_name www.zjx521.com;# 证书与密钥ssl_certificate /etc/nginx/ssl/fullchain.crt;ssl_certificate_key /etc/nginx/ssl/privkey.key;# 协议与加密套件(安全红线)ssl_protocols TLSv1.2 TLSv1.3; # 禁用SSLv3/TLS1.0/1.1ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256; # 前向安全ssl_prefer_server_ciphers off; # TLS 1.3必须off# 性能优化ssl_session_cache shared:SSL:50m; # 共享内存缓存会话(50MB ≈ 20万会话)ssl_session_timeout 10m; # 会话复用有效期ssl_session_tickets on; # 启用Session Ticket(TLS 1.3默认)# 安全增强ssl_stapling on; # OCSP Staplingssl_stapling_verify on;resolver 100.100.2.136 valid=300s; # 内网DNS# HSTS(防SSL剥离)add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;location / {proxy_pass http://backend;}
}
教训:
- 2018年某业务漏配
ssl_session_cache
,TLS握手CPU打满,RT飙升3倍;- 私钥权限755导致
nginx -t
报错“unsafe key”,大促前夜手忙脚乱。
自动化建议:用Ansible + Vault管理证书,配合ACME自动续签(阿里内部用自研证书平台,开源可用certbot)。
3.let's encrypt免费证书申请
① 环境准备
# CentOS / Rocky / AlmaLinux
sudo yum install -y epel-release
sudo yum install -y certbot# Ubuntu / Debian
sudo apt update
sudo apt install -y certbottee /tmp/ssl.sh <<EOF
# 适用于任何 DNS 服务商(Cloudflare、华为云、Godaddy 等)
DOMAIN="zjx521.com"
sudo certbot certonly \--manual \--preferred-challenges dns \--server https://acme-v02.api.letsencrypt.org/directory \--agree-tos --no-eff-email --email admin@$DOMAIN \-d "*.$DOMAIN" -d "$DOMAIN"
EOF
② 执行过程
chmod +x /tmp/ssl.sh
cd /tmp
./ssl.sh
以下是输出内容 次案例走阿里云dns解析
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Requesting a certificate for *.zjx521.com and zjx521.com- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please deploy a DNS TXT record under the name:_acme-challenge.zjx521.com.with the following value:cFl5DxIR9-sH49xHckxJK6XvHIKFOKDXeyzG7BLw8dQ- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Press Enter to Continue#打开阿里云dns 设置txt _acme-challenge.zjx521.com. 值:cFl5DxIR9-sH49xHckxJK6XvHIKFOKDXeyzG7BLw8dQ
然后enter- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please deploy a DNS TXT record under the name:_acme-challenge.zjx521.com.with the following value:3jxOKHAnvNbIACbhObwN4_izeQ-uhBZeo2D8lDn2FrM(This must be set up in addition to the previous challenges; do not remove,
replace, or undo the previous challenge tasks yet. Note that you might be
asked to create multiple distinct TXT records with the same name. This is
permitted by DNS standards.)#打开阿里云dns 设置txt _acme-challenge.zjx521.com. 值:c3jxOKHAnvNbIACbhObwN4_izeQ-uhBZeo2D8lDn2FrM
然后enter
Before continuing, verify the TXT record has been deployed. Depending on the DNS
provider, this may take some time, from a few seconds to multiple minutes. You can
check if it has finished deploying with aid of online tools, such as the Google
Admin Toolbox: https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.zjx521.com.
Look for one or more bolded line(s) below the line ';ANSWER'. It should show the
value(s) you've just added.#然后 在enter
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Press Enter to ContinueSuccessfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/zjx521.com/fullchain.pem
Key is saved at: /etc/letsencrypt/live/zjx521.com/privkey.pem
This certificate expires on 2025-12-28.
These files will be updated when the certificate renews.NEXT STEPS:
- This certificate will not be renewed automatically. Autorenewal of --manual certificates requires the use of an authentication hook script (--manual-auth-hook) but one was not provided. To renew this certificate, repeat this same certbot command before the certificate's expiry date.- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
If you like Certbot, please consider supporting our work by:* Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate* Donating to EFF: https://eff.org/donate-le
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
此刻申请成功:
证书地址和有效时间如上
③ 证书路径
tree /etc/letsencrypt/live/zjx521.com/
/etc/letsencrypt/live/zjx521.com/
├── cert.pem -> ../../archive/zjx521.com/cert1.pem # 仅域名证书
├── chain.pem -> ../../archive/zjx521.com/chain1.pem # 仅中间CA
├── fullchain.pem -> ../../archive/zjx521.com/fullchain1.pem # 证书链(Nginx ssl_certificate 用这个)
├── privkey.pem -> ../../archive/zjx521.com/privkey1.pem 私钥(ssl_certificate_key 用这个)
└── README1 directory, 5 files权限安全:privkey.pem 默认为 600,属主 root,无需额外处理
④ 提醒续约脚本
# 创建提醒脚本
cat > /root/letsencrypt-reminder.sh <<'EOF'
#!/bin/bash
days=$(certbot certificates 2>/dev/null | grep -o 'VALID: [0-9]* days' | grep -o '[0-9]*' | head -1)
if [ -n "$days" ] && [ "$days" -lt 30 ]; thenecho "⚠️ 证书将在 $days 天后过期!"
fi
echo "命令:sudo certbot certonly --manual --preferred-challenges dns -d '*.zjx521.com' -d 'zjx521.com'"
EOF
chmod +x /root/letsencrypt-reminder.sh# 加入 crontab,每30天提醒一次
echo "0 9 1 */1 * /root/letsencrypt-reminder.sh | mail -s 'Let\'s Encrypt 证书即将过期' your@email.com" | sudo crontab -
⑤ 续约命令
certbot certonly --manual --preferred-challenges dns -d '*.zjx521.com' -d 'zjx521.com'
输入2确定
自动续约版本
待更新测试
核心思路:用 acme.sh
替代 certbot
三、会话绑定(Session Sticky) vs 会话复制(Session Replication)
1. 会话绑定(Sticky Session)
- 原理:负载均衡器(如Nginx/LVS)将同一用户的所有请求固定转发到同一后端实例。
- Nginx实现:
upstream backend {ip_hash; # 基于客户端IP哈希(简单但不精准)# 或用商业版sticky cookie(开源可用nginx-sticky-module)sticky cookie srv_id expires=1h domain=.tmall.com path=/; }
- 适用场景:
- 有状态服务:如传统Java Web应用(Tomcat Session未集群);
- 低频交互:用户操作间隔长,实例故障影响小;
- 阿里案例:2015年前老版购物车服务(后已改造为无状态)。
致命缺陷:实例故障时用户会话丢失,“高可用”铁律。
2. 会话复制(Session Replication)
- 原理:后端实例间实时同步会话数据(如Tomcat Cluster广播)。
- 典型架构:
Nginx → [App1 ←→ App2 ←→ App3] # 通过组播/Redis同步Session
- 适用场景:
- 强一致性要求:如支付确认页,不能丢状态;
- 小规模集群(≤10节点):广播风暴随节点数平方增长;
- 教训:2016年大促,Tomcat集群32节点,会话复制网络打满,全站雪崩。
现代解法:会话外置(终极方案)
- 用Redis集中存储Session(
spring-session-data-redis
);- 应用完全无状态,Nginx无需sticky;
- 实测:10万+实例弹性伸缩,会话零丢失。
四、架构选型决策树(阿里SRE标准)
场景 | 推荐方案 | 原因 |
---|---|---|
新业务/云原生 | 会话外置(Redis) | 无状态、弹性伸缩、符合云原生 |
老系统改造中 | 会话绑定(Sticky) | 快速上线,逐步改造 |
强一致性+小集群 | 会话复制 | 仅限遗留系统,不推荐新项目 |
双11级大促 | 必须无状态 + 会话外置 | 铁律:状态即负债 |
五、现在立刻行动建议
检查TLS配置:
openssl s_client -connect your_domain:443 -servername your_domain | grep "Cipher is"
确保是
ECDHE
开头(前向安全)。验证会话方案:
- 如果用
ip_hash
或sticky
,马上评估迁移到Redis; - 用
redis-cli monitor
看Session写入频率,避免大Key。
- 如果用
开启OCSP Stapling:
ssl_stapling on; ssl_stapling_verify on; resolver 8.8.8.8; # 临时测试用
能省100ms,用户体验直接提升。