在 Chatwise 中调用 DeepSeek 模型时,reqwest
抛出的 AlertReceived(HandshakeFailure)
通常意味着 TLS 握手阶段被服务器中止。本文透视 DeepSeek API 的 TLS 要求、Rust 网络栈的实现细节以及常见的环境陷阱,借助可直接运行的代码与真实案例,给出一条可复制的排障路线。
Error reqwest::Error { kind: Request, url:
[https://api.deepseek.com/v1/chat/completions](https://api.deepseek.com/v1/chat/completions%60), source: hyper_util::client::legacy::Error(Connect, Custom { kind: Other, error: Custom { kind: InvalidData, error: AlertReceived(HandshakeFailure) } }) }
hyper_util::client::legacy::Error(Connect…)
表示 TCP 连接阶段尚未完成 TLS 握手。
AlertReceived(HandshakeFailure)
是服务端返回的 TLS 警报 0x28,意指双方未能在协议版本或密码套件上达成一致 (stackoverflow.com, thesslstore.com)。
DeepSeek 网关(由 Cloudflare 终结 TLS)声明同时支持 TLS 1.2 与 TLS 1.3,并优先启用 TLS_AES_256_GCM_SHA384
等现代套件 (milvus.io)。如果客户端仅携带过旧的 TLS 1.2 套件(例如 TLS_RSA_WITH_AES_128_CBC_SHA
),Cloudflare 会立即发送 HandshakeFailure
警报 (community.cloudflare.com, community.openai.com)。
reqwest
的默认 TLS 后端是 Rustls。旧版 Rustls (≤0.21)只在编译时内置证书库,若系统证书或根 CA 更新,新证书无法即时生效,从而在 SNI 或 OCSP 检查阶段失败 (github.com, github.com)。
自 hyper 1.0 起,官方将旧 API 迁入 hyper_util::client::legacy
命名空间,仅作为过渡层保留。若同时引用旧版 hyper
与新版 reqwest
,会强制回退到 legacy 适配器,间接拖低 TLS 功能集 (github.com)。
DeepSeek 采用与 OpenAI 兼容的 HTTP + JSON 协议;官方示例使用 https://api.deepseek.com/v1
作为根路径 (api-docs.deepseek.com, api-docs.deepseek.com)。在 2025 Q1,DeepSeek 升级为 TLS 1.3 默认,仅在必要时降级 TLS 1.2。若客户端启用了过时的 SSLv23
探测或显式禁用了 TLS 1.3,将触发握手失败 (sectigo.com, stackoverflow.com)。
配置键 | 正确示例 | 错误示例 | 影响 |
---|---|---|---|
api_base |
https://api.deepseek.com/v1 |
缺少 /v1 、或误写成 https://deepseek.com/api |
404 或握手失败 |
model |
deepseek-chat 等官方型号 |
deepseek (老前缀) |
返回 400 invalid model |
代理 | 无或 HTTPS 支持 | 仅 HTTP、劫持 SNI | 提前重置连接 |
社区用户在 Chatwise Issue #745 中复现了含 TLS 报错的完整栈追踪,根因即为老版本 Chatwise 强制禁用了 TLS 1.3 (github.com)。
curl
诊断curl -v --tlsv1.3 \
-H `Authorization: Bearer $DEEPSEEK_API_KEY` \
-H `Content-Type: application/json` \
-d '{
"model":"deepseek-chat",
"messages":[{"role":"user","content":"Hello"}],
"stream":false
}' \
https://api.deepseek.com/v1/chat/completions
若输出 Connected
→ handshake completed
,说明链路与证书皆正常;若 handshake failure
,请在同网络环境下访问 https://www.cloudflare.com
比对,以排除本地防火墙插拦 (community.cloudflare.com, [reddit.com](https://www.reddit.com/r/sysadmin/comments/1iqagau/help_with_failed_received_fatal_alert_handshake/?utm_source=chatgpt.com “Help with “failed received fatal alert: handshake failure” – SSL/TLS …”))。
// Cargo.toml
// reqwest = { version = "0.11.27", features = ["rustls-tls-native-roots"] }
// tokio = { version = "1.37", features = ["rt-multi-thread", "macros"] }
use reqwest::{Client, header::{HeaderMap, HeaderValue, AUTHORIZATION}};
use serde_json::json;
#[tokio::main]
async fn main() -> Result<(), Box> {
let mut hdr = HeaderMap::new();
hdr.insert(AUTHORIZATION,
HeaderValue::from_str(&format!("Bearer {}", std::env::var("DEEPSEEK_API_KEY")?))?);
hdr.insert("Content-Type", HeaderValue::from_static("application/json"));
let body = json!({
"model": "deepseek-chat",
"messages": [{"role":"user", "content":"ping"}],
"stream": false
});
let rsp = Client::builder()
.user_agent("deepseek-demo/0.1")
.use_rustls_tls()
.https_only(true)
.build()?
.post("https://api.deepseek.com/v1/chat/completions")
.headers(hdr)
.json(&body)
.send()
.await?;
println!("{}", rsp.text().await?);
Ok(())
}
关键点:
use_rustls_tls()
指定 Rustls,并自动加载系统根证书,避免旧版嵌入证书过期 (github.com)。
https_only(true)
拒绝任何意外降级到明文,防止中间人劫持。
若仍报错,可在 .danger_accept_invalid_certs(true)
打开后再次尝试,若成功则说明本机 CA 链缺失。
providers:
- id: deepseek
api_base: `https://api.deepseek.com/v1`
api_key: ${DEEPSEEK_API_KEY}
http_proxy: "" # 确保无透明 HTTP 代理
timeout_ms: 30000
insecure_skip_verify: false
确认 Chatwise 已升级至 ≥0.11.8(该版本开始内置 Rustls 0.23 并恢复 TLS 1.3 支持) (github.com)。
Windows 用户在系统 Internet Options ➜ Advanced
中勾选 Use TLS 1.3
后重启,可修复 SChannel 的默认降级策略 (sectigo.com)。
场景 | 触发点 | 诊断结论 | 解决手段 |
---|---|---|---|
Replit 环境 | 出站 443 仅允许 SNI 为 replit.com | DeepSeek SNI 被阻断导致握手失败 (reddit.com) | 申请 Replit https-egress 权限或改用自托管代理 |
社区 TIL llm 工具 |
默认 openssl v1.0.2 无 TLS 1.3 |
服务端拒绝 | 编译带 OpenSSL 3.x 的二进制 (github.com) |
家宽老路由器 | NAT 会重写 MSS,导致大包分片 | 握手阶段 ClientHello 被截断 | 升级固件或开启 TCP MSS clamp |
DeepSeek 用户还反馈服务器繁忙率高,会被误诊为 TLS 错误;实际可在返回 HTTP 429 时抓到 Retry-After
头部 (reddit.com)。
自 2024 年起,多家 AI 服务(OpenAI、Anthropic、DeepSeek)统一启用 TLS 1.3,仅容忍极少数 TLS 1.2 套件,意在提升 0-RTT 握手与性能,同时抑制被动嗅探 (vox.com, milvus.io)。这与 AI 行业愈发重视敏感数据传输安全的趋势呼应,亦会倒逼旧平台升级 Java 7、OpenSSL 1.0.2 等过时依赖 (community.openai.com, community.openai.com)。
✅ 使用支持 TLS 1.3 的运行时(OpenSSL 3.x、Rustls 0.23、libcurl 8.x)
✅ 系统时间与时区准确,防止证书链验证失败
✅ SNI 与 Host
头准确写成 api.deepseek.com
✅ 无透明代理或 SSL 检测网关劫持流量
✅ 若位于企业内网,确保防火墙允许 TCP 443
全量出口
HandshakeFailure
并非 DeepSeek 独有,而是加密层通用的自卫机制。通过升级 TLS 库、检查 API 基础 URL、消除代理及证书链问题,即可在 90% 情况下解决 Chatwise 调用 DeepSeek 的握手错误。当握手通过但仍报错时,再去关注请求体、模型配额或并发限流,这样的排障才更高效、更系统化。
本文引用与资料来源:
DeepSeek API 官方文档 (postman.com, api-docs.deepseek.com)
Chatwise Issue 追踪 (github.com)
Rustls、Hyper 与 Reqwest 的 GitHub 讨论 (github.com, github.com, github.com)
TLS 握手原理与常见错误指南 (sectigo.com, stackoverflow.com, thesslstore.com)
Cloudflare 与网络拦截案例 (community.cloudflare.com)
Replit 与 DeepSeek 外网限制 (reddit.com)
LLM 工具对 DeepSeek 的兼容性笔记 (github.com, meghashyamthiruveedula.medium.com)
DeepSeek 行业影响报道 (vox.com)