Error message
received fatal alert: HandshakeFailure
reqwest prints it at the end of its error chain, ureq as "io: received fatal alert: HandshakeFailure". Against a TLS 1.0-only server (tls-v1-0.badssl.com) our rustls client received this same alert. Other alerts appear with their own rustls names after "received fatal alert: ".
rustls (via reqwest, ureq, tonic) — rustls::Error::AlertReceived(HandshakeFailure)
What it means
The server answered rustls's ClientHello with TLS alert 40 (handshake_failure). rustls deliberately implements only modern TLS: TLS 1.2 and 1.3 with ECDHE key exchange and AEAD ciphers. Servers that require RSA key exchange, CBC suites, TLS 1.0/1.1 or a client certificate refuse it this way.
Why it happens
No cipher suite rustls implements
commonLegacy servers limited to RSA key exchange, CBC-SHA or 3DES suites have nothing in common with rustls.
The server speaks only TLS 1.0 or 1.1
commonrustls does not implement TLS versions below 1.2.
The server requires a client certificate
occasionalMutual-TLS endpoints answer a client without a certificate with handshake_failure on TLS 1.2.
How to fix it
- 1.
Check what the server offers
If it offers no ECDHE + AEAD suite, the server needs to be modernised.
bash # What does the server accept? rustls offers TLS 1.2/1.3 with ECDHE + AES-GCM or ChaCha20 only nmap --script ssl-enum-ciphers -p 443 legacy.example.com - 2.
Last resort for an unmaintainable legacy server: native-tls
Building a dedicated client with reqwest's native-tls feature uses the platform TLS library (OpenSSL on Linux, which needs its development headers to build), and that library may still allow older suites, subject to the system policy. Keep it scoped to the legacy endpoint; the server's weak configuration remains a risk.
toml # A legacy server you cannot upgrade: build a separate client on the OS TLS library # (OpenSSL on Linux), which still implements older suites. Cargo.toml: reqwest = { version = "=0.13.5", default-features = false, features = ["native-tls", "blocking"] } - 3.
Mutual TLS: present a client certificate
reqwest::Identity::from_pem with a PEM containing the certificate and private key, set on the builder.
rust // Mutual TLS with rustls: present a client certificate (PEM with certificate + private key) let identity = reqwest::Identity::from_pem(&std::fs::read("client-cert-and-key.pem")?)?; let client = reqwest::Client::builder().identity(identity).build()?;