# How SSL actually works

> SSL, now properly called TLS, does two separate jobs: it proves the server genuinely owns the domain you asked for, and it encrypts everything sent after that. The proof comes from a certificate signed by an authority your device already trusts, and the encryption uses a key the two sides negotiate during the handshake and discard when the connection ends.

- Source: https://techdecoded.dev/blog/how-ssl-works
- Author: Himesh Rupareliya (https://www.linkedin.com/in/himeshrupareliya/)
- Published: 2026-08-30
- Updated: 2026-08-31
- Category: Security
- Level: Beginner
- Licence: CC BY 4.0. Quote freely with a link to https://techdecoded.dev/blog/how-ssl-works

## Key takeaways

- SSL is a dead protocol name. Every version of SSL is broken and switched off; what you are actually running is TLS, almost always 1.2 or 1.3.
- Encryption is the solved half of the problem. The hard part is identity, and the entire certificate authority system exists to answer one question: does this server really control this domain name?
- A certificate is a public key plus a list of domain names, signed by someone your device already trusts. It is a statement about a name, not about honesty.
- The padlock proves you are talking to the domain in the address bar, and nothing more. A phishing site can hold a perfectly valid certificate.
- Most TLS failures in production are not cryptography failures. They are a missing intermediate certificate, a renewal nobody automated, or a hostname the certificate does not cover.

---

> **TL;DR**
> 
> SSL, these days properly called TLS, encrypts your connection and proves the server genuinely controls the domain you typed. The encryption is the part everyone pictures. The proof is where certificates, authorities and every confusing error message come from.

Type your password into a page served over plain `http://`, and every device between you and that server can read it. Not decode it, not crack it. Read it, the way you are reading this sentence. The same is true of the session cookie that comes back, which is the thing an attacker actually wants, because it lets them be you without ever knowing your password.

That is the hole `https://` closes. Everything else in this article is about how it closes it, and why the answer needs so many moving parts.

## Why any of this exists

Start with two machines and nothing else, and add one piece at a time.

*\[Figure: Four steps. Each one exists because the previous one leaves a specific hole. Why TLS is shaped the way it is, built up in four stages. Stage one is a plain connection between you and a server, sending text anyone on the path can read: every password, cookie and page. Stage two adds encryption, so the traffic is unreadable in transit, but you now have a private channel to someone whose identity you have not checked, and an attacker sitting in the middle is happy to encrypt with you instead. Stage three adds a certificate, in which the server states which domain it owns and proves it holds the matching private key, but anyone can generate that document about any name, so the claim alone is worth nothing. Stage four adds a certificate authority, a party your device already trusts, whose signature on the certificate is what makes the claim checkable. That is the whole apparatus: encryption solves reading, the certificate makes a claim, and the authority makes the claim verifiable.\]*

Encrypting to an attacker sitting between you and your bank achieves nothing except making the eavesdropping tidier. Identity is also where the day-to-day trouble is: almost every TLS problem you hit in production is a certificate problem, not a cryptography problem.

## A note on the name

SSL stands for Secure Sockets Layer, built by Netscape in the mid-1990s. Every version of it is broken: SSL 2.0 was prohibited by RFC 6176, and SSL 3.0 followed after the POODLE attack. The replacement is TLS, Transport Layer Security, whose 1.0 and 1.1 versions were themselves deprecated by RFC 8996 in 2021. You are almost certainly running **TLS 1.2** or **TLS 1.3**.

The old word survives because it is welded into config keys (`ssl_certificate` in nginx), library names (OpenSSL) and a decade of marketing. When someone says “SSL cert” they mean a TLS certificate, and you do not need to correct them. This article uses the two words for the same thing, and names a version where it matters.

> **In plain English**
> 
> A sealed envelope nobody can read through is the encryption. Knowing that the person handing it to you is who they say, and not someone in a convincing uniform, is the identity problem. A certificate is their ID card, and a certificate authority is the office whose stamp on that card you already decided to trust.

## The handshake, step by step

Before any HTTP request is sent, the two sides run a **handshake** (The opening exchange of a TLS connection, where both sides agree which version and algorithms to use, establish a shared key, and the server proves its identity.). This is what happens on a modern TLS 1.3 connection.

*\[Figure: One round trip. The server proves who it is, and the traffic is already encrypted by the time the certificate arrives. A TLS 1.3 handshake between a browser and a server, in six messages. First the browser sends a ClientHello carrying the TLS versions it supports, the hostname it wants (in cleartext), and a public key share. The server replies with a ServerHello naming the chosen version and cipher plus its own key share, at which point both sides can independently compute the same shared secret and everything that follows is encrypted. The server then sends its certificate chain together with a signature over the whole conversation so far, proving it holds the matching private key. The browser verifies that signature, walks the chain to a root it already trusts, and checks the dates and the hostname. Both sides send a Finished message covering the full transcript, which makes tampering with any earlier step detectable. Only then does the real HTTP request go out. The whole exchange costs one round trip before application data can flow.\]*

TLS 1.2 needed two round trips here, which is a large part of why 1.3 exists. TLS 1.3 also cut the cipher suite list to five (RFC 8446, appendix B.4) and removed every mode that had turned out to be a foot-gun, including static RSA key exchange and renegotiation.

The key that encrypts your traffic is generated fresh for this connection and thrown away afterwards, so recording the traffic today and stealing the server’s private key next year does not decrypt it. That property is called **forward secrecy** (Past sessions stay unreadable even if the server’s long-term private key is later compromised, because each session used its own ephemeral key.). The certificate’s key only ever signs. It never touches your data.

**Check yourself:** An attacker steals a server's private key today. What can they do with traffic they recorded from that server six months ago?

-   Decrypt all of it, since that key protected the connections
    -   This was true for old TLS 1.2 configurations using static RSA key exchange, which is exactly why they were removed. In TLS 1.3 the certificate's key only signs; it never encrypts your traffic.
-   **(correct answer)** Nothing, because each session used its own ephemeral key
    -   Right. That is forward secrecy. The session keys came from a fresh key exchange and were discarded, so the long-term private key does not unlock old recordings.
-   Decrypt it only if the certificate has not expired yet
    -   Expiry is about whether clients will accept the certificate going forward. It has no bearing on decryption, and the session keys were never derived from the certificate's key anyway.

## What is actually inside a certificate

A certificate is a small, boring, structured file. Click through the parts that matter, taken from the certificate Wikipedia was actually serving when this article was written:

*\[Figure: Five fields carry the whole argument. Nothing in here says whether the operator is honest. The fields inside a TLS certificate. Subject is the primary name, here CN = \*.wikipedia.org, a legacy field browsers no longer read on its own. Subject Alternative Name is the authoritative list of hostnames the certificate covers, and a name missing from this list will fail validation no matter how DNS is configured. Public key is the server's public key, used to verify the handshake signature, here a 256-bit elliptic curve key. Validity holds the not-before and not-after dates, here 5 August 2026 to 3 November 2026, a 90-day window, and expiry is the single most common cause of TLS outages. Issuer names who signed the certificate and is the next link up the chain, here Let's Encrypt intermediate YE2. Signature is the issuer's signature over everything above, which makes tampering detectable. Nothing in a certificate says anything about whether the operator is honest or competent.\]*

Nothing in there says whether the operator is honest, solvent or competent. A public certificate is issued once the applicant proves they control the domain, usually by serving a specific file or publishing a specific DNS record. That is the whole test.

> **The Common Name field no longer counts**
> 
> Certificates used to put the domain in a field called Common Name. Browsers stopped reading it years ago and now look only at the Subject Alternative Name extension. If you generate a certificate with a CN and no SAN, every modern client will reject it, and the error message will unhelpfully mention the name it wanted.

## The chain of trust, and why your laptop believes anyone

Your device ships with a **root store** (The set of certificate authority certificates your operating system or browser trusts by default. It is a list of a few hundred organisations, curated by Apple, Microsoft, Mozilla and Google.). That list is the foundation of the whole system, and you did not choose it.

Roots do not sign your certificate directly. Their private keys live offline in hardware, brought out rarely. Instead a root signs an intermediate, the intermediate signs your certificate, and your server presents the resulting chain. Press the button and watch the client walk it:

*\[Figure: The client climbs until it lands on something it already trusts. Run out of links first and the chain fails. A certificate chain of four links, walked from the bottom upwards. At the bottom is the leaf certificate for \*.wikipedia.org, which is what the site operator received from the certificate authority. Above it is the Let's Encrypt intermediate YE2, which signed the leaf and lives on the authority's online systems. Above that is ISRG Root YE, a further intermediate. At the top is ISRG Root X2, the root certificate, which is already present in the device's trust store. The client checks each signature in turn, climbing until it reaches a certificate it already trusts. If it reaches one, the chain is verified. If it runs out of links before reaching a trusted root, verification fails. The server must send the leaf and every intermediate, but must not send the root, because the client already has it.\]*

The client walks upwards, checking each signature, until it lands on something in its root store. If it lands there, the chain is trusted. If it runs out of links first, it is not.

**Your server must send the intermediates. It must not send the root.** The root is already on the client, and sending it wastes bytes on every handshake. This one sentence is the source of an enormous share of TLS support tickets.

> **It works in Chrome, so the chain must be fine**
> 
> Browsers hide a missing intermediate. They cache intermediates from previous sites and will often fetch a missing one from the URL in the certificate. curl, Java, Python and Go usually do neither, so they fail on a server that looks perfectly healthy in your browser. Always test with a command-line client, and not only in a tab.
> 
> This is what a colleague means by “the cert is fine, it is the chain that is broken”. The certificate itself is valid. The server is not sending enough of the chain for anything without a browser’s shortcuts to reach a root.

**Check yourself:** Your API works in the browser but Java clients report 'unable to find valid certification path'. What is the most likely cause?

-   The certificate has expired
    -   An expired certificate fails everywhere, loudly, including in the browser. A failure that only affects non-browser clients points somewhere else.
-   **(correct answer)** The server is sending the leaf certificate without its intermediates
    -   Exactly. The browser filled the gap from its own cache or by fetching the intermediate. Java did not, so it could not reach a trusted root. Serve the full chain file instead of the leaf alone.
-   Java does not trust the certificate authority
    -   Possible but far less common, and it would show up on the very first request from a fresh install rather than as a puzzling browser-versus-client split. Check what your server is sending first.

## The traps

**Wildcards cover one level, the way a key opens one floor rather than the building.** A certificate for `*.example.com` matches `api.example.com` and `www.example.com`. It does not match `a.b.example.com`, and it does not match `example.com` itself unless that name is listed separately. Most issuers add the bare domain for you, which is why this only bites when you generate certificates yourself.

**Expiry is an outage, not a warning.** An expired certificate is a passport at a border, not a library card: there is no talking your way past the date. At the not-after timestamp every client starts refusing the connection, with no grace period. The window is shrinking fast:

*\[Figure: Under ballot SC-081v3, the window shrinks by roughly eight times in three years. Renewal has to be something a machine does. The maximum lifetime of a publicly trusted TLS certificate, shrinking over time under CA/Browser Forum ballot SC-081v3. Before 15 March 2026 the cap was 398 days, a little over a year, which made an annual manual renewal survivable. From 15 March 2026 the cap is 200 days, which is the rule in force today. From 15 March 2027 it drops to 100 days. From 15 March 2029 it drops to 47 days, roughly eight times shorter than the original 398, at which point a certificate has to be renewed around eight times a year. The practical consequence is that manual renewal has stopped being viable and issuance has to be automated.\]*

Manual renewal has stopped being a viable strategy.

> **If your renewal is a calendar reminder, it will fail**
> 
> Automate issuance with ACME (Let’s Encrypt, ZeroSSL, or your cloud provider’s managed certificates) and alert on days-remaining rather than on the renewal job succeeding. A renewal job that silently stopped running six weeks ago looks identical to one that had nothing to do, right up until the morning it does not.

**The hostname you connect to is what gets checked, not the IP.** Modern servers host many sites on one address, so the client announces which one it wants via **SNI** (Server Name Indication, a field in the first handshake message naming the hostname the client wants. It lets one IP address serve certificates for many different sites.). Connect by IP address and you will get whichever certificate is the default, and validation will fail. That is why `openssl s_client` has a separate `-servername` flag.

**TLS usually stops at your edge.** In most deployments the load balancer or CDN terminates TLS and opens a second connection to your application. Whether that second hop is encrypted is a decision somebody made, possibly by accident. The padlock in the user’s browser says nothing about it.

**Mixed content quietly downgrades your page.** An HTTPS page that pulls a script over plain HTTP is as compromised as if the whole page were HTTP, because an attacker who can rewrite that script owns the page. Browsers block this outright for scripts and stylesheets now, so the usual symptom is a feature that silently does not work.

**Check yourself:** A phishing site at paypa1-secure.com shows a padlock and a valid certificate. What went wrong?

-   The certificate authority was compromised or negligent
    -   Nothing was compromised. The certificate correctly attests that whoever runs paypa1-secure.com controls that domain, which happens to be true. The system worked as designed.
-   **(correct answer)** Nothing. The padlock only ever attested to the domain name
    -   Correct, and this is the most misunderstood point about HTTPS. A certificate proves control of a name. Judging whether that name is the one you meant to visit is still your job.
-   The browser failed to check the certificate against a phishing list
    -   Browsers do run separate safe-browsing checks, but those are an entirely different mechanism layered on top. TLS itself has no concept of a site being trustworthy.

## What TLS will not do for you

**It does not make your application secure.** TLS protects data while it is moving between two points. SQL injection, a leaky API, a public S3 bucket and a weak password policy are all completely untouched by it.

**It does not hide who you are talking to.** Your DNS lookup, the destination IP, and the SNI hostname in the ClientHello are all visible to anyone on the path. What is hidden is the URL path, the headers, the cookies and the body. Encrypted Client Hello (RFC 9849, published March 2026) fixes the SNI leak and is supported by several large CDNs, but it needs matching DNS records and client support, so cleartext SNI is still the norm.

**It does not verify the client.** By default only the server proves anything. Mutual TLS, where the client presents a certificate too, exists and works well, but it is a deliberate extra step and it brings its own certificate lifecycle to manage.

**It is not free.** You pay a round trip on every new connection, some CPU on every handshake, and a permanent operational cost in renewals and expiry monitoring. Worth paying almost every time, but pretending it is free is how the renewal job ends up nobody’s job.

> **Note**
> 
> Revocation, the mechanism for cancelling a certificate before it expires, has never worked well in practice. Let’s Encrypt shut down its OCSP responders in August 2025 over the privacy cost of answering roughly twelve billion lookups a day, moving to certificate revocation lists. The industry’s real answer is the shrinking lifetimes above: a certificate valid for six days barely needs revoking.

## Checking it yourself

One command shows you the whole picture:

```bash
echo | openssl s_client -connect wikipedia.org:443 -servername wikipedia.org
```

The `-servername` flag sets SNI, and without it you may get the wrong certificate entirely. The parts that matter:

```
Certificate chain
 0 s:CN = *.wikipedia.org
   i:C = US, O = Let's Encrypt, CN = YE2
   v:NotBefore: Aug  5 19:15:41 2026 GMT; NotAfter: Nov  3 19:15:40 2026 GMT
 1 s:C = US, O = Let's Encrypt, CN = YE2
   i:C = US, O = ISRG, CN = Root YE
...
Verification: OK
New, TLSv1.3, Cipher is TLS_AES_128_GCM_SHA256
Verify return code: 0 (ok)
```

Read it upwards. `s:` is the subject, `i:` is the issuer, and each entry’s issuer is the next entry’s subject. That is the chain. `Verification: OK` means it reached a trusted root; anything else names the specific link that failed. The `NotAfter` date on entry 0 is your renewal deadline, and here it is 90 days after issuance, which is the Let’s Encrypt default.

### Try it yourself: Find a certificate that covers more names than you expect

Pull the list of hostnames out of a live certificate:

```bash
echo | openssl s_client -connect wikipedia.org:443 -servername wikipedia.org \
  2>/dev/null \
  | openssl x509 -noout -text \
  | grep -A3 "Subject Alternative Name"
```

Then try one that will fail, and read the error rather than the page:

```bash
curl -sSI https://expired.badssl.com/
```

**What you should see**

The first command prints a long comma-separated list of DNS names, including entries for every Wikimedia project and their mobile subdomains. One certificate, one key, many names. That list is the definitive answer to “which hostnames will this certificate work for”, and a name missing from it is a name that will fail no matter how the DNS is configured.

The second command fails before a single byte of HTTP moves, with a message like `certificate has expired`. Note that `curl` never sends the request. The handshake is what failed, which is why an expired certificate takes a site down completely rather than degrading it.

## Where to go next

Three directions, depending on what you are trying to do.

If you run servers: learn ACME properly, so certificate renewal becomes something you configure once. Then read up on HSTS, the header that tells browsers never to try plain HTTP for your domain again.

If you are debugging: get comfortable with `openssl s_client`, then look at Certificate Transparency logs (searchable at `crt.sh`), which record every public certificate ever issued for a domain. They are how you find out that somebody issued a certificate for your domain without telling you.

If you want the mechanism underneath: read about Diffie-Hellman key exchange, which is the trick that lets two strangers agree on a secret over a wire everyone can read. It is the one piece of mathematics in this article that genuinely feels like magic, and it is more approachable than its reputation suggests.

## Quick answers

**Is SSL the same thing as TLS?**

They are the same idea under two names. SSL was the original protocol from Netscape, and every version of it (2.0 and 3.0) is now broken and disabled everywhere. TLS is the successor, and the versions in real use today are TLS 1.2 and TLS 1.3. The word SSL survives in product names, config file keys and library names, so you will keep typing it for years, but the bytes on the wire are TLS.

**What does the padlock icon actually prove?**

It proves two things: the connection is encrypted, and the server presented a certificate for the exact domain shown in the address bar, signed by an authority your device trusts. It proves nothing about whether the site is honest, safe or well run. Phishing sites routinely have valid certificates, because getting one only requires proving you control the domain.

**Why does my site work in the browser but fail with curl or Java?**

Almost always a missing intermediate certificate. Your server is meant to send the leaf certificate plus every intermediate above it, stopping short of the root. Browsers paper over a missing intermediate by caching one from an earlier visit or fetching it on the fly, while curl, Java, Python and Go generally do not. Fix it by serving the full chain file rather than the leaf alone.

**Does HTTPS hide which website I am visiting?**

Not by itself. Your DNS lookup and the destination IP address are visible, and the hostname is sent in cleartext inside the first handshake message, in a field called SNI. HTTPS hides the URL path, the headers, the cookies and the body. Encrypted Client Hello (RFC 9849, published March 2026) closes the SNI gap, but it needs support at both ends and is not widely deployed yet.

**How long do SSL certificates last now?**

Public certificates are getting steadily shorter. The CA/Browser Forum ballot SC-081v3 caps them at 200 days from 15 March 2026, 100 days from 15 March 2027, and 47 days from 15 March 2029. Let's Encrypt still issues 90 days by default and offers an opt-in 6-day profile. The practical consequence is that manual renewal has stopped being viable.

