必须开启ssl_prefer_server_ciphers on才能让nginx在tls 1.2握手时按ssl_ciphers从左到右优先选用强套件,但需配合禁用弱算法、限定tlsv1.2+协议及server块内完整配置才生效。

要让 Nginx 在 TLS 握手时由服务端决定用哪个加密套件,核心是开启 ssl_prefer_server_ciphers on;,但它不是“单独起作用”的开关,必须和明确、安全的 ssl_ciphers 配合使用,且需限定协议版本。
必须开启服务端主导权
ssl_prefer_server_ciphers on; 是启用服务端优先协商的关键指令。默认值为 off,此时客户端控制选套件顺序 —— 它发来的 ClientHello 里排第一的、Nginx 又支持的,就会被采用,哪怕是你配置里靠后的弱算法。
- 设为 on 后,Nginx 忽略客户端顺序,转而从自己
ssl_ciphers列表中,从左到右找第一个双方都支持的套件 - 这个“从左到右”就是你定义的安全等级排序,也是防降级攻击的基础
- 该指令对 TLS 1.2 握手生效;TLS 1.3 不受它影响(但依然要保留,因多数连接会同时协商多个版本)
配套配置不能少
只写 ssl_prefer_server_ciphers on; 没有效果。它需要三样东西同时到位:
-
强而明确的 ssl_ciphers 列表:禁用 RC4、DES、3DES、SHA1、CBC 模式等;只留 ECDHE + AES-GCM / ChaCha20 / TLSv1.3 原生套件,例如:
"ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256" -
严格限制协议版本:
ssl_protocols TLSv1.2 TLSv1.3;(不要写TLSv1,它等价于 TLS 1.0) -
放在正确的配置位置:必须在
server块内,与ssl_certificate、ssl_ciphers等同级;不能只在http块全局开,个别server却漏配套件
验证是否真正生效
重载配置后,不能只看语法正确就认为成功。必须实测确认旧协议被拒、强套件被选中:
- 用 OpenSSL 测试 TLS 1.2 协商:
openssl s_client -connect example.com:443 -tls1_2 | grep "Cipher is"
输出应显示你ssl_ciphers列表中最靠前的那个套件(如ECDHE-RSA-AES128-GCM-SHA256) - 用 SSL Labs 的 SSL Server Test 全面扫描,重点看 “Handshake Simulation” 中各客户端实际协商结果,以及 “Cipher Suites” 是否全为 AEAD 类型、支持前向保密(PFS)
- 检查 Nginx 错误日志,确保没有因证书类型不匹配(如 ECDSA 证书配了 RSA 套件)导致握手失败
常见误区提醒
这些做法看似省事,实则削弱安全性或导致失效:
- 用模糊表达如
HIGH:!aNULL:!MD5:无法排除非前向保密套件,也拦不住 SHA1 签名的弱组合 - 把
ssl_prefer_server_ciphers on;放在http块却不在server块里配ssl_ciphers:该站点将回退到默认弱套件 - 未同步更新
ssl_ecdh_curve:建议设为secp384r1:prime256v1,兼顾性能与兼容性 - 忽略 TLS 1.3 的特殊性:它自动使用内置套件,不读取
ssl_ciphers,所以你的强列表只需覆盖 TLS 1.2 场景即可











