BAD SKY.NET

post_handshake_auth

post_handshake_auth lets a TLS 1.3 server ask for a client certificate later. OpenSSL clients such as Python requests send it.

TLS extensionCode 0x0031 (49)

What it is

post_handshake_auth (extension 49) is defined in RFC 8446, section 4.2.6. By sending it, a TLS 1.3 client says it is willing to present a client certificate after the handshake has finished, if the server asks for one.

This replaces a pattern from TLS 1.2, where a server renegotiated the connection to request a client certificate only when the user opened a protected part of the site. TLS 1.3 has no renegotiation, so post-handshake authentication covers that case instead.

RFC 8740 forbids post-handshake authentication over HTTP/2, and Chrome does not send the extension. It mostly appears in libraries that enable it in case a client certificate is configured.

The extension is empty: its presence alone tells the server that a later CertificateRequest is acceptable. Without it, a TLS 1.3 server is not allowed to ask for a client certificate after the handshake.

Who sends it

Chrome 153 (macOS) browser Not sent
curl_cffi (impersonate chrome) Not sent
curl-impersonate chrome116 Not sent
curl-impersonate ff117 Not sent
curl 8.7 (macOS) Not sent
curl 7.88 (Debian 12) Sent at position 7
Python requests Sent at position 8
Python httpx Not sent
Python httpx (HTTP/2) Not sent
Go net/http Not sent
Node.js fetch Not sent
PHP Guzzle Sent at position 8

Measured on 2026-10-25 against our own test server: sent by 3 of 12 clients. Position counts from 1 and ignores GREASE values.

Why it matters for fingerprinting

In our measurements only Python requests, PHP Guzzle and Debian curl send it, all clients built on OpenSSL. Chrome, curl_cffi, curl-impersonate, macOS curl, httpx, Go net/http and Node.js fetch do not. Seeing it in a ClientHello that claims to be a browser is a strong hint that a script sent it.

Check whether your client sends it

Open the test from your browser, or call it from your script, and look for post_handshake_auth in the TLS details.

Run the TLS fingerprint test

References

Related