Networking Tutorial · Core Protocols · Lesson 14 of 28

HTTPS and TLS

How TLS encrypts connections and proves a server's identity with certificates, and the errors you'll meet.

Create a free account to save this page, take notes and track your progress. Sign in

What TLS does

TLS (Transport Layer Security) sits between TCP and HTTP. HTTPS is simply HTTP over TLS. It provides:

  • Encryption, so nobody on the path can read the traffic.
  • Integrity, so nobody can change it unnoticed.
  • Authentication: the server proves it really is shop.example.com with a certificate.

The TLS 1.3 handshake

The client says which versions and ciphers it supports and sends a key share. The server answers with its key share, its certificate and a signature proving it holds the certificate's private key. Both sides derive the same session keys. TLS 1.3 needs one round trip (TLS 1.2 needed two).

Certificates and the chain of trust

A certificate binds a name (shop.example.com, or *.example.com) to a public key and is signed by a certificate authority (CA). Browsers trust a set of root CAs; servers send their certificate plus any intermediate certificates, and the client follows the chain up to a trusted root.

Walking a certificate chainTry it yourself
TRUSTED_ROOTS = {"Example Root CA"}
certs = {
    "shop.example.com": {"issuer": "Example Intermediate CA", "names": ["shop.example.com", "www.shop.example.com"]},
    "Example Intermediate CA": {"issuer": "Example Root CA"},
}

def verify(hostname, sent_chain):
    leaf = sent_chain[0]
    if hostname not in certs[leaf]["names"]:
        return "hostname mismatch"
    current = leaf
    while certs[current]["issuer"] not in TRUSTED_ROOTS:
        issuer = certs[current]["issuer"]
        if issuer not in sent_chain:
            return f"unable to get issuer certificate ({issuer} not sent)"
        current = issuer
    return "trusted"

print(verify("shop.example.com", ["shop.example.com", "Example Intermediate CA"]))
print(verify("shop.example.com", ["shop.example.com"]))
print(verify("api.example.com", ["shop.example.com", "Example Intermediate CA"]))

Output

trusted
unable to get issuer certificate (Example Intermediate CA not sent)
hostname mismatch

Common TLS errors

Error Usual cause
Certificate has expired Renewal failed or wasn't automated
Hostname mismatch Certificate is for a different name, or the client used an IP address
Unable to get local issuer certificate Server didn't send the intermediate certificate
Self-signed certificate Internal CA not trusted by the client

Automate renewal (Let's Encrypt via ACME, AWS Certificate Manager, Azure Key Vault, Google-managed certificates) and alert well before expiry.

$ openssl s_client -connect example.com:443 -servername example.com </dev/null | openssl x509 -noout -dates -subject

Test yourself

Browsers accept your certificate, but a Python client fails with 'unable to get local issuer certificate'. What's the likely fix?

  1. Configure the server to send the intermediate certificate (the full chain)
  2. Switch the server to HTTP
  3. Disable certificate verification in the client
  4. Use a longer RSA key
Show answer

Answer: Configure the server to send the intermediate certificate (the full chain)

Browsers often cache or fetch missing intermediates; many other clients don't. Serve the full chain; never disable verification.

Was this page helpful?