// Engineering Log
HTTP, HTTPS and TLS: Part 4 — What is HTTPS and how the TLS handshake works
Published on 2026-10-02
// Fast route
This article belongs to the topic Security and protection.
HTTPS — this is HTTP transmitted inside a TLS (Transport Layer Security) connection. TLS solves three problems: it encrypts data so they cannot be read in transit; it verifies integrity so they cannot be altered; and it confirms that you are talking to the server example.ru, not to someone in the middle. The third problem is solved by a certificate, and without it the first two are meaningless: an encrypted channel to an attacker offers no protection.
SSL — the old name of the protocol. The last version SSL 3.0 was prohibited in 2015 (RFC 7568), but the word stuck: “SSL certificate”, ssl_certificate in Nginx, the OpenSSL library. Today SSL is always understood to mean TLS.
What is encrypted and what is visible
Inside TLS the entire HTTP is transmitted: method, path, query string, headers, cookies, body. A network observer — ISP, Wi-Fi owner, DPI equipment — can see:
- IP addresses and ports of the client and server;
- the site name in the SNI field of the initial handshake (if ECH is not used);
- the DNS query for that name, if DNS is not encrypted;
- the volume and timing of the data transfer;
- handshake parameters that can identify the client type (more — “What are JA3 and JA4”).
The path /account/orders?id=42 and the page contents are not visible. Therefore network blocks work by IP address, by SNI, or by DNS, but not by individual pages.
Certificate
An X.509 certificate is the server’s public key together with data about whom it belongs to, signed by a Certificate Authority (CA). Main fields:
- Subject Alternative Name (SAN) — the list of names the certificate is valid for:
example.ru,www.example.ru,*.example.ru. Browsers check only SAN; the Common Name field is not used for name verification since 2017; - validity period —
notBeforeandnotAfter; - issuer — who signed it;
- public key — RSA (commonly 2048 bits) or ECDSA (P-256).
A wildcard in SAN covers one level: *.example.ru works for shop.example.ru, but not for example.ru and not for a.shop.example.ru.
Life spans are shortening. The industry consortium CA/Browser Forum adopted a phased reduction of the maximum lifetime of public certificates in 2025: 200 days from 15 March 2026, 100 days from March 2027 and 47 days from March 2029. Let’s Encrypt certificates live 90 days, and the project is moving to even shorter lifetimes. The result is one: issuance and renewal must be automated (ACME client certbot, acme.sh, built-in ACME in Caddy and Traefik).
Chain of trust
A site certificate is signed not by the root CA certificate but by an intermediate. The chain looks like this:
example.ru ← site certificate (leaf), signed by intermediate
└ R11 (Let's Encrypt) ← intermediate, signed by root
└ ISRG Root X1 ← root, included in the OS or browser trust storeThe server must send the site certificate and all intermediates. It does not need to send the root — the client already has it. The most common configuration error is the server sending only the site certificate without the intermediate. Desktop browsers often forgive this (Chrome and Firefox can find or cache intermediates), but curl, Python, Java and mobile apps do not, and they report a verification error. That’s why certbot recommends fullchain.pem in Nginx, not cert.pem.
The client checks:
- each signature in the chain is valid and the chain ends in a root from the trusted store;
- certificates are not expired;
- the site name is present in SAN;
- the certificate is not revoked (revocation mechanisms differ between browsers; Let’s Encrypt stopped OCSP support in 2025 and publishes only CRL lists).
TLS 1.3 handshake
TLS 1.3 (RFC 8446, 2018) establishes a secure connection in one round trip (1-RTT) on top of an already open TCP connection.
- ClientHello (client → server, plaintext):
- supported versions (extension
supported_versions); - list of cipher suites:
TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256; - supported groups for key exchange:
X25519MLKEM768,x25519,secp256r1; - key_share — a ready public key part for one or two groups. The client guesses which the server will pick, saving a round trip;
- SNI — the site name so the server can pick the correct certificate;
- ALPN — which protocols the client is willing to use over TLS:
h2,http/1.1; - a random value and other extensions.
ServerHello (server → client): the chosen cipher suite and its part of the key. From this moment both sides have a shared secret (Diffie–Hellman key exchange), and everything further is encrypted.
Encrypted part from the server:
- EncryptedExtensions — chosen ALPN and other parameters;
- Certificate — the certificate chain;
- CertificateVerify — a signature over the handshake by the certificate’s private key. This proves the server owns the key and did not just forward someone else’s certificate;
- Finished — a checksum of the handshake.
- Finished from the client — and immediately the first HTTP request in the same packet.
In TLS 1.3 even the server certificate is transmitted encrypted, and a passive observer does not see which certificate the server returned. ClientHello and ServerHello remain in plaintext.
If the client guessed the wrong group in key_share, the server responds with HelloRetryRequest, and the handshake takes two round trips.
Session resumption and 0-RTT. After the handshake the server gives the client a ticket (session ticket). On the next connection the client presents it and skips certificate verification, and in 0-RTT mode sends the first request together with ClientHello. 0-RTT data can be captured and replayed, so they are used only for safe requests like GET; in Nginx 0-RTT is disabled by default (ssl_early_data off).
TLS 1.2 handshake
TLS 1.2 (RFC 5246, 2008) is still widely supported and requires two round trips:
- ClientHello — versions and cipher suites;
- ServerHello, Certificate (plaintext), ServerKeyExchange, ServerHelloDone;
- ClientKeyExchange, ChangeCipherSpec, Finished from the client;
- ChangeCipherSpec and Finished from the server — only now can HTTP be sent.
Differences from 1.3 that matter in practice:
- one round slower: with a 100 ms latency to the server that adds 100 ms to each new connection;
- the server certificate is visible in the network;
- allows legacy algorithms: RSA key exchange without forward secrecy, CBC ciphers, SHA-1. These should be disabled in server configuration (more — “TLS versions, cipher suites, SNI, ALPN, ECH and mTLS”);
- with RSA key exchange, anyone who later obtains the server’s private key can decrypt all previously recorded sessions. In TLS 1.3 that exchange is removed: session keys are ephemeral, and leakage of the certificate private key does not reveal past traffic. This property is called forward secrecy.
Where TLS ends
TLS protects the segment between the client and whoever holds the certificate. If a CDN or reverse proxy sits in front of the site, the browser’s TLS ends there (TLS termination), and onwards to your server the request goes over a separate connection. That second segment must also be encrypted if it traverses someone else’s network: Cloudflare, for example, has a Flexible mode that sends requests to the origin over plain HTTP, so data between the CDN and the server is unencrypted. Use Full (strict) mode with certificate verification at the origin.
Inspect the handshake yourself
Handshake steps. The -state flag of openssl s_client prints each message the client sent or received. Output for TLS 1.3 (site behind CloudFront, September 2026):
echo | openssl s_client -connect example.ru:443 -servername example.ru -state 2>&1 | grep -E '^SSL_connect|^Negotiated|^New'SSL_connect:SSLv3/TLS write client hello
SSL_connect:SSLv3/TLS read server hello
SSL_connect:TLSv1.3 read encrypted extensions
SSL_connect:SSLv3/TLS read server certificate
SSL_connect:TLSv1.3 read server certificate verify
SSL_connect:SSLv3/TLS read finished
SSL_connect:SSLv3/TLS write change cipher spec
SSL_connect:SSLv3/TLS write finished
Negotiated TLS1.3 group: X25519MLKEM768
New, TLSv1.3, Cipher is TLS_AES_128_GCM_SHA256Everything below read server hello the client received already encrypted. change cipher spec in TLS 1.3 is an empty message for compatibility with old network equipment. The group X25519MLKEM768 is a hybrid post-quantum key exchange (more — in the article “TLS versions, cipher suites, SNI, ALPN, ECH and mTLS”).
The same server with -tls1_2:
SSL_connect:SSLv3/TLS write client hello
SSL_connect:SSLv3/TLS read server hello
SSL_connect:SSLv3/TLS read server certificate
SSL_connect:SSLv3/TLS read server key exchange
SSL_connect:SSLv3/TLS read server done
SSL_connect:SSLv3/TLS write client key exchange
SSL_connect:SSLv3/TLS write change cipher spec
SSL_connect:SSLv3/TLS write finished
SSL_connect:SSLv3/TLS read server session ticket
SSL_connect:SSLv3/TLS read change cipher spec
SSL_connect:SSLv3/TLS read finishedHere you can see both rounds: the certificate and server key are received in plaintext, the client responds with its key and waits for finished from the server. For a full message breakdown use -msg, and in OpenSSL builds with tracing support — -trace.
Cost of the extra round. Measurement with curl against the same site, five requests per version, averages:
curl --tlsv1.2 --tls-max 1.2 -s -o /dev/null -w '%{time_connect} %{time_appconnect}\n' https://example.ru/
curl --tlsv1.3 -s -o /dev/null -w '%{time_connect} %{time_appconnect}\n' https://example.ru/| Version | TCP (≈ 1 RTT) | TLS handshake |
|---|---|---|
| TLS 1.2 | 15 ms | 37 ms |
| TLS 1.3 | 17 ms | 23 ms |
The TLS 1.2 handshake took about two RTTs, TLS 1.3 a little more than one (plus computation time). On a server in the other hemisphere where RTT is 150–200 ms, the difference between versions is already 150–200 ms for each new connection.
In Wireshark. The filter tls.handshake will leave only handshake messages. In TLS 1.2 you can expand the Certificate packet to view the server certificate; in TLS 1.3 after ServerHello you only see Application Data records — the certificate is encrypted.
In the browser. Click the icon to the left of the address → “Secure connection” → “Certificate is valid” to see the chain, and in Chrome DevTools on the Security tab — the TLS version, key exchange group and cipher.
Self-signed and corporate certificates
A self-signed certificate encrypts just as reliably, but the client has no way to verify it is genuine, and the browser shows a warning. For internal services it’s better to set up your own internal CA (for example, step-ca) and install its root certificate on workstations instead of using self-signed certificates.
Corporate protection systems and antiviruses often decrypt HTTPS: they install their own root certificate on the computer and mint fake certificates on the fly for every site. In the browser this is visible by the certificate issuer — instead of Let’s Encrypt or another public CA you see the antivirus or company name. Applications with embedded trusted certificate lists (pinning) stop working in such a network.
Common mistakes
- In Nginx
cert.pemis specified instead offullchain.pem— the browser works, but APIs from Python and mobile apps fail with a certificate verification error. - The certificate is issued for
example.ru, but users visitwww.example.ru— name mismatch error. - Renewal is configured, but the web server does not reload the certificate afterward — the site serves the old, already expired certificate.
- Encryption only up to the CDN, but from the CDN to the server — plain HTTP.
- Monitoring scripts use
curl -kto check “does it work” — certificate verification is disabled, and expiration goes unnoticed.
// Similar task
If you are dealing with something similar
This article belongs to one of the main working topics. You can keep reading on the topic, go to the homepage to understand what I do, or open the service pages directly.
Article topic
Security and protection
SSL, hardening, access control, service protection, and secure configurations.
Typical tasks behind this topic
- Set up SSL, certificates, and secure connections
- Restrict access and close unnecessary entry points
- Harden server and service configuration
// Next step
If you need help with this topic, not just another article, it is better to go straight to the service page. The homepage and topic collection stay available as secondary routes.
Open services// Contact
Need help?
Get in touch with me and I'll help solve the problem
I reply within one business day (03:00-13:00 GMT)
Или оставьте заявку здесь:
// Related