Nginx HTTPS 异常排查实战教学文档
问题现象描述
网站上线后添加了 HTTPS 证书:
- 浏览器访问正常
- 通过 curl 请求时,连接被重置 (reset)
初步排查步骤
1. 基础网络连通性检查
- 使用 curl 请求同域名下的 HTTP URL → 返回正常,说明 80 端口网络正常
- 使用 curl 请求同服务器下其他 HTTPS 域名 → 返回正常,说明 443 端口网络正常
2. 证书检查
- 检查证书有效期 → 未到期
- 通过 myssl.com 查询证书详情 → 无问题
3. 加密套件检查
尝试添加兼容性更高的加密套件配置:
ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4
→ 问题依旧
4. 抓包分析
使用 tcpdump 抓包,Wireshark 分析:
- 从发起请求到 reset 共 16 个包
- 两端握手完成,发起数据传输后,第一个确认包被 reset
5. Nginx 缓冲区配置调整
修改 Nginx 相关 client 配置:
client_header_buffer_size 64k;
large_client_header_buffers 4 64k;
client_body_buffer_size 20m;
keepalive_timeout 120;
→ 无效果
6. 证书类型更换
从 RSA 证书更换为 ECC 证书:
- 出现新错误:
curl: (35) Cannot communicate securely with peer: no common encryption algorithm(s) - 原因:Redhat/CentOS 上 curl 默认使用 NSS 库,默认禁用 ECC 加密
- 解决方案:指定加密套件请求
curl --ciphers ecdhe_rsa_aes_128_gcm_sha_256 ...
→ 回到原点,问题依旧
问题解决
关键发现
对比其他网站 Nginx 配置,发现缺少 ssl_session_cache 配置
添加 ssl_session_cache 配置后问题解决
ssl_session_cache 参数详解
| 参数 | 说明 |
|---|---|
| off | 严禁使用 session 缓存:nginx 明确告诉客户端 session 可能不会被重用 |
| none | session 缓存的使用被禁止:nginx 告诉客户端 session 可能会被重用,但实际上不会存储 |
| builtin | 在 OpenSSL 中构建的缓存;仅由一个工作进程使用;默认大小 20480 会话 |
| shared | 所有工作进程共享缓存;以字节为单位指定大小(1MB ≈ 4000 会话) |
建议:使用 shared 参数,性能更高
技术原理分析
通过抓包对比发现:
- 数据传输前,reset 的连接过程中多了 Server Key Exchange
- Server Key Exchange 是密钥交换阶段的可选步骤,在以下场景存在:
- 采用 RSA 加密但服务端证书未提供 RSA 公钥
- 采用 DH(EC Diffie-Hellman)加密但服务端证书未提供 DH 参数
- 采用 fortezza_kea 加密但服务端证书未提供参数
Nginx 常见错误日志解析
-
"upstream prematurely closed connection"
- 原因:upstream 未返回应答时用户断开连接
- 影响:可忽略
-
"recv() failed (104: Connection reset by peer)"
- 可能原因:
- 服务器并发连接数超限
- 客户端关闭浏览器而服务器仍在发送数据
- 浏览器端按了 Stop
- 可能原因:
-
"(111: Connection refused) while connecting to upstream"
- 原因:后端 upstream 挂掉或不通
-
"(111: Connection refused) while reading response header from upstream"
- 原因:连接成功后读取数据时 upstream 挂掉或不通
-
"(111: Connection refused) while sending request to upstream"
- 原因:连接成功后发送数据时 upstream 挂掉或不通
-
"(110: Connection timed out) while connecting to upstream"
- 原因:连接 upstream 超时
-
"(110: Connection timed out) while reading upstream"
- 原因:读取 upstream 响应超时
-
"(110: Connection timed out) while reading response header from upstream"
- 原因:读取 upstream 响应头超时
-
"(104: Connection reset by peer) while connecting to upstream"
- 原因:upstream 发送了 RST 重置连接
-
"upstream sent invalid header while reading response header from upstream"
- 原因:upstream 发送的响应头无效
-
"upstream sent no valid HTTP/1.0 header while reading response header from upstream"
- 原因:upstream 发送的响应头无效
-
"client intended to send too large body"
- 原因:客户端请求体超过设置值(默认 1M)
-
"reopening logs"
- 原因:用户发送
kill -USR1命令
- 原因:用户发送
-
"gracefully shutting down"
- 原因:用户发送
kill -WINCH命令
- 原因:用户发送
-
"no servers are inside upstream"
- 原因:upstream 下未配置 server
-
"no live upstreams while connecting to upstream"
- 原因:upstream 下的 server 全都挂了
-
"SSL_do_handshake() failed"
- 原因:SSL 握手失败
-
"ngx_slab_alloc() failed: no memory in SSL session shared cache"
- 原因:ssl_session_cache 大小不够等
-
"could not add new SSL session to the session cache while SSL handshaking"
- 原因:ssl_session_cache 大小不够等
实用技巧
-
抓包分析
- 使用 tcpdump 抓包
- 使用 Wireshark 分析
-
证书选择
- ECC 证书比 RSA 证书更小,加解密更快,推荐使用
-
Redhat/CentOS 系统注意
- curl 默认使用 NSS 库,默认禁用 ECC 加密
- 需要时可通过
--ciphers参数指定加密套件