实战:一次“诡异” 的 Nginx 异常排查,真相竟不是证书问题?
字数 2886 2025-09-01 11:25:53

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 是密钥交换阶段的可选步骤,在以下场景存在:
    1. 采用 RSA 加密但服务端证书未提供 RSA 公钥
    2. 采用 DH(EC Diffie-Hellman)加密但服务端证书未提供 DH 参数
    3. 采用 fortezza_kea 加密但服务端证书未提供参数

Nginx 常见错误日志解析

  1. "upstream prematurely closed connection"

    • 原因:upstream 未返回应答时用户断开连接
    • 影响:可忽略
  2. "recv() failed (104: Connection reset by peer)"

    • 可能原因:
      • 服务器并发连接数超限
      • 客户端关闭浏览器而服务器仍在发送数据
      • 浏览器端按了 Stop
  3. "(111: Connection refused) while connecting to upstream"

    • 原因:后端 upstream 挂掉或不通
  4. "(111: Connection refused) while reading response header from upstream"

    • 原因:连接成功后读取数据时 upstream 挂掉或不通
  5. "(111: Connection refused) while sending request to upstream"

    • 原因:连接成功后发送数据时 upstream 挂掉或不通
  6. "(110: Connection timed out) while connecting to upstream"

    • 原因:连接 upstream 超时
  7. "(110: Connection timed out) while reading upstream"

    • 原因:读取 upstream 响应超时
  8. "(110: Connection timed out) while reading response header from upstream"

    • 原因:读取 upstream 响应头超时
  9. "(104: Connection reset by peer) while connecting to upstream"

    • 原因:upstream 发送了 RST 重置连接
  10. "upstream sent invalid header while reading response header from upstream"

    • 原因:upstream 发送的响应头无效
  11. "upstream sent no valid HTTP/1.0 header while reading response header from upstream"

    • 原因:upstream 发送的响应头无效
  12. "client intended to send too large body"

    • 原因:客户端请求体超过设置值(默认 1M)
  13. "reopening logs"

    • 原因:用户发送 kill -USR1 命令
  14. "gracefully shutting down"

    • 原因:用户发送 kill -WINCH 命令
  15. "no servers are inside upstream"

    • 原因:upstream 下未配置 server
  16. "no live upstreams while connecting to upstream"

    • 原因:upstream 下的 server 全都挂了
  17. "SSL_do_handshake() failed"

    • 原因:SSL 握手失败
  18. "ngx_slab_alloc() failed: no memory in SSL session shared cache"

    • 原因:ssl_session_cache 大小不够等
  19. "could not add new SSL session to the session cache while SSL handshaking"

    • 原因:ssl_session_cache 大小不够等

实用技巧

  1. 抓包分析

    • 使用 tcpdump 抓包
    • 使用 Wireshark 分析
  2. 证书选择

    • ECC 证书比 RSA 证书更小,加解密更快,推荐使用
  3. Redhat/CentOS 系统注意

    • curl 默认使用 NSS 库,默认禁用 ECC 加密
    • 需要时可通过 --ciphers 参数指定加密套件

参考链接

Nginx HTTPS 异常排查实战教学文档 问题现象描述 网站上线后添加了 HTTPS 证书: 浏览器访问正常 通过 curl 请求时,连接被重置 (reset) 初步排查步骤 1. 基础网络连通性检查 使用 curl 请求同域名下的 HTTP URL → 返回正常,说明 80 端口网络正常 使用 curl 请求同服务器下其他 HTTPS 域名 → 返回正常,说明 443 端口网络正常 2. 证书检查 检查证书有效期 → 未到期 通过 myssl.com 查询证书详情 → 无问题 3. 加密套件检查 尝试添加兼容性更高的加密套件配置: → 问题依旧 4. 抓包分析 使用 tcpdump 抓包,Wireshark 分析: 从发起请求到 reset 共 16 个包 两端握手完成,发起数据传输后,第一个确认包被 reset 5. Nginx 缓冲区配置调整 修改 Nginx 相关 client 配置: → 无效果 6. 证书类型更换 从 RSA 证书更换为 ECC 证书: 出现新错误: curl: (35) Cannot communicate securely with peer: no common encryption algorithm(s) 原因:Redhat/CentOS 上 curl 默认使用 NSS 库,默认禁用 ECC 加密 解决方案:指定加密套件请求 → 回到原点,问题依旧 问题解决 关键发现 对比其他网站 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 参数指定加密套件 参考链接 Winreshark抓包理解HTTPS请求流程